bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) { if (a_Split.empty()) { return false; } CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); if (cmd == m_ConsoleCommands.end()) { // Command not found return false; } if (cmd->second.m_Plugin == NULL) { // This is a built-in command return false; } // Ask plugins first if a command is okay to execute the console command: if (CallHookExecuteCommand(NULL, a_Split)) { a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str()); return false; } return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output); }
void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) { AStringVector split = StringSplit(a_Cmd, " "); if (split.empty()) { return; } // Special handling: "stop" and "restart" are built in if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0)) { return; } // "help" and "reload" are to be handled by MCS, so that they work no matter what if (split[0] == "help") { PrintHelp(split, a_Output); return; } if (split[0] == "reload") { cPluginManager::Get()->ReloadPlugins(); return; } // There is currently no way a plugin can do these (and probably won't ever be): if (split[0].compare("chunkstats") == 0) { cRoot::Get()->LogChunkStats(a_Output); a_Output.Finished(); return; } #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) if (split[0].compare("dumpmem") == 0) { LeakFinderXmlOutput Output("memdump.xml"); DumpUsedMemory(&Output); return; } if (split[0].compare("killmem") == 0) { while (true) { new char[100 * 1024 * 1024]; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS } } #endif if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output)) { a_Output.Finished(); return; } a_Output.Out("Unknown command, type 'help' for all commands."); a_Output.Finished(); }
bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) { ASSERT(!a_Split.empty()); CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); if (cmd == m_ConsoleCommands.end()) { LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".", a_Split[0].c_str(), GetName().c_str() ); return false; } cCSLock Lock(m_CriticalSection); // Push the function to be called: lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, cmd->second); // same as lua_getref() // Push the split: lua_createtable(m_LuaState, a_Split.size(), 0); int newTable = lua_gettop(m_LuaState); int index = 1; std::vector<std::string>::const_iterator iter = a_Split.begin(), end = a_Split.end(); while(iter != end) { tolua_pushstring(m_LuaState, (*iter).c_str()); lua_rawseti(m_LuaState, newTable, index); ++iter; ++index; } // Call function: int s = lua_pcall(m_LuaState, 1, 2, 0); if (report_errors(m_LuaState, s)) { LOGERROR("Lua error. Stack size: %i", lua_gettop(m_LuaState)); return false; } // Handle return values: if (lua_isstring(m_LuaState, -1)) { AString str = tolua_tocppstring(m_LuaState, -1, ""); a_Output.Out(str); } bool RetVal = (tolua_toboolean(m_LuaState, -2, 0) > 0); lua_pop(m_LuaState, 2); // Pop return values return RetVal; }
bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) { ASSERT(!a_Split.empty()); CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); if (cmd == m_ConsoleCommands.end()) { LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".", a_Split[0].c_str(), GetName().c_str() ); return false; } cCSLock Lock(m_CriticalSection); bool res = false; AString str; m_LuaState.Call(cmd->second, a_Split, a_FullCommand, cLuaState::Return, res, str); if (res && !str.empty()) { a_Output.Out(str); } return res; }
void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output) { UNUSED(a_Split); typedef std::pair<AString, AString> AStringPair; typedef std::vector<AStringPair> AStringPairs; class cCallback : public cPluginManager::cCommandEnumCallback { public: cCallback(void) : m_MaxLen(0) {} virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override { UNUSED(a_Plugin); UNUSED(a_Permission); if (!a_HelpString.empty()) { m_Commands.push_back(AStringPair(a_Command, a_HelpString)); if (m_MaxLen < a_Command.length()) { m_MaxLen = a_Command.length(); } } return false; } AStringPairs m_Commands; size_t m_MaxLen; } Callback; cPluginManager::Get()->ForEachConsoleCommand(Callback); std::sort(Callback.m_Commands.begin(), Callback.m_Commands.end()); for (AStringPairs::const_iterator itr = Callback.m_Commands.begin(), end = Callback.m_Commands.end(); itr != end; ++itr) { const AStringPair & cmd = *itr; a_Output.Out(Printf("%-*s%s\n", static_cast<int>(Callback.m_MaxLen), cmd.first.c_str(), cmd.second.c_str())); } // for itr - Callback.m_Commands[] a_Output.Finished(); }
void cRoot::LogChunkStats(cCommandOutputCallback & a_Output) { int SumNumValid = 0; int SumNumDirty = 0; int SumNumInLighting = 0; int SumNumInGenerator = 0; int SumMem = 0; for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) { cWorld * World = itr->second; int NumInGenerator = World->GetGeneratorQueueLength(); int NumInSaveQueue = (int)World->GetStorageSaveQueueLength(); int NumInLoadQueue = (int)World->GetStorageLoadQueueLength(); int NumValid = 0; int NumDirty = 0; int NumInLighting = 0; World->GetChunkStats(NumValid, NumDirty, NumInLighting); a_Output.Out("World %s:", World->GetName().c_str()); a_Output.Out(" Num loaded chunks: %d", NumValid); a_Output.Out(" Num dirty chunks: %d", NumDirty); a_Output.Out(" Num chunks in lighting queue: %d", NumInLighting); a_Output.Out(" Num chunks in generator queue: %d", NumInGenerator); a_Output.Out(" Num chunks in storage load queue: %d", NumInLoadQueue); a_Output.Out(" Num chunks in storage save queue: %d", NumInSaveQueue); int Mem = NumValid * sizeof(cChunk); a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024)); a_Output.Out(" Per-chunk memory size breakdown:"); a_Output.Out(" block types: " SIZE_T_FMT_PRECISION(6) " bytes (" SIZE_T_FMT_PRECISION(3) " KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024); a_Output.Out(" block metadata: " SIZE_T_FMT_PRECISION(6) " bytes (" SIZE_T_FMT_PRECISION(3) " KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); a_Output.Out(" block lighting: " SIZE_T_FMT_PRECISION(6) " bytes (" SIZE_T_FMT_PRECISION(3) " KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); a_Output.Out(" heightmap: " SIZE_T_FMT_PRECISION(6) " bytes (" SIZE_T_FMT_PRECISION(3) " KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024); a_Output.Out(" biomemap: " SIZE_T_FMT_PRECISION(6) " bytes (" SIZE_T_FMT_PRECISION(3) " KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024); SumNumValid += NumValid; SumNumDirty += NumDirty; SumNumInLighting += NumInLighting; SumNumInGenerator += NumInGenerator; SumMem += Mem; } a_Output.Out("Totals:"); a_Output.Out(" Num loaded chunks: %d", SumNumValid); a_Output.Out(" Num dirty chunks: %d", SumNumDirty); a_Output.Out(" Num chunks in lighting queue: %d", SumNumInLighting); a_Output.Out(" Num chunks in generator queue: %d", SumNumInGenerator); a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024)); }
void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) { AStringVector split = StringSplit(a_Cmd, " "); if (split.empty()) { return; } // "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables // "help" and "reload" are to be handled by MCS, so that they work no matter what if (split[0] == "help") { PrintHelp(split, a_Output); a_Output.Finished(); return; } else if (split[0] == "reload") { cPluginManager::Get()->ReloadPlugins(); a_Output.Finished(); return; } else if (split[0] == "reloadplugins") { cPluginManager::Get()->ReloadPlugins(); a_Output.Out("Plugins reloaded"); a_Output.Finished(); return; } else if (split[0] == "load") { if (split.size() > 1) { cPluginManager::Get()->RefreshPluginList(); // Refresh the plugin list, so that if the plugin was added just now, it is loadable a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin loaded" : "Error occurred loading plugin"); } else { a_Output.Out("Usage: load <PluginFolder>"); } a_Output.Finished(); return; } else if (split[0] == "unload") { if (split.size() > 1) { cPluginManager::Get()->UnloadPlugin(split[1]); a_Output.Out("Plugin unload scheduled"); } else { a_Output.Out("Usage: unload <PluginFolder>"); } a_Output.Finished(); return; } if (split[0] == "destroyentities") { class WorldCallback : public cWorldListCallback { virtual bool Item(cWorld * a_World) override { class EntityCallback : public cEntityCallback { virtual bool Item(cEntity * a_Entity) override { if (!a_Entity->IsPlayer()) { a_Entity->Destroy(); } return false; } } EC; a_World->ForEachEntity(EC); return false; } } WC; cRoot::Get()->ForEachWorld(WC); a_Output.Out("Destroyed all entities"); a_Output.Finished(); return; } // There is currently no way a plugin can do these (and probably won't ever be): else if (split[0].compare("chunkstats") == 0) { cRoot::Get()->LogChunkStats(a_Output); a_Output.Finished(); return; } #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) else if (split[0].compare("dumpmem") == 0) { LeakFinderXmlOutput Output("memdump.xml"); DumpUsedMemory(&Output); return; } else if (split[0].compare("killmem") == 0) { for (;;) { new char[100 * 1024 * 1024]; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS } } #endif else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output, a_Cmd)) { a_Output.Finished(); return; } a_Output.Out("Unknown command, type 'help' for all commands."); a_Output.Finished(); }