iw4x-client/src/Components/Modules/Party.cpp

387 lines
11 KiB
C++
Raw Normal View History

#include "STDInclude.hpp"
2015-12-25 15:42:35 -05:00
namespace Components
{
Party::JoinContainer Party::Container;
2015-12-26 21:56:00 -05:00
std::map<uint64_t, Network::Address> Party::LobbyMap;
SteamID Party::GenerateLobbyId()
{
SteamID id;
2015-12-27 08:05:08 -05:00
id.AccountID = Game::Com_Milliseconds();
id.Universe = 1;
id.AccountType = 8;
id.AccountInstance = 0x40000;
2015-12-26 21:56:00 -05:00
return id;
}
2015-12-25 15:42:35 -05:00
2016-01-01 20:28:09 -05:00
Network::Address Party::Target()
{
return Party::Container.Target;
}
2015-12-25 15:42:35 -05:00
void Party::Connect(Network::Address target)
{
Party::Container.Valid = true;
2016-01-01 20:28:09 -05:00
Party::Container.AwaitingPlaylist = false;
2015-12-25 15:42:35 -05:00
Party::Container.JoinTime = Game::Com_Milliseconds();
Party::Container.Target = target;
2016-02-08 08:27:15 -05:00
Party::Container.Challenge = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
2015-12-25 15:42:35 -05:00
2015-12-27 22:02:30 -05:00
Network::Send(Party::Container.Target, Utils::VA("getinfo %s\n", Party::Container.Challenge.data()));
2015-12-25 15:42:35 -05:00
Command::Execute("openmenu popup_reconnectingtoparty");
}
2015-12-26 21:56:00 -05:00
const char* Party::GetLobbyInfo(SteamID lobby, std::string key)
{
2015-12-27 08:05:08 -05:00
if (Party::LobbyMap.find(lobby.Bits) != Party::LobbyMap.end())
2015-12-26 21:56:00 -05:00
{
2015-12-27 08:05:08 -05:00
Network::Address address = Party::LobbyMap[lobby.Bits];
2015-12-26 21:56:00 -05:00
if (key == "addr")
{
2016-01-03 18:00:07 -05:00
return Utils::VA("%d", address.GetIP().full);
2015-12-26 21:56:00 -05:00
}
else if (key =="port")
{
2016-01-03 18:00:07 -05:00
return Utils::VA("%d", address.GetPort());
2015-12-26 21:56:00 -05:00
}
}
return "212";
}
void Party::RemoveLobby(SteamID lobby)
{
2015-12-27 08:05:08 -05:00
if (Party::LobbyMap.find(lobby.Bits) != Party::LobbyMap.end())
2015-12-26 21:56:00 -05:00
{
2015-12-27 08:05:08 -05:00
Party::LobbyMap.erase(Party::LobbyMap.find(lobby.Bits));
2015-12-26 21:56:00 -05:00
}
}
2015-12-27 09:39:49 -05:00
void Party::ConnectError(std::string message)
{
Command::Execute("closemenu popup_reconnectingtoparty");
Dvar::Var("partyend_reason").Set(message);
Command::Execute("openmenu menu_xboxlive_partyended");
}
Game::dvar_t* Party::RegisterMinPlayers(const char* name, int value, int min, int max, Game::dvar_flag flag, const char* description)
{
return Dvar::Register<int>(name, 1, 1, max, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED | flag, description).Get<Game::dvar_t*>();
}
2016-01-01 20:28:09 -05:00
bool Party::PlaylistAwaiting()
{
return Party::Container.AwaitingPlaylist;
}
void Party::PlaylistContinue()
{
2016-01-08 08:24:05 -05:00
Dvar::Var("xblive_privateserver").Set(false);
2016-01-02 12:36:01 -05:00
// Ensure we can join
*Game::g_lobbyCreateInProgress = false;
2016-01-01 20:28:09 -05:00
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);
}
void Party::PlaylistError(std::string error)
{
Party::Container.Valid = false;
Party::Container.AwaitingPlaylist = false;
Party::ConnectError(error);
}
2016-01-02 20:37:06 -05:00
DWORD Party::UIDvarIntStub(char* dvar)
{
if (!_stricmp(dvar, "onlinegame"))
{
return 0x649E660;
}
return Utils::Hook::Call<DWORD(char*)>(0x4D5390)(dvar);
}
2015-12-25 15:42:35 -05:00
Party::Party()
{
2016-01-17 06:31:56 -05:00
static Game::dvar_t* partyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsDedicated(), Game::dvar_flag::DVAR_FLAG_NONE, "Enable party system").Get<Game::dvar_t*>();
2016-01-02 20:37:06 -05:00
Dvar::Register<bool>("xblive_privatematch", true, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED, "").Get<Game::dvar_t*>();
2015-12-25 15:42:35 -05:00
// various changes to SV_DirectConnect-y stuff to allow non-party joinees
Utils::Hook::Set<WORD>(0x460D96, 0x90E9);
Utils::Hook::Set<BYTE>(0x460F0A, 0xEB);
Utils::Hook::Set<BYTE>(0x401CA4, 0xEB);
Utils::Hook::Set<BYTE>(0x401C15, 0xEB);
// disable configstring checksum matching (it's unreliable at most)
Utils::Hook::Set<BYTE>(0x4A75A7, 0xEB); // SV_SpawnServer
Utils::Hook::Set<BYTE>(0x5AC2CF, 0xEB); // CL_ParseGamestate
Utils::Hook::Set<BYTE>(0x5AC2C3, 0xEB); // CL_ParseGamestate
2015-12-26 21:56:00 -05:00
// AnonymousAddRequest
Utils::Hook::Set<BYTE>(0x5B5E18, 0xEB);
Utils::Hook::Set<BYTE>(0x5B5E64, 0xEB);
Utils::Hook::Nop(0x5B5E5C, 2);
// HandleClientHandshake
Utils::Hook::Set<BYTE>(0x5B6EA5, 0xEB);
Utils::Hook::Set<BYTE>(0x5B6EF3, 0xEB);
Utils::Hook::Nop(0x5B6EEB, 2);
// Allow local connections
Utils::Hook::Set<BYTE>(0x4D43DA, 0xEB);
// LobbyID mismatch
Utils::Hook::Nop(0x4E50D6, 2);
Utils::Hook::Set<BYTE>(0x4E50DA, 0xEB);
// causes 'does current Steam lobby match' calls in Steam_JoinLobby to be ignored
Utils::Hook::Set<BYTE>(0x49D007, 0xEB);
// functions checking party heartbeat timeouts, cause random issues
Utils::Hook::Nop(0x4E532D, 5);
// Steam_JoinLobby call causes migration
Utils::Hook::Nop(0x5AF851, 5);
Utils::Hook::Set<BYTE>(0x5AF85B, 0xEB);
2015-12-27 09:39:49 -05:00
// Allow xpartygo in public lobbies
Utils::Hook::Set<BYTE>(0x5A969E, 0xEB);
2016-01-02 23:01:55 -05:00
Utils::Hook::Nop(0x5A96BE, 2);
2015-12-25 15:42:35 -05:00
2016-01-02 20:37:06 -05:00
// Always open lobby menu when connecting
// It's not possible to entirely patch it via code
//Utils::Hook::Set<BYTE>(0x5B1698, 0xEB);
//Utils::Hook::Nop(0x5029F2, 6);
//Utils::Hook::SetString(0x70573C, "menu_xboxlive_lobby");
// Disallow selecting team in private match
//Utils::Hook::Nop(0x5B2BD8, 6);
// Force teams, even if not private match
Utils::Hook::Set<BYTE>(0x487BB2, 0xEB);
// Force xblive_privatematch 0 and rename it
2016-01-02 23:01:55 -05:00
//Utils::Hook::Set<BYTE>(0x420A6A, 4);
2016-01-02 20:37:06 -05:00
Utils::Hook::Set<BYTE>(0x420A6C, 0);
2016-01-02 23:01:55 -05:00
Utils::Hook::Set<char*>(0x420A6E, "xblive_privateserver");
2016-01-02 20:37:06 -05:00
2016-01-26 16:56:28 -05:00
// Remove migration shutdown, it causes crashes and will be destroyed when erroring anyways
Utils::Hook::Nop(0x5A8E1C, 12);
Utils::Hook::Nop(0x5A8E33, 11);
2016-01-02 20:37:06 -05:00
// Enable XP Bar
Utils::Hook(0x62A2A7, Party::UIDvarIntStub, HOOK_CALL).Install()->Quick();
2016-01-02 22:13:35 -05:00
// Set NAT to open
Utils::Hook::Set<int>(0x79D898, 1);
// Disable host migration
Utils::Hook::Set<BYTE>(0x5B58B2, 0xEB);
2016-01-08 08:24:05 -05:00
// Patch playlist stuff for non-party behavior
Utils::Hook::Set<Game::dvar_t**>(0x4A4093, &partyEnable);
Utils::Hook::Set<Game::dvar_t**>(0x4573F1, &partyEnable);
Utils::Hook::Set<Game::dvar_t**>(0x5B1A0C, &partyEnable);
// Invert corresponding jumps
Utils::Hook::Xor<BYTE>(0x4A409B, 1);
Utils::Hook::Xor<BYTE>(0x4573FA, 1);
Utils::Hook::Xor<BYTE>(0x5B1A17, 1);
2016-01-02 20:37:06 -05:00
// Fix xstartlobby
//Utils::Hook::Set<BYTE>(0x5B71CD, 0xEB);
2015-12-27 09:39:49 -05:00
// Patch party_minplayers to 1 and protect it
2015-12-29 22:19:52 -05:00
//Utils::Hook(0x4D5D51, Party::RegisterMinPlayers, HOOK_CALL).Install()->Quick();
2015-12-25 15:42:35 -05:00
2015-12-27 09:39:49 -05:00
Command::Add("connect", [] (Command::Params params)
2015-12-26 21:56:00 -05:00
{
if (params.Length() < 2)
{
return;
}
2015-12-27 09:39:49 -05:00
Party::Connect(Network::Address(params[1]));
2015-12-26 21:56:00 -05:00
});
2016-01-12 14:05:38 -05:00
Command::Add("reconnect", [] (Command::Params params)
{
Party::Connect(Party::Container.Target);
});
2015-12-26 21:56:00 -05:00
2015-12-25 15:42:35 -05:00
Renderer::OnFrame([] ()
{
2016-01-01 20:28:09 -05:00
if (Party::Container.Valid)
{
if ((Game::Com_Milliseconds() - Party::Container.JoinTime) > 5000)
{
Party::Container.Valid = false;
Party::ConnectError("Server connection timed out.");
}
}
if (Party::Container.AwaitingPlaylist)
2015-12-25 15:42:35 -05:00
{
2016-01-01 20:28:09 -05:00
if ((Game::Com_Milliseconds() - Party::Container.RequestTime) > 5000)
{
Party::Container.AwaitingPlaylist = false;
Party::ConnectError("Playlist request timed out.");
}
2015-12-25 15:42:35 -05:00
}
});
// Basic info handler
Network::Handle("getInfo", [] (Network::Address address, std::string data)
{
int clientCount = 0;
2015-12-30 09:37:53 -05:00
int maxclientCount = *Game::svs_numclients;
2015-12-25 15:42:35 -05:00
2016-01-02 14:30:19 -05:00
if (maxclientCount)
2015-12-25 15:42:35 -05:00
{
2016-01-24 13:58:13 -05:00
for (int i = 0; i < maxclientCount; ++i)
2015-12-25 15:42:35 -05:00
{
2016-01-02 14:30:19 -05:00
if (Game::svs_clients[i].state >= 3)
{
clientCount++;
}
2015-12-25 15:42:35 -05:00
}
}
2016-01-02 14:30:19 -05:00
else
{
//maxclientCount = Dvar::Var("sv_maxclients").Get<int>();
maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
clientCount = Game::PartyHost_CountMembers((Game::PartyData_s*)0x1081C00);
}
2015-12-25 15:42:35 -05:00
Utils::InfoString info;
2016-01-04 05:32:05 -05:00
info.Set("challenge", Utils::ParseChallenge(data));
2015-12-25 15:42:35 -05:00
info.Set("gamename", "IW4");
info.Set("hostname", Dvar::Var("sv_hostname").Get<const char*>());
info.Set("gametype", Dvar::Var("g_gametype").Get<const char*>());
info.Set("fs_game", Dvar::Var("fs_game").Get<const char*>());
2015-12-27 08:05:08 -05:00
info.Set("xuid", Utils::VA("%llX", Steam::SteamUser()->GetSteamID().Bits));
2015-12-25 15:42:35 -05:00
info.Set("clients", Utils::VA("%i", clientCount));
2015-12-30 09:37:53 -05:00
info.Set("sv_maxclients", Utils::VA("%i", maxclientCount));
2015-12-29 18:13:12 -05:00
info.Set("protocol", Utils::VA("%i", PROTOCOL));
2016-01-01 11:36:47 -05:00
info.Set("shortversion", VERSION_STR);
2015-12-29 18:13:12 -05:00
info.Set("checksum", Utils::VA("%d", Game::Com_Milliseconds()));
2016-01-01 11:36:47 -05:00
info.Set("mapname", Dvar::Var("mapname").Get<const char*>());
2016-01-04 12:28:11 -05:00
info.Set("isPrivate", (Dvar::Var("g_password").Get<std::string>().size() ? "1" : "0"));
info.Set("hc", (Dvar::Var("g_hardcore").Get<bool>() ? "1" : "0"));
2016-01-01 11:36:47 -05:00
// Ensure mapname is set
if (!info.Get("mapname").size())
{
info.Set("mapname", Dvar::Var("ui_mapname").Get<const char*>());
}
2015-12-25 15:42:35 -05:00
2015-12-27 09:39:49 -05:00
// Set matchtype
// 0 - No match, connecting not possible
// 1 - Party, use Steam_JoinLobby to connect
// 2 - Match, use CL_ConnectFromParty to connect
2016-01-02 20:37:06 -05:00
if (Dvar::Var("party_enable").Get<bool>() && Dvar::Var("party_host").Get<bool>()) // Party hosting
2015-12-27 09:39:49 -05:00
{
info.Set("matchtype", "1");
}
else if (Dvar::Var("sv_running").Get<bool>()) // Match hosting
{
info.Set("matchtype", "2");
}
else
{
info.Set("matchtype", "0");
}
2015-12-29 18:13:12 -05:00
Network::Send(address, Utils::VA("infoResponse\n\\%s\n", info.Build().data()));
2015-12-25 15:42:35 -05:00
});
Network::Handle("infoResponse", [] (Network::Address address, std::string data)
{
2015-12-27 22:02:30 -05:00
Utils::InfoString info(data);
2015-12-25 15:42:35 -05:00
// Handle connection
if (Party::Container.Valid)
{
if (Party::Container.Target == address)
{
// Invalidate handler for future packets
Party::Container.Valid = false;
2015-12-27 09:39:49 -05:00
int matchType = atoi(info.Get("matchtype").data());
2015-12-25 15:42:35 -05:00
if (info.Get("challenge") != Party::Container.Challenge)
{
2015-12-27 09:39:49 -05:00
Party::ConnectError("Invalid join response: Challenge mismatch.");
2015-12-25 15:42:35 -05:00
}
2015-12-27 09:39:49 -05:00
else if (!matchType)
2015-12-25 15:42:35 -05:00
{
2015-12-27 09:39:49 -05:00
Party::ConnectError("Server is not hosting a match.");
2015-12-25 15:42:35 -05:00
}
2015-12-27 09:39:49 -05:00
// Connect
else if (matchType == 1) // Party
2015-12-25 15:42:35 -05:00
{
2016-01-01 20:28:09 -05:00
// Send playlist request
Party::Container.RequestTime = Game::Com_Milliseconds();
Party::Container.AwaitingPlaylist = true;
Network::Send(address, "getplaylist\n");
// This is not a safe method
// TODO: Fix actual error!
if (Game::CL_IsCgameInitialized())
{
Command::Execute("disconnect", true);
}
2015-12-27 09:39:49 -05:00
}
else if (matchType == 2) // Match
{
if (atoi(info.Get("clients").data()) >= atoi(info.Get("sv_maxclients").data()))
{
Party::ConnectError("@EXE_SERVERISFULL");
}
if (info.Get("mapname") == "" || info.Get("gametype") == "")
{
Party::ConnectError("Invalid map or gametype.");
}
else
{
2016-01-08 08:24:05 -05:00
Dvar::Var("xblive_privateserver").Set(true);
2015-12-27 09:39:49 -05:00
Game::Menus_CloseAll(Game::uiContext);
char xnaddr[32];
Game::CL_ConnectFromParty(0, xnaddr, *address.Get(), 0, 0, info.Get("mapname").data(), info.Get("gametype").data());
}
}
else
{
2015-12-28 08:08:46 -05:00
Party::ConnectError("Invalid join response: Unknown matchtype");
2015-12-25 15:42:35 -05:00
}
}
}
2015-12-27 22:02:30 -05:00
ServerList::Insert(address, info);
2015-12-25 15:42:35 -05:00
});
}
Party::~Party()
{
2015-12-26 21:56:00 -05:00
Party::LobbyMap.clear();
2015-12-25 15:42:35 -05:00
}
}