diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 16367371..3b7d2c46 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -21,6 +21,7 @@ namespace Components Loader::Register(new Console()); Loader::Register(new IPCPipe()); Loader::Register(new Network()); + Loader::Register(new Playlist()); Loader::Register(new RawFiles()); Loader::Register(new Renderer()); Loader::Register(new UIFeeder()); diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 711cadea..6928fd3e 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -32,6 +32,7 @@ namespace Components #include "Modules\IPCPipe.hpp" #include "Modules\Network.hpp" #include "Modules\Party.hpp" // Destroys the order, but requires network classes :D +#include "Modules\Playlist.hpp" #include "Modules\RawFiles.hpp" #include "Modules\Renderer.hpp" #include "Modules\UIFeeder.hpp" diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 03a53795..78208c7c 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -129,11 +129,7 @@ namespace Components callback(); } - __asm - { - mov eax, 5A8E80h - call eax - } + Utils::Hook::Call(0x5A8E80); } Dedicated::Dedicated() @@ -231,6 +227,13 @@ namespace Components Dedicated::Heartbeat(); } }); + + // Wrap xstartprivatematch + Command::Add("lobby_start", [] (Command::Params params) + { + Playlist::LoadPlaylist(); + Command::Execute("xstartprivatematch", false); + }); } } diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index abbee067..cd4226e0 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -17,9 +17,15 @@ namespace Components return id; } + Network::Address Party::Target() + { + return Party::Container.Target; + } + void Party::Connect(Network::Address target) { Party::Container.Valid = true; + Party::Container.AwaitingPlaylist = false; Party::Container.JoinTime = Game::Com_Milliseconds(); Party::Container.Target = target; Party::Container.Challenge = Utils::VA("%X", Party::Container.JoinTime); @@ -68,6 +74,26 @@ namespace Components return Dvar::Register(name, 1, 1, max, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED | flag, description).Get(); } + bool Party::PlaylistAwaiting() + { + return Party::Container.AwaitingPlaylist; + } + + void Party::PlaylistContinue() + { + Party::Container.AwaitingPlaylist = false; + + SteamID id = Party::GenerateLobbyId(); + + Party::LobbyMap[id.Bits] = Party::Container.Target; + + Game::Steam_JoinLobby(id, 0); + + // Callback not registered on first try + // TODO: Fix :D + if (Party::LobbyMap.size() <= 1) Game::Steam_JoinLobby(id, 0); + } + Party::Party() { // various changes to SV_DirectConnect-y stuff to allow non-party joinees @@ -126,12 +152,22 @@ namespace Components Renderer::OnFrame([] () { - if (!Party::Container.Valid) return; - - if ((Game::Com_Milliseconds() - Party::Container.JoinTime) > 5000) + if (Party::Container.Valid) { - Party::Container.Valid = false; - Party::ConnectError("Server connection timed out."); + if ((Game::Com_Milliseconds() - Party::Container.JoinTime) > 5000) + { + Party::Container.Valid = false; + Party::ConnectError("Server connection timed out."); + } + } + + if (Party::Container.AwaitingPlaylist) + { + if ((Game::Com_Milliseconds() - Party::Container.RequestTime) > 5000) + { + Party::Container.AwaitingPlaylist = false; + Party::ConnectError("Playlist request timed out."); + } } }); @@ -219,15 +255,10 @@ namespace Components // Connect else if (matchType == 1) // Party { - SteamID id = Party::GenerateLobbyId(); - - Party::LobbyMap[id.Bits] = address; - - Game::Steam_JoinLobby(id, 0); - - // Callback not registered on first try - // TODO: Fix :D - if (Party::LobbyMap.size() <= 1) Game::Steam_JoinLobby(id, 0); + // Send playlist request + Party::Container.RequestTime = Game::Com_Milliseconds(); + Party::Container.AwaitingPlaylist = true; + Network::Send(address, "getplaylist\n"); } else if (matchType == 2) // Match { diff --git a/src/Components/Modules/Party.hpp b/src/Components/Modules/Party.hpp index 887f166e..c5af0afa 100644 --- a/src/Components/Modules/Party.hpp +++ b/src/Components/Modules/Party.hpp @@ -7,10 +7,14 @@ namespace Components ~Party(); const char* GetName() { return "Party"; }; + static Network::Address Target(); static void Connect(Network::Address target); static const char* GetLobbyInfo(SteamID lobby, std::string key); static void RemoveLobby(SteamID lobby); + static bool PlaylistAwaiting(); + static void PlaylistContinue(); + private: struct JoinContainer { @@ -18,6 +22,10 @@ namespace Components std::string Challenge; DWORD JoinTime; bool Valid; + + // Party-specific stuff + DWORD RequestTime; + bool AwaitingPlaylist; }; static JoinContainer Container; diff --git a/src/Components/Modules/Playlist.cpp b/src/Components/Modules/Playlist.cpp new file mode 100644 index 00000000..26ddd2b4 --- /dev/null +++ b/src/Components/Modules/Playlist.cpp @@ -0,0 +1,119 @@ +#include "..\..\STDInclude.hpp" + +namespace Components +{ + std::string Playlist::CurrentPlaylistBuffer; + + void Playlist::LoadPlaylist() + { + // Check if playlist already loaded + if (Utils::Hook::Get(0x1AD3680)) return; + + std::string playlistFilename = Dvar::Var("playlistFilename").Get(); + FileSystem::File playlist(playlistFilename); + + if (playlist.Exists()) + { + Logger::Print("Parsing playlist '%s'...\n", playlist.GetName().data()); + Game::Live_ParsePlaylists(playlist.GetBuffer().data()); + Utils::Hook::Set(0x1AD3680, true); // Playlist loaded + } + else + { + Logger::Print("Unable to load playlist '%s'!\n", playlist.GetName().data()); + } + } + + DWORD Playlist::StorePlaylistStub(const char** buffer) + { + Playlist::CurrentPlaylistBuffer = *buffer; + //return Utils::Hook::Call(0x4C0350); + __asm + { + push buffer + mov eax, 4C0350h + call eax + add esp, 4h + } + } + + void Playlist::PlaylistRequest(Network::Address address, std::string data) + { + Logger::Print("Received playlist request, sending currently stored buffer.\n"); + Network::Send(address, std::string("playlistresponse\n") + Playlist::CurrentPlaylistBuffer); + } + + void Playlist::PlaylistReponse(Network::Address address, std::string data) + { + if (Party::PlaylistAwaiting()) + { + if (address == Party::Target()) + { + Logger::Print("Received playlist response, loading and continuing connection.\n"); + Game::Live_ParsePlaylists(data.data()); + Party::PlaylistContinue(); + } + else + { + Logger::Print("Received playlist from someone else than our target host, ignoring it.\n"); + } + } + else + { + Logger::Print("Received stray playlist response, ignoring it.\n"); + } + } + + Playlist::Playlist() + { + // Apply new playlist + char* playlist = "mp_playlists_dlc2"; + Utils::Hook::Set(0x494803, playlist); + Utils::Hook::Set(0x4C6EC1, playlist); + Utils::Hook::Set(0x4CF7F9, playlist); + Utils::Hook::Set(0x4D6E63, playlist); + Utils::Hook::Set(0x4D7358, playlist); + Utils::Hook::Set(0x4D73C8, playlist); + Utils::Hook::Set(0x4F4EA1, playlist); + Utils::Hook::Set(0x4D47FB, "mp_playlists_dlc2.ff"); + Utils::Hook::Set(0x60B06E, "playlists.patch2"); + + // disable playlist download function + Utils::Hook::Set(0x4D4790, 0xC3); + + // Load playlist, but don't delete it + Utils::Hook::Nop(0x4D6EBB, 5); + Utils::Hook::Nop(0x4D6E67, 5); + Utils::Hook::Nop(0x4D6E71, 2); + + // playlist dvar 'validity check' + Utils::Hook::Set(0x4B1170, 0xC3); + + // disable playlist checking + Utils::Hook::Set(0x5B69E9, 0xEB);// too new + Utils::Hook::Set(0x5B696E, 0xEB); // too old + + //Got playlists is true + //Utils::Hook::Set(0x1AD3680, true); + + // Store playlist buffer on load + Utils::Hook(0x42961C, Playlist::StorePlaylistStub, HOOK_CALL).Install()->Quick(); + + if (Dedicated::IsDedicated()) + { + // Custom playlist loading + Utils::Hook(0x420B5A, Playlist::LoadPlaylist, HOOK_JUMP).Install()->Quick(); + + // disable playlist.ff loading function + Utils::Hook::Set(0x4D6E60, 0xC3); + } + + Network::Handle("getplaylist", PlaylistRequest); + Network::Handle("playlistresponse", PlaylistReponse); + } + + Playlist::~Playlist() + { + Playlist::CurrentPlaylistBuffer.clear(); + } +} diff --git a/src/Components/Modules/Playlist.hpp b/src/Components/Modules/Playlist.hpp new file mode 100644 index 00000000..68ed320e --- /dev/null +++ b/src/Components/Modules/Playlist.hpp @@ -0,0 +1,22 @@ +namespace Components +{ + class Playlist : public Component + { + public: + typedef void(*Callback)(); + + Playlist(); + ~Playlist(); + const char* GetName() { return "Playlist"; }; + + static void LoadPlaylist(); + + private: + static std::string CurrentPlaylistBuffer; + + static DWORD StorePlaylistStub(const char** buffer); + + static void PlaylistRequest(Network::Address address, std::string data); + static void PlaylistReponse(Network::Address address, std::string data); + }; +} diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 3214bc02..d848d25a 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -28,35 +28,6 @@ namespace Components Utils::Hook::Nop(0x451145, 5); Utils::Hook::Set(0x45114C, 0xEB); - // Apply new playlist - char* playlist = "mp_playlists_dlc2"; - Utils::Hook::Set(0x494803, playlist); - Utils::Hook::Set(0x4C6EC1, playlist); - Utils::Hook::Set(0x4CF7F9, playlist); - Utils::Hook::Set(0x4D6E63, playlist); - Utils::Hook::Set(0x4D7358, playlist); - Utils::Hook::Set(0x4D73C8, playlist); - Utils::Hook::Set(0x4F4EA1, playlist); - Utils::Hook::Set(0x4D47FB, "mp_playlists_dlc2.ff"); - Utils::Hook::Set(0x60B06E, "playlists.patch2"); - - // disable playlist download function - Utils::Hook::Set(0x4D4790, 0xC3); - - // disable playlist.ff loading function - //Utils::Hook::Set(0x4D6E60, 0xC3); - - // Load playlist, but don't delete it - Utils::Hook::Nop(0x4D6EBB, 5); - Utils::Hook::Nop(0x4D6E67, 5); - Utils::Hook::Nop(0x4D6E71, 2); - - // playlist dvar 'validity check' - Utils::Hook::Set(0x4B1170, 0xC3); - - //Got playlists is true - //Utils::Hook::Set(0x1AD3680, true); - // LSP disabled Utils::Hook::Set(0x435950, 0xC3); // LSP HELLO Utils::Hook::Set(0x49C220, 0xC3); // We wanted to send a logging packet, but we haven't connected to LSP! diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 46f91857..afcad4f8 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -65,6 +65,7 @@ namespace Game NET_StringToAdr_t NET_StringToAdr = (NET_StringToAdr_t)0x409010; Live_MPAcceptInvite_t Live_MPAcceptInvite = (Live_MPAcceptInvite_t)0x420A6D; + Live_ParsePlaylists_t Live_ParsePlaylists = (Live_ParsePlaylists_t)0x4295A0; LoadInitialFF_t LoadInitialFF = (LoadInitialFF_t)0x506AC0; LoadModdableRawfile_t LoadModdableRawfile = (LoadModdableRawfile_t)0x61ABC0; diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index c98a8bb7..eb9216cf 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -151,6 +151,9 @@ namespace Game typedef void(__cdecl * Live_MPAcceptInvite_t)(_XSESSION_INFO *hostInfo, const int controllerIndex, bool fromGameInvite); extern Live_MPAcceptInvite_t Live_MPAcceptInvite; + typedef void(__cdecl * Live_ParsePlaylists_t)(const char* data); + extern Live_ParsePlaylists_t Live_ParsePlaylists; + typedef void(*LoadInitialFF_t)(void); extern LoadInitialFF_t LoadInitialFF; diff --git a/src/Utils/Utils.cpp b/src/Utils/Utils.cpp index dcbccdcb..2744b927 100644 --- a/src/Utils/Utils.cpp +++ b/src/Utils/Utils.cpp @@ -1,7 +1,7 @@ #include "..\STDInclude.hpp" #define VA_BUFFER_COUNT 4 -#define VA_BUFFER_SIZE 4096 +#define VA_BUFFER_SIZE 65536 namespace Utils { @@ -13,7 +13,7 @@ namespace Utils va_list ap; va_start(ap, fmt); char* dest = g_vaBuffer[g_vaNextBufferIndex]; - vsprintf_s(g_vaBuffer[g_vaNextBufferIndex], fmt, ap); + vsnprintf(g_vaBuffer[g_vaNextBufferIndex], VA_BUFFER_SIZE, fmt, ap); g_vaNextBufferIndex = (g_vaNextBufferIndex + 1) % VA_BUFFER_COUNT; va_end(ap); return dest;