2016-01-04 14:58:00 -05:00
# 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
2016-02-10 11:18:45 -05:00
Network : : SendCommand ( Party : : Container . Target , " getinfo " , Party : : Container . Challenge ) ;
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 ) ;
}
2016-01-03 09:14:44 -05:00
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 " ) ;
}
2016-02-10 11:18:45 -05:00
Network : : SendCommand ( address , " infoResponse " , " \\ " + info . Build ( ) ) ;
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 ;
2016-02-10 11:18:45 -05:00
Network : : SendCommand ( address , " getplaylist " ) ;
2016-01-03 09:14:44 -05:00
// 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
}
}