diff --git a/include/LuaAPI.h b/include/LuaAPI.h index 188cb78..0cd0e1f 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -5,7 +5,7 @@ namespace LuaAPI { int PanicHandler(lua_State* State); - +std::string LuaToString(const sol::object Value, size_t Indent = 1, bool QuoteStrings = false); void Print(sol::variadic_args); namespace MP { extern TLuaEngine* Engine; diff --git a/include/TConsole.h b/include/TConsole.h index 053deaa..ae34001 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -18,7 +18,14 @@ public: Commandline& Internal() { return mCommandline; } private: + void ChangeToLuaConsole(); + void ChangeToRegularConsole(); + Commandline mCommandline; + std::vector mCachedLuaHistory; + std::vector mCachedRegularHistory; TLuaEngine* mLuaEngine { nullptr }; + bool mIsLuaConsole { false }; + bool mFirstTime { true }; const std::string mStateId = "BEAMMP_SERVER_CONSOLE"; }; diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 7b56543..14cd8f6 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -6,7 +6,7 @@ #define SOL_ALL_SAFETIES_ON 1 #include -static std::string LuaToString(const sol::object Value, size_t Indent = 1, bool QuoteStrings = false) { +std::string LuaAPI::LuaToString(const sol::object Value, size_t Indent, bool QuoteStrings) { if (Indent > 80) { return "[[possible recursion, refusing to keep printing]]"; } diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 4c49e96..b45a0a7 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -2,11 +2,23 @@ #include "Common.h" #include "Compat.h" +#include "LuaAPI.h" #include "TLuaEngine.h" #include #include +static inline std::string TrimString(std::string S) { + S.erase(S.begin(), std::find_if(S.begin(), S.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); + S.erase(std::find_if(S.rbegin(), S.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), + S.end()); + return S; +} + std::string GetDate() { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); time_t tt = std::chrono::system_clock::to_time_t(now); @@ -79,6 +91,26 @@ void TConsole::BackupOldLog() { } } +void TConsole::ChangeToLuaConsole() { + if (!mIsLuaConsole) { + mIsLuaConsole = true; + beammp_info("Entered Lua console. To exit, type `exit()`"); + mCachedRegularHistory = mCommandline.history(); + mCommandline.set_history(mCachedLuaHistory); + mCommandline.set_prompt("lua> "); + } +} + +void TConsole::ChangeToRegularConsole() { + if (mIsLuaConsole) { + mIsLuaConsole = false; + beammp_info("Left Lua console."); + mCachedLuaHistory = mCommandline.history(); + mCommandline.set_history(mCachedRegularHistory); + mCommandline.set_prompt("> "); + } +} + TConsole::TConsole() { mCommandline.enable_history(); mCommandline.set_history_limit(20); @@ -89,25 +121,75 @@ TConsole::TConsole() { beammp_error("unable to open file for writing: \"Server.log\""); } mCommandline.on_command = [this](Commandline& c) { - auto cmd = c.get_command(); - mCommandline.write("> " + cmd); - if (cmd == "exit") { - beammp_info("gracefully shutting down"); - Application::GracefullyShutdown(); - } else if (cmd == "clear" || cmd == "cls") { - // TODO: clear screen - } else { + try { + auto cmd = c.get_command(); + cmd = TrimString(cmd); + mCommandline.write(mCommandline.prompt() + cmd); if (!mLuaEngine) { beammp_info("Lua not started yet, please try again in a second"); } else { - auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared(cmd), "", "" }); - while (!Future->Ready) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - if (Future->Error) { - beammp_lua_error(Future->ErrorMessage); + if (mIsLuaConsole) { + if (cmd == "exit()") { + ChangeToRegularConsole(); + } else { + auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared(cmd), "", "" }); + while (!Future->Ready) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); // TODO: Add a timeout + } + if (Future->Error) { + beammp_lua_error(Future->ErrorMessage); + } + } + } else { + if (cmd == "exit") { + beammp_info("gracefully shutting down"); + Application::GracefullyShutdown(); + } else if (cmd == "lua") { + ChangeToLuaConsole(); + } else if (!cmd.empty()) { + auto FutureIsNonNil = + [](const std::shared_ptr& Future) { + auto Type = Future->Result.get_type(); + return Type != sol::type::lua_nil && Type != sol::type::none; + }; + std::vector> NonNilFutures; + { // Futures scope + auto Futures = mLuaEngine->TriggerEvent("onConsoleInput", "", cmd); + TLuaEngine::WaitForAll(Futures); + size_t Count = 0; + for (auto& Future : Futures) { + if (!Future->Error) { + ++Count; + } + } + for (const auto& Future : Futures) { + if (FutureIsNonNil(Future)) { + NonNilFutures.push_back(Future); + } + } + } + if (NonNilFutures.size() == 0) { + Application::Console().WriteRaw("Error: Unknown command: '" + cmd + "'"); + } else { + std::stringstream Reply; + if (NonNilFutures.size() > 1) { + for (size_t i = 0; i < NonNilFutures.size(); ++i) { + Reply << NonNilFutures[i]->StateId << ": \n" + << LuaAPI::LuaToString(NonNilFutures[i]->Result); + if (i < NonNilFutures.size() - 1) { + Reply << "\n"; + } + } + } else { + Reply << LuaAPI::LuaToString(NonNilFutures[0]->Result); + } + Application::Console().WriteRaw(Reply.str()); + } + } } } + } catch (const std::exception& e) { + beammp_error("Console died with: " + std::string(e.what()) + ". This could be a fatal error and could cause the server to terminate."); } }; } diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index cd712c1..b493f75 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -77,6 +77,9 @@ void TLuaEngine::operator()() { // event loop auto Before = std::chrono::high_resolution_clock::now(); while (!mShutdown) { + if (mLuaStates.size() == 0) { + std::this_thread::sleep_for(std::chrono::seconds(500)); + } { // Timed Events Scope std::unique_lock Lock(mTimedEventsMutex); for (auto& Timer : mTimedEvents) { diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index c3e9169..49cfca8 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -203,7 +203,7 @@ void TNetwork::HandleDownload(SOCKET TCPSock) { }); } -void TNetwork::Authentication(const TConnection& ClientConnection) { +void TNetwork:: Authentication(const TConnection& ClientConnection) { auto Client = CreateClient(ClientConnection.Socket); char AddrBuf[64]; // TODO: IPv6 would need this to be changed