From fba383752309ad5e6cfbc696c61473f594922e81 Mon Sep 17 00:00:00 2001 From: ds Date: Tue, 13 Oct 2020 15:43:25 +0300 Subject: [PATCH] add: rapid output console commands are now put on queue and sent with intervals. fixes #176, and overflows causes by `yb cvars` and `yb g clean all` commands. --- ext/crlib/cr-deque.h | 9 ++++----- inc/control.h | 48 ++++++++++++++++++++++++++++++++++++++------ src/control.cpp | 21 +++++++++++++++++++ src/engine.cpp | 11 +++++----- src/linkage.cpp | 3 +++ 5 files changed, 76 insertions(+), 16 deletions(-) diff --git a/ext/crlib/cr-deque.h b/ext/crlib/cr-deque.h index 6052e25..ec6f1e5 100644 --- a/ext/crlib/cr-deque.h +++ b/ext/crlib/cr-deque.h @@ -132,17 +132,16 @@ public: return index_.first == index_.second; } - template void emplaceLast (U &&object) { + template void emplaceLast (Args &&...args) { auto rear = pickRearIndex (); - alloc.construct (&contents_[index_.second], cr::forward (object)); + alloc.construct (&contents_[index_.second], cr::forward (args)...); index_.second = rear; } - template void emplaceFront (U &&object) { + template void emplaceFront (Args &&...args) { index_.first = pickFrontIndex (); - - alloc.construct (&contents_[index_.first], cr::forward (object)); + alloc.construct (&contents_[index_.first], cr::forward (args)...); } void discardFront () { diff --git a/inc/control.h b/inc/control.h index bd0bcca..74d339a 100644 --- a/inc/control.h +++ b/inc/control.h @@ -22,6 +22,12 @@ CR_DECLARE_SCOPED_ENUM (BotCommandResult, BadFormat // wrong params ) +// print queue destination +CR_DECLARE_SCOPED_ENUM (PrintQueueDestination, + ServerConsole, // use server console + ClientConsole // use client console +); + // bot command manager class BotControl final : public Singleton { public: @@ -36,7 +42,9 @@ public: public: BotCmd () = default; - BotCmd (StringRef name, StringRef format, StringRef help, Handler handler) : name (name), format (format), help (help), handler (cr::move (handler)) { } + + BotCmd (StringRef name, StringRef format, StringRef help, Handler handler) : name (name), format (format), help (help), handler (cr::move (handler)) + { } }; // single bot menu @@ -46,13 +54,27 @@ public: MenuHandler handler; public: - BotMenu (int ident, int slots, StringRef text, MenuHandler handler) : ident (ident), slots (slots), text (text), handler (cr::move (handler)) { } + BotMenu (int ident, int slots, StringRef text, MenuHandler handler) : ident (ident), slots (slots), text (text), handler (cr::move (handler)) + { } + }; + + // queued text message to prevent overflow with rapid output + struct PrintQueue { + int32 destination; + String text; + + public: + PrintQueue () = default; + + PrintQueue (int32 destination, StringRef text) : destination (destination), text (text) + { } }; private: StringArray m_args; Array m_cmds; Array m_menus; + Deque m_printQueue; IntArray m_campIterator; edict_t *m_ent; @@ -65,6 +87,8 @@ private: int m_menuServerFillTeam; int m_interMenuData[4] = { 0, }; + float m_printQueueFlushTimestamp {}; + public: BotControl (); ~BotControl () = default; @@ -142,6 +166,7 @@ public: void kickBotByMenu (int page); void assignAdminRights (edict_t *ent, char *infobuffer); void maintainAdminRights (); + void flushPrintQueue (); public: void setFromConsole (bool console) { @@ -208,8 +233,14 @@ template inline void BotControl::msg (const char *fmt, Args & auto result = strings.format (conf.translate (fmt), cr::forward (args)...); // if no receiver or many message have to appear, just print to server console - if (game.isNullEntity (m_ent) || m_rapidOutput) { - game.print (result); // print the info + if (game.isNullEntity (m_ent)) { + + if (m_rapidOutput) { + m_printQueue.emplaceLast (PrintQueueDestination::ServerConsole, result); + } + else { + game.print (result); // print the info + } // enable translation aftetwards if (isDedicated) { @@ -218,8 +249,13 @@ template inline void BotControl::msg (const char *fmt, Args & return; } - if (m_isFromConsole || strlen (result) > 56) { - game.clientPrint (m_ent, result); + if (m_isFromConsole || strlen (result) > 56 || m_rapidOutput) { + if (m_rapidOutput) { + m_printQueue.emplaceLast (PrintQueueDestination::ClientConsole, result); + } + else { + game.clientPrint (m_ent, result); + } } else { game.centerPrint (m_ent, result); diff --git a/src/control.cpp b/src/control.cpp index 68892e6..a22cd68 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -207,6 +207,9 @@ int BotControl::cmdCvars () { cfg.open (strings.format ("%s/addons/%s/conf/%s.cfg", game.getRunningModName (), product.folder, product.folder), "wt"); cfg.puts ("// Configuration file for %s\n\n", product.name); } + else { + ctrl.setRapidOutput (true); + } for (const auto &cvar : game.getCvars ()) { if (cvar.info.empty ()) { @@ -258,6 +261,7 @@ int BotControl::cmdCvars () { msg (" "); } } + ctrl.setRapidOutput (false); if (isSave) { msg ("Bots cvars has been written to file."); @@ -1854,6 +1858,22 @@ void BotControl::maintainAdminRights () { } } +void BotControl::flushPrintQueue () { + if (m_printQueueFlushTimestamp > game.time () || m_printQueue.empty ()) { + return; + } + auto printable = m_printQueue.popFront (); + + // send to needed destination + if (printable.destination == PrintQueueDestination::ServerConsole) { + game.print (printable.text.chars ()); + } + else if (!game.isNullEntity (m_ent)) { + game.clientPrint (m_ent, printable.text.chars ()); + } + m_printQueueFlushTimestamp = game.time () + 0.05f; +} + BotControl::BotControl () { m_ent = nullptr; m_djump = nullptr; @@ -1862,6 +1882,7 @@ BotControl::BotControl () { m_isMenuFillCommand = false; m_rapidOutput = false; m_menuServerFillTeam = 5; + m_printQueueFlushTimestamp = 0.0f; m_cmds.emplace ("add/addbot/add_ct/addbot_ct/add_t/addbot_t/addhs/addhs_t/addhs_ct", "add [difficulty[personality[team[model[name]]]]]", "Adding specific bot into the game.", &BotControl::cmdAddBot); m_cmds.emplace ("kick/kickone/kick_ct/kick_t/kickbot_ct/kickbot_t", "kick [team]", "Kicks off the random bot from the game.", &BotControl::cmdKickBot); diff --git a/src/engine.cpp b/src/engine.cpp index 0afa1c8..1130bfe 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -430,6 +430,7 @@ void Game::sendClientMessage (bool console, edict_t *ent, StringRef message) { if (!util.isPlayer (ent) || util.isFakeClient (ent)) { return; } + const String &buffer = message; // used to split messages auto sendTextMsg = [&console, &ent] (StringRef text) { @@ -439,19 +440,19 @@ void Game::sendClientMessage (bool console, edict_t *ent, StringRef message) { }; // do not excess limit - constexpr size_t maxSendLength = 125; + constexpr size_t maxSendLength = 187; // split up the string into chunks if needed (maybe check if it's multibyte?) - if (message.length () > maxSendLength) { - auto chunks = message.split (maxSendLength); - + if (buffer.length () > maxSendLength) { + auto chunks = buffer.split (maxSendLength); + // send in chunks for (size_t i = 0; i < chunks.length (); ++i) { sendTextMsg (chunks[i]); } return; } - sendTextMsg (message); + sendTextMsg (buffer); } void Game::sendServerMessage (StringRef message) { diff --git a/src/linkage.cpp b/src/linkage.cpp index 8f892cf..889bca0 100644 --- a/src/linkage.cpp +++ b/src/linkage.cpp @@ -406,6 +406,9 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int) { // keep bot number up to date bots.maintainQuota (); + + // flush print queue to users + ctrl.flushPrintQueue (); if (game.is (GameFlags::Metamod)) { RETURN_META (MRES_IGNORED);