#include "CCommandProcessor.h" #include "CConVarManager.h" #include "CEngine.h" #include "U_String.h" #include "CLogger.h" #include "CWrapable.h" #include #include #include #include std::unordered_map CmdStringTypes = { { "client", CCommand::CType::Client }, { "serverpreprocess", CCommand::CType::ServerPreProcess}, { "server", CCommand::CType::Server } }; std::unordered_map CmdStringRights = { { "client", CCommand::CmdRight::Client }, { "clientonly", CCommand::CmdRight::ClientOnly }, { "admin", CCommand::CmdRight::Admin }, { "adminonly", CCommand::CmdRight::AdminOnly }, { "console", CCommand::CmdRight::Console } }; std::unordered_map CmdStringStatuses = { { "ok", CCommand::CmdStatus::OK }, { "incorrect", CCommand::CmdStatus::Incorrect }, { "error", CCommand::CmdStatus::Error }, { "server", CCommand::CmdStatus::Server } }; bool CCommandProcessor::V_ScriptInit(std::shared_ptr _state, sol::table table) { if(_state->get("command") == sol::lua_nil) { sol::table command = _state->create_named_table("command"); sol::table type = command.create_named("type"); sol::table rights = command.create_named("rights"); sol::table status = command.create_named("status"); for(auto& kv : CmdStringTypes) { type.set(kv.first, int(kv.second)); } for(auto& kv : CmdStringRights) { rights.set(kv.first, int(kv.second)); } for(auto& kv : CmdStringStatuses) { status.set(kv.first, int(kv.second)); } } table.set_function("register", [this](const std::string& cmdname, int cmdtype, int cmdrights, sol::function cmdfunc) -> int { if(cmdname.empty() || Commands.IsCommandPresent(cmdname)) { return 1; } if(!cmdfunc.valid()) { return 2; } Commands.CreateLuaCommand(cmdname, (CCommand::CType)cmdtype, (CCommand::CmdRight)cmdrights, cmdfunc); return 0; }); table.set_function("process", [this](const std::wstring& cmdline) -> void //TODO maybe return statuses + ability to specify sender { ProcessCommandLine(cmdline, CCommandSender::Self); }); return true; } void CCommandProcessor::AddCommandToQueue(const std::wstring& cmd) { std::lock_guard lock(Mutex); Queue.push_back(cmd); History.SentCommand(cmd); } void CCommandProcessor::ProcessQueue() { std::vector queue_copy; { std::lock_guard lock(Mutex); if(Queue.empty()) { return; } queue_copy.swap(Queue); } bool echo = true; COMPONENT_CALL_GET(echo, CConVarManager, GetConVarValue("con.echo")); for (auto& cmd : queue_copy) { if(echo) { COMPONENT_CALL(CLogger, Outln(L"> " + cmd)); } ProcessCommandLine(cmd, CCommandSender::Self); } } void CCommandProcessor::V_Update() { ProcessQueue(); } std::wstring CCommandProcessor::ProcessSpecialChars(const std::wstring& cmdline) { std::wstring line = cmdline; StringUtils::replace(line, L"\\", L"\\\\"); StringUtils::replace(line, L"\n", L"\\n"); StringUtils::replace(line, L"\r", L"\\r"); StringUtils::replace(line, L"\"", L"\\\""); StringUtils::replace(line, L";", L"\\;"); return line; } CCommand::CmdStatus CCommandProcessor::ProcessSingleCommand(const std::string& cmd, const CCommandArgsWrapper& args, const CCommandSender& sender) { if(!m_customProcs.empty()) { m_customProcs.top()->ProcessSingleCommand(cmd, args, sender); return CCommand::CmdStatus::OK; } CConVarManager::CVarNode* cvar = nullptr; COMPONENT_CALL_GET(cvar, CConVarManager, Root.GetNode(cmd)); auto italias = Aliases.find(cmd); if(italias != Aliases.end()) //TODO aliases only for self sender (console) { std::wstring cmdline = italias->second; for(size_t i = 0; i < args.size(); i++) { std::wstring thisArg = L"%ARG" + std::to_wstring(i) + L"%"; StringUtils::replace(cmdline, thisArg, args.at(i)); } ProcessCommandLine(cmdline, sender); return CCommand::CmdStatus::OK; } bool exist = Commands.IsCommandPresent(cmd); if(cvar && !exist) { if(args.empty()) { std::wstringstream ws; ws << StringUtils::StrToWstr(cmd) << L" is a node " << (cvar->Value.Wrapable ? (L"with value \"" + cvar->Value.Wrapable->GetValueStr() + L"\"") : L"without value") << '\n'; COMPONENT_CALL(CLogger, Out(ws.str())); } else { std::wstring val = args.Merge(0, args.size() - 1); if(cvar->Value.Wrapable) { cvar->Value.Wrapable->SetValueStr(val); } } return CCommand::CmdStatus::OK; } if (!exist) { return CCommand::CmdStatus::Incorrect; } auto& command = Commands.GetCommand(cmd); //TODO command/user rights check CCommand::CmdStatus status = command->CallHandler(args, sender); return status; } std::vector> CCommandProcessor::PreProcessString(const std::wstring& cmdline) //TODO rewrite it { if (StringUtils::IsEmpty(cmdline)) { return {}; } bool inString = false; std::vector cmdlines; std::wstring procString = cmdline; std::vector> ret; wchar_t reat = procString.front(); while (reat == L' ') { procString.erase(procString.begin()); reat = procString.front(); } reat = procString.back(); while (reat == L' ') { procString.erase(procString.begin() + (procString.size() - 1)); reat = procString.back(); } for (size_t i = 0; i < procString.size(); ++i) { if ((i > 0 && procString.at(i) == L'\"' && procString.at(i - 1) != L'\\') || (i == 0 && procString.at(0) == L'\"')) { inString = !inString; continue; } if (i > 0 && !inString && (procString.at(i) == L' ' || procString.at(i) == L'\t') && (procString.at(i - 1) == L' ' || procString.at(i - 1) == L'\t')) { procString.erase(procString.begin() + (i - 1)); i--; } } inString = false; for (size_t i = 0; i < procString.size(); ++i) { if ((i > 0 && procString.at(i) == L'\"' && procString.at(i - 1) != L'\\') || (i == 0 && procString.at(0) == L'\"')) { inString = !inString; continue; } if ((i + 1) < procString.size() && (procString.at(i) == L' ' || procString.at(i) == L'\t') && procString.at(i + 1) == L';') { procString.erase(procString.begin() + i); i--; } } inString = false; std::wstring curCmd; for (size_t i = 0; i < procString.size(); ++i) { if ((i > 0 && procString.at(i) == L'\"' && procString.at(i - 1) != L'\\') || (i == 0 && procString.at(0) == L'\"')) { inString = !inString; } if (procString.at(i) == L';' && ((i == 0) || (procString.at(i - 1) != L'\\')) && !inString) { procString.erase(procString.begin() + i); if (!StringUtils::IsEmpty(curCmd)) { cmdlines.push_back(curCmd); //Log::Instance() << "cmdlines.push_back(\"" << curCmd << "\")\n"; curCmd.clear(); } //continue; TODO hack (when commented) } curCmd += procString.at(i); if ((i + 1) >= procString.size()) { if (!StringUtils::IsEmpty(curCmd)) { cmdlines.push_back(curCmd); //Log::Instance() << "cmdlines.push_back2(\"" << curCmd << "\")\n"; curCmd.clear(); } } } for (std::wstring& s : cmdlines) { bool inStr = false; std::wstring curline; std::vector toadd; for (size_t i = 0; i < s.size(); ++i) { if ((i > 0 && s.at(i) == '\"' && s.at(i - 1) != '\\') || (i == 0 && s.at(0) == '\"')) { inStr = !inStr; if ((i + 1) >= s.size()) { toadd.push_back(curline); curline.clear(); } continue; } if ((s.at(i) == ' ' || s.at(i) == '\t') && !inStr) { if (!StringUtils::IsEmpty(curline)) { toadd.push_back(curline); curline.clear(); } continue; } curline += s.at(i); if ((i + 1) >= s.size()) { if (!StringUtils::IsEmpty(curline)) { toadd.push_back(curline); curline.clear(); } } } for (std::wstring& ins : toadd) { StringUtils::replace(ins, L"\\\\", L"\\"); StringUtils::replace(ins, L"\\n", L"\n"); StringUtils::replace(ins, L"\\r", L"\r"); StringUtils::replace(ins, L"\\\"", L"\""); StringUtils::replace(ins, L"\\;", L";"); } if (!toadd.empty()) { ret.push_back(toadd); } } return ret; } void CCommandProcessor::ProcessCommandLine(const std::wstring& cmdline, const CCommandSender& sender) //TODO maybe return vector { std::vector> commands = PreProcessString(cmdline); for (std::vector& args : commands) { if (args.empty()) { continue; } std::wstring wcommand = args.front(); std::string command = StringUtils::WstrToStr(args.front()); args.erase(args.begin()); CCommand::CmdStatus status = ProcessSingleCommand(command, args, sender); if (status == CCommand::CmdStatus::Incorrect) { //TODO status notification } } } CCommandProcessor::CCommandsHistory::CCommandsHistory() { Commands.reserve(64); COMPONENT_CALL(CConVarManager, AddConVar("con.history.max", new CWrapable(64))); COMPONENT_CALL(CConVarManager, AddConVar("con.history.edit.keepindex", new CWrapable(1))); COMPONENT_CALL(CConVarManager, AddConVar("con.history.edit.replace", new CWrapable(0))); } void CCommandProcessor::CCommandsHistory::Add(const std::wstring& cmd) { if (cmd.empty()) { return; } unsigned int historyMax = 64; COMPONENT_CALL_GET(historyMax, CConVarManager, GetConVarValue("con.history.max", 64)); if (Commands.size() + 1 >= historyMax) { Commands.erase(Commands.begin()); } Commands.push_back(cmd); } std::wstring CCommandProcessor::CCommandsHistory::GetNext() { if (Commands.empty()) { return CurrentInput; } if (Index + 1 < Commands.size()) { Index++; } return *(Commands.rbegin() + Index); } std::wstring CCommandProcessor::CCommandsHistory::GetPrev() { if (Commands.empty()) { return CurrentInput; } if (Index - 1 >= -1) { Index--; } if (Index < 0) { return CurrentInput; } return *(Commands.rbegin() + Index); } void CCommandProcessor::CCommandsHistory::SentCommand(const std::wstring& cmd, bool add) { Index = -1; CurrentInput.clear(); if (add) { Add(cmd); } } void CCommandProcessor::CCommandsHistory::EditedCommandAsync(const std::wstring& cmd) { std::lock_guard lock(Mutex); EditedCommand(cmd); } void CCommandProcessor::CCommandsHistory::EditedCommand(const std::wstring& cmd) { bool keepindex = true, editreplace = false; COMPONENT_CALL_GET(keepindex, CConVarManager, GetConVarValue("con.history.edit.keepindex", 1)); COMPONENT_CALL_GET(editreplace, CConVarManager, GetConVarValue("con.history.edit.replace", 0)); if (keepindex && editreplace && !cmd.empty()) { Commands.at(Index) = cmd; } else if (!keepindex) { CurrentInput = cmd; Index = -1; } } void CCommandProcessor::CCommandsHistory::Clear() { Index = -1; Commands.clear(); } void CCommandProcessor::CCommandsHistory::Reset() { Index = -1; CurrentInput.clear(); } void CCommandProcessor::AddCustomProcessor(std::unique_ptr&& ptr) { m_customProcs.push(std::move(ptr)); } void CCommandProcessor::QuitCustomProcessor() { if(m_customProcs.empty()) { return; } m_customProcs.top()->OnQuit(m_customProcs.top().get()); m_customProcs.pop(); } void CCustomCommandProcessor::Quit() { COMPONENT_CALL(CCommandProcessor, QuitCustomProcessor()); } void CCustomCommandProcessor::ProcessSingleCommand(const std::string& cmd, const CCommandArgsWrapper& args, const CCommandSender& sender) { if(cmd == "quit" || cmd == "exit") { return Quit(); } return m_process(cmd, args, sender); } void CCommandProcessor::AddAlias(const std::string& _cmd1, const std::wstring& _cmd2) { auto it = Aliases.find(_cmd1); if(it != Aliases.end()) { Aliases.erase(it); } Aliases.emplace(_cmd1, _cmd2); } std::wstring CCommandProcessor::GetAlias(const std::string& _alias) const { auto it = Aliases.find(_alias); return it != Aliases.end() ? it->second : std::wstring{}; } bool CCommandProcessor::AliasExists(const std::string& _alias) const { return Aliases.find(_alias) != Aliases.end(); } void CCommandProcessor::RemoveAlias(const std::string& _alias) { auto it = Aliases.find(_alias); if(it != Aliases.end()) { Aliases.erase(it); } } sol::object CCommandProcessor::V_GetScriptUserType(sol::state_view state) { return sol::make_object(state, this); } LINK_COMPONENT_TO_CLASS(CCommandProcessor, commandprocessor);