#include "LuaAPI.h" #include "Client.h" #include "Common.h" #include "TLuaEngine.h" #define SOL_ALL_SAFETIES_ON 1 #include std::string LuaAPI::LuaToString(const sol::object Value, size_t Indent, bool QuoteStrings) { if (Indent > 80) { return "[[possible recursion, refusing to keep printing]]"; } switch (Value.get_type()) { case sol::type::userdata: { std::stringstream ss; ss << "[[userdata: " << Value.as().pointer() << "]]"; return ss.str(); } case sol::type::thread: { std::stringstream ss; ss << "[[thread: " << Value.as().pointer() << "]] {" << "\n"; for (size_t i = 0; i < Indent; ++i) { ss << "\t"; } ss << "status: " << std::to_string(int(Value.as().status())) << "\n}"; return ss.str(); } case sol::type::lightuserdata: { std::stringstream ss; ss << "[[lightuserdata: " << Value.as().pointer() << "]]"; return ss.str(); } case sol::type::string: if (QuoteStrings) { return "\"" + Value.as() + "\""; } else { return Value.as(); } case sol::type::number: { std::stringstream ss; ss << Value.as(); return ss.str(); } case sol::type::lua_nil: case sol::type::none: return ""; case sol::type::boolean: return Value.as() ? "true" : "false"; case sol::type::table: { std::stringstream Result; auto Table = Value.as(); Result << "[[table: " << Table.pointer() << "]]: {"; if (!Table.empty()) { for (const auto& Entry : Table) { Result << "\n"; for (size_t i = 0; i < Indent; ++i) { Result << "\t"; } Result << LuaToString(Entry.first, Indent + 1) << ": " << LuaToString(Entry.second, Indent + 1, true) << ","; } Result << "\n"; } for (size_t i = 0; i < Indent - 1; ++i) { Result << "\t"; } Result << "}"; return Result.str(); } case sol::type::function: { std::stringstream ss; ss << "[[function: " << Value.as().pointer() << "]]"; return ss.str(); } default: return "((unprintable type))"; } } std::string LuaAPI::MP::GetOSName() { #if WIN32 return "Windows"; #elif __linux return "Linux"; #else return "Other"; #endif } std::tuple LuaAPI::MP::GetServerVersion() { return { Application::ServerVersion().major, Application::ServerVersion().minor, Application::ServerVersion().patch }; } void LuaAPI::Print(sol::variadic_args Args) { std::string ToPrint = ""; for (const auto& Arg : Args) { ToPrint += LuaToString(static_cast(Arg)); ToPrint += "\t"; } luaprint(ToPrint); } bool LuaAPI::MP::TriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) { std::string Packet = "E:" + EventName + ":" + Data; if (PlayerID == -1) Engine->Network().SendToAll(nullptr, Packet, true, true); else { auto MaybeClient = GetClient(Engine->Server(), PlayerID); if (!MaybeClient || MaybeClient.value().expired()) { beammp_lua_error("TriggerClientEvent invalid Player ID"); return false; } auto c = MaybeClient.value().lock(); if (!Engine->Network().Respond(*c, Packet, true)) { beammp_lua_error("Respond failed, dropping client " + std::to_string(PlayerID)); Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets"); return false; } } return true; } void LuaAPI::MP::DropPlayer(int ID, std::optional MaybeReason) { auto MaybeClient = GetClient(Engine->Server(), ID); if (!MaybeClient || MaybeClient.value().expired()) { beammp_lua_error("Tried to drop client with id " + std::to_string(ID) + ", who doesn't exist"); return; } auto c = MaybeClient.value().lock(); LuaAPI::MP::Engine->Network().ClientKick(*c, MaybeReason.value_or("No reason")); } void LuaAPI::MP::SendChatMessage(int ID, const std::string& Message) { std::string Packet = "C:Server: " + Message; if (ID == -1) { LogChatMessage(" (to everyone) ", -1, Message); Engine->Network().SendToAll(nullptr, Packet, true, true); } else { auto MaybeClient = GetClient(Engine->Server(), ID); if (MaybeClient && !MaybeClient.value().expired()) { auto c = MaybeClient.value().lock(); if (!c->IsSynced()) return; LogChatMessage(" (to \"" + c->GetName() + "\")", -1, Message); Engine->Network().Respond(*c, Packet, true); } else { beammp_lua_error("SendChatMessage invalid argument [1] invalid ID"); } } } void LuaAPI::MP::RemoveVehicle(int PID, int VID) { auto MaybeClient = GetClient(Engine->Server(), PID); if (!MaybeClient || MaybeClient.value().expired()) { beammp_lua_error("RemoveVehicle invalid Player ID"); return; } auto c = MaybeClient.value().lock(); if (!c->GetCarData(VID).empty()) { std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID); Engine->Network().SendToAll(nullptr, Destroy, true, true); c->DeleteCar(VID); } } void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) { switch (ConfigID) { case 0: // debug if (NewValue.is()) { Application::Settings.DebugModeEnabled = NewValue.as(); beammp_info(std::string("Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false")); } else beammp_lua_error("set invalid argument [2] expected boolean"); break; case 1: // private if (NewValue.is()) { Application::Settings.Private = NewValue.as(); beammp_info(std::string("Set `Private` to ") + (Application::Settings.Private ? "true" : "false")); } else beammp_lua_error("set invalid argument [2] expected boolean"); break; case 2: // max cars if (NewValue.is()) { Application::Settings.MaxCars = NewValue.as(); beammp_info(std::string("Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars)); } else beammp_lua_error("set invalid argument [2] expected integer"); break; case 3: // max players if (NewValue.is()) { Application::Settings.MaxPlayers = NewValue.as(); beammp_info(std::string("Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers)); } else beammp_lua_error("set invalid argument [2] expected integer"); break; case 4: // Map if (NewValue.is()) { Application::Settings.MapName = NewValue.as(); beammp_info(std::string("Set `Map` to ") + Application::Settings.MapName); } else beammp_lua_error("set invalid argument [2] expected string"); break; case 5: // Name if (NewValue.is()) { Application::Settings.ServerName = NewValue.as(); beammp_info(std::string("Set `Name` to ") + Application::Settings.ServerName); } else beammp_lua_error("set invalid argument [2] expected string"); break; case 6: // Desc if (NewValue.is()) { Application::Settings.ServerDesc = NewValue.as(); beammp_info(std::string("Set `Description` to ") + Application::Settings.ServerDesc); } else beammp_lua_error("set invalid argument [2] expected string"); break; default: beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this."); break; } } void LuaAPI::MP::Sleep(size_t Ms) { std::this_thread::sleep_for(std::chrono::milliseconds(Ms)); } bool LuaAPI::MP::IsPlayerConnected(int ID) { auto MaybeClient = GetClient(Engine->Server(), ID); if (MaybeClient && !MaybeClient.value().expired()) { return MaybeClient.value().lock()->IsConnected(); } else { return false; } } bool LuaAPI::MP::IsPlayerGuest(int ID) { auto MaybeClient = GetClient(Engine->Server(), ID); if (MaybeClient && !MaybeClient.value().expired()) { return MaybeClient.value().lock()->IsGuest(); } else { return false; } } void LuaAPI::MP::PrintRaw(sol::variadic_args Args) { std::string ToPrint = ""; for (const auto& Arg : Args) { ToPrint += LuaToString(static_cast(Arg)); ToPrint += "\t"; } Application::Console().WriteRaw(ToPrint); } int LuaAPI::PanicHandler(lua_State* State) { beammp_lua_error("PANIC: " + sol::stack::get(State, 1)); return 0; } template static std::pair FSWrapper(FnT Fn, ArgsT&&... Args) { std::error_code errc; std::pair Result; Fn(std::forward(Args)..., errc); Result.first = errc == std::error_code {}; if (!Result.first) { Result.second = errc.message(); } return Result; } std::pair LuaAPI::FS::CreateDirectory(const std::string& Path) { std::error_code errc; std::pair Result; fs::create_directories(fs::relative(Path), errc); Result.first = errc == std::error_code {}; if (!Result.first) { Result.second = errc.message(); } return Result; } std::pair LuaAPI::FS::Remove(const std::string& Path) { std::error_code errc; std::pair Result; fs::remove(fs::relative(Path), errc); Result.first = errc == std::error_code {}; if (!Result.first) { Result.second = errc.message(); } return Result; } std::pair LuaAPI::FS::Rename(const std::string& Path, const std::string& NewPath) { std::error_code errc; std::pair Result; fs::rename(fs::relative(Path), fs::relative(NewPath), errc); Result.first = errc == std::error_code {}; if (!Result.first) { Result.second = errc.message(); } return Result; } std::pair LuaAPI::FS::Copy(const std::string& Path, const std::string& NewPath) { std::error_code errc; std::pair Result; fs::copy(fs::relative(Path), fs::relative(NewPath), fs::copy_options::recursive, errc); Result.first = errc == std::error_code {}; if (!Result.first) { Result.second = errc.message(); } return Result; } bool LuaAPI::FS::Exists(const std::string& Path) { return fs::exists(fs::relative(Path)); } std::string LuaAPI::FS::GetFilename(const std::string& Path) { return fs::path(Path).filename().string(); } std::string LuaAPI::FS::GetExtension(const std::string& Path) { return fs::path(Path).extension().string(); } std::string LuaAPI::FS::GetParentFolder(const std::string& Path) { return fs::path(Path).parent_path().string(); } bool LuaAPI::FS::IsDirectory(const std::string& Path) { return fs::is_directory(Path); } bool LuaAPI::FS::IsFile(const std::string& Path) { return fs::is_regular_file(Path); } std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) { fs::path Path; for (size_t i = 0; i < Args.size(); ++i) { auto Obj = Args[i]; if (!Obj.is()) { beammp_lua_error("FS.Concat called with non-string argument"); return ""; } Path += Obj.as(); if (i < Args.size() - 1 && !Path.empty()) { Path += fs::path::preferred_separator; } } auto Result = Path.lexically_normal().string(); return Result; }