diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 576bcdc8..974f849e 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -30,6 +30,8 @@ namespace Components std::uint16_t lastAltWeapon; std::uint8_t meleeDist; float meleeYaw; + std::int8_t remoteAngles[2]; + float angles[3]; bool active; }; @@ -54,10 +56,11 @@ namespace Components { "sprint", Game::CMD_BUTTON_SPRINT }, { "leanleft", Game::CMD_BUTTON_LEAN_LEFT }, { "leanright", Game::CMD_BUTTON_LEAN_RIGHT }, - { "ads", Game::CMD_BUTTON_ADS }, + { "ads", Game::CMD_BUTTON_ADS | Game::CMD_BUTTON_THROW }, { "holdbreath", Game::CMD_BUTTON_BREATH }, { "usereload", Game::CMD_BUTTON_USE_RELOAD }, { "activate", Game::CMD_BUTTON_ACTIVATE }, + { "remote", Game::CMD_BUTTON_REMOTE }, }; void Bots::UpdateBotNames() @@ -214,7 +217,10 @@ namespace Components } ZeroMemory(&g_botai[entref.entnum], sizeof(BotMovementInfo)); - g_botai[entref.entnum].weapon = 1; + g_botai[entref.entnum].weapon = static_cast(ent->client->ps.weapCommon.weapon); + g_botai[entref.entnum].angles[0] = ent->client->ps.viewangles[0]; + g_botai[entref.entnum].angles[1] = ent->client->ps.viewangles[1]; + g_botai[entref.entnum].angles[2] = ent->client->ps.viewangles[2]; g_botai[entref.entnum].active = true; }); @@ -311,6 +317,43 @@ namespace Components g_botai[entref.entnum].meleeDist = static_cast(dist); g_botai[entref.entnum].active = true; }); + + GSC::Script::AddMethod("BotRemoteAngles", [](const Game::scr_entref_t entref) // Usage: BotRemoteAngles(, ); + { + const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref); + if (!Game::SV_IsTestClient(ent->s.number)) + { + Game::Scr_Error("BotRemoteAngles: Can only call on a bot!"); + return; + } + + const auto pitch = std::clamp(Game::Scr_GetInt(0), std::numeric_limits::min(), std::numeric_limits::max()); + const auto yaw = std::clamp(Game::Scr_GetInt(1), std::numeric_limits::min(), std::numeric_limits::max()); + + g_botai[entref.entnum].remoteAngles[0] = static_cast(pitch); + g_botai[entref.entnum].remoteAngles[1] = static_cast(yaw); + g_botai[entref.entnum].active = true; + }); + + GSC::Script::AddMethod("BotAngles", [](const Game::scr_entref_t entref) // Usage: BotAngles(, , ); + { + const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref); + if (!Game::SV_IsTestClient(ent->s.number)) + { + Game::Scr_Error("BotAngles: Can only call on a bot!"); + return; + } + + const auto pitch = Game::Scr_GetFloat(0); + const auto yaw = Game::Scr_GetFloat(1); + const auto roll = Game::Scr_GetFloat(2); + + g_botai[entref.entnum].angles[0] = pitch; + g_botai[entref.entnum].angles[1] = yaw; + g_botai[entref.entnum].angles[2] = roll; + + g_botai[entref.entnum].active = true; + }); } void Bots::BotAiAction(Game::client_s* cl) @@ -341,10 +384,12 @@ namespace Components userCmd.primaryWeaponForAltMode = g_botai[clientNum].lastAltWeapon; userCmd.meleeChargeYaw = g_botai[clientNum].meleeYaw; userCmd.meleeChargeDist = g_botai[clientNum].meleeDist; + userCmd.remoteControlAngles[0] = g_botai[clientNum].remoteAngles[0]; + userCmd.remoteControlAngles[1] = g_botai[clientNum].remoteAngles[1]; - userCmd.angles[0] = ANGLE2SHORT((cl->gentity->client->ps.viewangles[0] - cl->gentity->client->ps.delta_angles[0])); - userCmd.angles[1] = ANGLE2SHORT((cl->gentity->client->ps.viewangles[1] - cl->gentity->client->ps.delta_angles[1])); - userCmd.angles[2] = ANGLE2SHORT((cl->gentity->client->ps.viewangles[2] - cl->gentity->client->ps.delta_angles[2])); + userCmd.angles[0] = ANGLE2SHORT(g_botai[clientNum].angles[0] - cl->gentity->client->ps.delta_angles[0]); + userCmd.angles[1] = ANGLE2SHORT(g_botai[clientNum].angles[1] - cl->gentity->client->ps.delta_angles[1]); + userCmd.angles[2] = ANGLE2SHORT(g_botai[clientNum].angles[2] - cl->gentity->client->ps.delta_angles[2]); Game::SV_ClientThink(cl, &userCmd); } diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index c8fd5a54..5300c66f 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -847,11 +847,9 @@ namespace Components // Always refresh arena when loading or unloading a zone Utils::Hook::Nop(0x485017, 2); - Utils::Hook::Nop(0x4FD8C7, 2); // Gametypes Utils::Hook::Nop(0x4BDFB7, 2); // Unknown Utils::Hook::Nop(0x45ED6F, 2); // loadGameInfo Utils::Hook::Nop(0x4A5888, 2); // UI_InitOnceForAllClients - // Allow hiding specific smodels Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index e8a7ded9..ca8dfc9d 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -244,12 +244,12 @@ namespace Components return; } - auto workingDir = std::filesystem::current_path().string(); - const std::string binary = *Game::sys_exitCmdLine; - const std::string command = binary == "iw4x-sp.exe" ? "iw4x-sp" : "iw4x"; + const std::filesystem::path workingDir = std::filesystem::current_path(); + const std::wstring binary = Utils::String::Convert(*Game::sys_exitCmdLine); + const std::wstring commandLine = std::format(L"\"{}\" iw4x --pass \"{}\"", (workingDir / binary).wstring(), Utils::GetLaunchParameters()); - SetEnvironmentVariableA("MW2_INSTALL", workingDir.data()); - Utils::Library::LaunchProcess(binary, std::format("{} --pass \"{}\"", command, GetCommandLineA()), workingDir); + SetEnvironmentVariableA("MW2_INSTALL", workingDir.string().data()); + Utils::Library::LaunchProcess(binary, commandLine, workingDir); } __declspec(naked) void QuickPatch::SND_GetAliasOffset_Stub() diff --git a/src/Components/Modules/RCon.cpp b/src/Components/Modules/RCon.cpp index 87a20462..792f11bf 100644 --- a/src/Components/Modules/RCon.cpp +++ b/src/Components/Modules/RCon.cpp @@ -11,9 +11,6 @@ namespace Components std::vector RCon::RConAddresses; - RCon::Container RCon::RConContainer; - Utils::Cryptography::ECC::Key RCon::RConKey; - std::string RCon::Password; Dvar::Var RCon::RConPassword; @@ -96,25 +93,6 @@ namespace Components Network::SendCommand(target, "rconSafe", directive.SerializeAsString()); }); - Command::Add("remoteCommand", [](const Command::Params* params) - { - if (params->size() < 2) return; - - RConContainer.command = params->get(1); - - auto* addr = reinterpret_cast(0xA5EA44); - Network::Address target(addr); - if (!target.isValid() || target.getIP().full == 0) - { - target = Party::Target(); - } - - if (target.isValid()) - { - Network::SendCommand(target, "rconRequest"); - } - }); - Command::AddSV("RconWhitelistAdd", [](const Command::Params* params) { if (params->size() < 2) @@ -261,47 +239,9 @@ namespace Components if (!Dedicated::IsEnabled()) { - Network::OnClientPacket("rconAuthorization", [](const Network::Address& address, [[maybe_unused]] const std::string& data) - { - if (RConContainer.command.empty()) - { - return; - } - - const auto& key = CryptoKeyECC::Get(); - const auto signedMsg = Utils::Cryptography::ECC::SignMessage(key, data); - - Proto::RCon::Command rconExec; - rconExec.set_command(RConContainer.command); - rconExec.set_signature(signedMsg); - - Network::SendCommand(address, "rconExecute", rconExec.SerializeAsString()); - }); - return; } - // Load public key - static std::uint8_t publicKey[] = - { - 0x04, 0x01, 0x9D, 0x18, 0x7F, 0x57, 0xD8, 0x95, 0x4C, 0xEE, 0xD0, 0x21, - 0xB5, 0x00, 0x53, 0xEC, 0xEB, 0x54, 0x7C, 0x4C, 0x37, 0x18, 0x53, 0x89, - 0x40, 0x12, 0xF7, 0x08, 0x8D, 0x9A, 0x8D, 0x99, 0x9C, 0x79, 0x79, 0x59, - 0x6E, 0x32, 0x06, 0xEB, 0x49, 0x1E, 0x00, 0x99, 0x71, 0xCB, 0x4A, 0xE1, - 0x90, 0xF1, 0x7C, 0xB7, 0x4D, 0x60, 0x88, 0x0A, 0xB7, 0xF3, 0xD7, 0x0D, - 0x4F, 0x08, 0x13, 0x7C, 0xEB, 0x01, 0xFF, 0x00, 0x32, 0xEE, 0xE6, 0x23, - 0x07, 0xB1, 0xC2, 0x9E, 0x45, 0xD6, 0xD7, 0xBD, 0xED, 0x05, 0x23, 0xB5, - 0xE7, 0x83, 0xEF, 0xD7, 0x8E, 0x36, 0xDC, 0x16, 0x79, 0x74, 0xD1, 0xD5, - 0xBA, 0x2C, 0x4C, 0x28, 0x61, 0x29, 0x5C, 0x49, 0x7D, 0xD4, 0xB6, 0x56, - 0x17, 0x75, 0xF5, 0x2B, 0x58, 0xCD, 0x0D, 0x76, 0x65, 0x10, 0xF7, 0x51, - 0x69, 0x1D, 0xB9, 0x0F, 0x38, 0xF6, 0x53, 0x3B, 0xF7, 0xCE, 0x76, 0x4F, - 0x08 - }; - - RConKey.set(std::string(reinterpret_cast(publicKey), sizeof(publicKey))); - - RConContainer.timestamp = 0; - Events::OnDvarInit([] { RConPassword = Dvar::Register("rcon_password", "", Game::DVAR_NONE, "The password for rcon"); @@ -359,6 +299,7 @@ namespace Components if (!key.isValid()) { Logger::PrintError(Game::CON_CHANNEL_NETWORK, "RSA public key is invalid\n"); + return; } Proto::RCon::Command directive; @@ -382,96 +323,6 @@ namespace Components RConSafeExecutor(address, s); }, Scheduler::Pipeline::MAIN); }); - - Network::OnClientPacket("rconRequest", [](const Network::Address& address, [[maybe_unused]] const std::string& data) - { - RConContainer.address = address; - RConContainer.challenge = Utils::Cryptography::Rand::GenerateChallenge(); - RConContainer.timestamp = Game::Sys_Milliseconds(); - - Network::SendCommand(address, "rconAuthorization", RConContainer.challenge); - }); - - Network::OnClientPacket("rconExecute", [](const Network::Address& address, [[maybe_unused]] const std::string& data) - { - if (address != RConContainer.address) return; // Invalid IP - if (!RConContainer.timestamp || (Game::Sys_Milliseconds() - RConContainer.timestamp) > (1000 * 10)) return; // Timeout - - RConContainer.timestamp = 0; - - Proto::RCon::Command rconExec; - rconExec.ParseFromString(data); - - if (!Utils::Cryptography::ECC::VerifyMessage(RConKey, RConContainer.challenge, rconExec.signature())) - { - return; - } - - RConContainer.output.clear(); - Logger::PipeOutput([](const std::string& output) - { - RConContainer.output.append(output); - }); - - Command::Execute(rconExec.command(), true); - - Logger::PipeOutput(nullptr); - - Network::SendCommand(address, "print", RConContainer.output); - RConContainer.output.clear(); - }); - } - - bool RCon::CryptoKeyECC::LoadKey(Utils::Cryptography::ECC::Key& key) - { - std::string data; - if (!Utils::IO::ReadFile("./private.key", &data)) - { - return false; - } - - key.deserialize(data); - return key.isValid(); - } - - Utils::Cryptography::ECC::Key RCon::CryptoKeyECC::GenerateKey() - { - auto key = Utils::Cryptography::ECC::GenerateKey(512); - if (!key.isValid()) - { - throw std::runtime_error("Failed to generate server key!"); - } - - if (!Utils::IO::WriteFile("./private.key", key.serialize())) - { - throw std::runtime_error("Failed to write server key!"); - } - - return key; - } - - Utils::Cryptography::ECC::Key RCon::CryptoKeyECC::LoadOrGenerateKey() - { - Utils::Cryptography::ECC::Key key; - if (LoadKey(key)) - { - return key; - } - - return GenerateKey(); - } - - Utils::Cryptography::ECC::Key RCon::CryptoKeyECC::GetKeyInternal() - { - auto key = LoadOrGenerateKey(); - Utils::IO::WriteFile("./public.key", key.getPublicKey()); - return key; - } - - Utils::Cryptography::ECC::Key& RCon::CryptoKeyECC::Get() - { - static auto key = GetKeyInternal(); - return key; } Utils::Cryptography::RSA::Key RCon::CryptoKeyRSA::LoadPublicKey() diff --git a/src/Components/Modules/RCon.hpp b/src/Components/Modules/RCon.hpp index ef691ce1..ad4136db 100644 --- a/src/Components/Modules/RCon.hpp +++ b/src/Components/Modules/RCon.hpp @@ -18,17 +18,6 @@ namespace Components Network::Address address{}; }; - class CryptoKeyECC - { - public: - static Utils::Cryptography::ECC::Key& Get(); - private: - static bool LoadKey(Utils::Cryptography::ECC::Key& key); - static Utils::Cryptography::ECC::Key GenerateKey(); - static Utils::Cryptography::ECC::Key LoadOrGenerateKey(); - static Utils::Cryptography::ECC::Key GetKeyInternal(); - }; - class CryptoKeyRSA { public: @@ -52,9 +41,6 @@ namespace Components static std::vector RConAddresses; - static Container RConContainer; - static Utils::Cryptography::ECC::Key RConKey; - static std::string Password; static std::string RConOutputBuffer; diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index afc64295..38c66564 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -384,7 +384,7 @@ namespace Components Toast::Show("cardicon_headshot", "Server Browser", "Fetching servers...", 3000); - const auto url = std::format("http://iw4x.plutools.pw/v1/servers/iw4x?protocol={}", PROTOCOL); + const auto url = std::format("http://iw4x.getserve.rs/v1/servers/iw4x?protocol={}", PROTOCOL); const auto reply = Utils::WebIO("IW4x", url).setTimeout(5000)->get(); if (reply.empty()) { diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index d2c2e9b1..6778b88b 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -1978,6 +1978,7 @@ namespace Game CMD_BUTTON_FRAG = 1 << 14, CMD_BUTTON_OFFHAND_SECONDARY = 1 << 15, CMD_BUTTON_THROW = 1 << 19, + CMD_BUTTON_REMOTE = 1 << 20, }; struct usercmd_s diff --git a/src/Utils/Library.cpp b/src/Utils/Library.cpp index 70d661de..d5220666 100644 --- a/src/Utils/Library.cpp +++ b/src/Utils/Library.cpp @@ -102,17 +102,17 @@ namespace Utils this->module_ = nullptr; } - void Library::LaunchProcess(const std::string& process, const std::string& commandLine, const std::string& currentDir) + void Library::LaunchProcess(const std::wstring& process, const std::wstring& commandLine, const std::filesystem::path& currentDir) { - STARTUPINFOA startupInfo; + STARTUPINFOW startupInfo; PROCESS_INFORMATION processInfo; ZeroMemory(&startupInfo, sizeof(startupInfo)); ZeroMemory(&processInfo, sizeof(processInfo)); startupInfo.cb = sizeof(startupInfo); - CreateProcessA(process.data(), const_cast(commandLine.data()), nullptr, - nullptr, false, NULL, nullptr, currentDir.data(), + CreateProcessW(process.data(), const_cast(commandLine.data()), nullptr, + nullptr, false, NULL, nullptr, currentDir.wstring().data(), &startupInfo, &processInfo); if (processInfo.hThread && processInfo.hThread != INVALID_HANDLE_VALUE) diff --git a/src/Utils/Library.hpp b/src/Utils/Library.hpp index 367d9d08..3ff72663 100644 --- a/src/Utils/Library.hpp +++ b/src/Utils/Library.hpp @@ -65,7 +65,7 @@ namespace Utils return T(); } - static void LaunchProcess(const std::string& process, const std::string& commandLine, const std::string& currentDir); + static void LaunchProcess(const std::wstring& process, const std::wstring& commandLine, const std::filesystem::path& currentDir); private: HMODULE module_; diff --git a/src/Utils/Utils.cpp b/src/Utils/Utils.cpp index 141a0fcc..60e06a5f 100644 --- a/src/Utils/Utils.cpp +++ b/src/Utils/Utils.cpp @@ -163,6 +163,28 @@ namespace Utils } } + std::wstring GetLaunchParameters() + { + std::wstring args; + + int numArgs; + auto* const argv = CommandLineToArgvW(GetCommandLineW(), &numArgs); + + if (argv) + { + for (auto i = 1; i < numArgs; ++i) + { + std::wstring arg(argv[i]); + args.append(arg); + args.append(L" "); + } + + LocalFree(argv); + } + + return args; + } + HMODULE GetNTDLL() { return GetModuleHandleA("ntdll.dll"); diff --git a/src/Utils/Utils.hpp b/src/Utils/Utils.hpp index f864a670..9ec8664b 100644 --- a/src/Utils/Utils.hpp +++ b/src/Utils/Utils.hpp @@ -24,6 +24,8 @@ namespace Utils void OpenUrl(const std::string& url); + std::wstring GetLaunchParameters(); + bool HasIntersection(unsigned int base1, unsigned int len1, unsigned int base2, unsigned int len2); template