Você está na página 1de 10

/* RGC PROTOCOL DOCUMENTATION 0xc1 :: RGC_GAME_ABORTED (e.g. !unhost, ctrl+c) 0xc2 :: RGC_GAME_STARTED (e.g. !start) 0xc3 :: RGC_GAME_ENDED (e.g.

stats sent (not implemented yet)) 0xc4 :: RGC_GAME_REFRESH (e.g. !open, !close, player joining/leaving) */ /* Please note that your 'ghost.cfg' will need to have the following entry added : udp_ghlproxy = ghl1.proxy.rankedgaming.com # Or using a different DNS entry / IP as the value for udp_ghlproxy */ game_base.h :: class CBaseGame -----------------------------uint32_t m_LastPlayerLeaveTicks; cks when the most recent player left the game /*CND>*/ uint32_t m_RefreshSlots; many open slots at the last refresh packet void refreshSlots( ); /*<CND*/ double m_MinimumScore; // the minimum allowed score for matchmaking mode

// GetTi // Counting how

game_base.cpp :: refreshSlots() ------------------------------/*CND>*/ void CBaseGame :: refreshSlots( ) { if( m_RefreshSlots != GetSlotsOpen( ) ) { int slotsTotal = m_Slots.size( ); int currentPlayers = slotsTotal - GetSlotsOpen( ); //If the game is full, remove one player, to accomodate for poss ible reserved players. This is due to the WC3 restriction, not allowing the user to try and join the game if it's full. if( slotsTotal == currentPlayers ) currentPlayers--; //Send the packet m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_RGC _REFRESHGAME( m_GHost->m_LocalIP, currentPlayers, slotsTotal, m_HostPort ), true ); m_RefreshSlots = GetSlotsOpen( ); } } /*<CND*/ game_base.cpp :: Update() ------------------------if( GetTime( ) - m_LastPingTime >= 5 ) { // note: we must send pings to players who are downloading the m ap because Warcraft III disconnects from the lobby if it doesn't receive a ping

every ~90 seconds // so if the player takes longer than 90 seconds to download the map they would be disconnected unless we keep sending pings // todotodo: ignore pings received from players who have recentl y finished downloading the map SendAll( m_Protocol->SEND_W3GS_PING_FROM_HOST( ) ); // we also broadcast the game to the local network every 5 secon ds so we hijack this timer for our nefarious purposes // however we only want to broadcast if the countdown hasn't sta rted // see the !sendlan code later in this file for some more inform ation about how this works // todotodo: should we send a game cancel message somewhere? we' ll need to implement a host counter for it to work if( !m_CountDownStarted ) { // construct a fixed host counter which will be used to identify players from this "realm" (i.e. LAN) // the fixed host counter's 4 most significant bits will contain a 4 bit ID (0-15) // the rest of the fixed host counter will contain the 2 8 least significant bits of the actual host counter // since we're destroying 4 bits of information here the actual host counter should not be greater than 2^28 which is a reasonable assum ption // when a player joins a game we can obtain the ID from the received host counter // note: LAN broadcasts use an ID of 0, battle.net refre shes use an ID of 1-10, the rest are unused uint32_t FixedHostCounter = m_HostCounter & 0x0FFFFFFF; if( m_SaveGame ) { // note: the PrivateGame flag is not set when br oadcasting to LAN (as you might expect) uint32_t MapGameType = MAPGAMETYPE_SAVEDGAME; BYTEARRAY MapWidth; MapWidth.push_back( 0 ); MapWidth.push_back( 0 ); BYTEARRAY MapHeight; MapHeight.push_back( 0 ); MapHeight.push_back( 0 ); /*CND>*/ int slotsTotal = m_Slots.size(); int slotsOpen = GetSlotsOpen() + 1; m_GHost->m_UDPSocket->Broadcast( 6112, m_Protoco l->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByt eArray( MapGameType, false ), m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_ GameName, "RGC", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame ->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), slotsTotal, slotsOpen, m_ HostPort, FixedHostCounter, m_GHost->m_LocalIP ), true ); /*<CND*/ } else

{ // note: the PrivateGame flag is not set when br oadcasting to LAN (as you might expect) // note: we do not use m_Map->GetMapGameType bec ause none of the filters are set when broadcasting to LAN (also as you might exp ect) uint32_t MapGameType = MAPGAMETYPE_UNKNOWN0; /*CND>*/ int slotsTotal = m_Slots.size(); int slotsOpen = GetSlotsOpen() + 1; BYTEARRAY local_ip; bool proxy = m_GameName != " GHost++ Admin Game"; if(proxy) local_ip = m_GHost->m_LocalIP; m_GHost->m_UDPSocket->Broadcast( 6112, m_Protoco l->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByt eArray( MapGameType, false ), m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "RGC", GetTime( ) - m_CreationTime, m_Map->G etMapPath( ), m_Map->GetMapCRC( ), slotsTotal, slotsOpen, m_HostPort, FixedHostC ounter, local_ip ), proxy ); /*<CND*/ } } m_LastPingTime = GetTime( ); } /*CND>*/ refreshSlots( ); /*<CND*/ // auto rehost if there was a refresh error in autohosted games game_base.cpp :: Update() ------------------------// create the virtual host player /*CND>*/ if( !m_GameLoading && !m_GameLoaded && GetNumPlayers( ) < m_Slots.size() ) CreateVirtualHost( ); /*<CND*/ // unlock the game

game_base.cpp :: EventPlayerJoined() -----------------------------------// we have a slot for the new player // make room for them by deleting the virtual host player if we have to /*CND>*/ if( GetNumPlayers( ) >= m_Slots.size() - 1 || EnforcePID == m_VirtualHos tPID ) DeleteVirtualHost( ); /*<CND*/

// turning the CPotentialPlayer into cause we have to be careful not to close the // this problem is solved by setting and handling the NULL case in the destructor // we also have to be careful to not e we're currently looping through it

a CGamePlayer is a bit of a pain be socket the socket to NULL before deletion modify the m_Potentials vector sinc

game_base.cpp :: EventPlayerJoinedWithScore() ---------------------------------------------// we have a slot for the new player // make room for them by deleting the virtual host player if we have to /*CND>*/ if( GetNumPlayers( ) >= m_Slots.size() - 1 ) DeleteVirtualHost( ); /*<CND*/ // identify their joined realm // this is only possible because when we send a game refresh via LAN or battle.net we encode an ID value in the 4 most significant bits of the host coun ter // the client sends the host counter when it joins so we can extract the ID value here // note: this is not a replacement for spoof checking since it doesn't v erify the player's name and it can be spoofed anyway game_base.cpp :: CreateFakePlayer() ----------------------------------if( SID < m_Slots.size( ) ) { /*CND>*/ if( GetNumPlayers( ) >= m_Slots.size() - 1 ) DeleteVirtualHost( ); /*<CND*/ m_FakePlayerPID = GetNewPID( ); BYTEARRAY IP; IP.push_back( 0 ); IP.push_back( 0 ); IP.push_back( 0 ); IP.push_back( 0 ); SendAll( m_Protocol->SEND_W3GS_PLAYERINFO( m_FakePlayerPID, "Fak ePlayer", IP, IP ) ); m_Slots[SID] = CGameSlot( m_FakePlayerPID, 100, SLOTSTATUS_OCCUP IED, 0, m_Slots[SID].GetTeam( ), m_Slots[SID].GetColour( ), m_Slots[SID].GetRace ( ) ); SendAllSlotInfo( ); } game_base.cpp :: EventGameStarted() ----------------------------------void CBaseGame :: EventGameStarted( ) {

/*CND>*/ m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_RGC( m_GHos t->m_LocalIP, 0xc2, m_HostPort ), true ); /*<CND*/ game_base.cpp :: ~CBaseGame() ----------------------------CBaseGame :: ~CBaseGame( ) { // save replay // todotodo: put this in a thread /*CND>*/ if( !m_GameLoading && !m_GameLoaded ) m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_RGC ( m_GHost->m_LocalIP, 0xc1, m_HostPort ), true ); /*<CND*/ if( m_Replay && ( m_GameLoading || m_GameLoaded ) ) { gameprotocol.h :: (anywhere) --------------------------/*CND>*/ BYTEARRAY SEND_W3GS_RGC( BYTEARRAY local_ip, uint32_t header, uint32_t p ort ); BYTEARRAY SEND_W3GS_RGC_REFRESHGAME( BYTEARRAY local_ip, uint32_t player s, uint32_t playerSlots, uint32_t port ); /*<CND*/ gameprotocol.cpp :: (anywhere) -----------------------------/*CND>*/ BYTEARRAY CGameProtocol :: SEND_W3GS_RGC( BYTEARRAY local_ip, uint32_t header, u int32_t port ) { BYTEARRAY packet; packet.push_back( W3GS_HEADER_CONSTANT ); // W3GS header constant packet.push_back( header ); // W3GS_Action UTIL_AppendByteArray( packet, port, 4 ); // Port packet.push_back( 0 ); packet.push_back( 0 ); AssignLength( packet ); UTIL_AppendByteArray( packet, local_ip ); // DEBUG_Print( "SENT W3GS_RGC" ); // DEBUG_Print( packet ); return packet; } BYTEARRAY CGameProtocol :: SEND_W3GS_RGC_REFRESHGAME( BYTEARRAY local_ip, uint32 _t players, uint32_t playerSlots, uint32_t port ) { BYTEARRAY packet; packet.push_back( W3GS_HEADER_CONSTANT ); // W3GS header constant

packet.push_back( 0xC4 ); // W3GS_RGC_REFRESHGAME UTIL_AppendByteArray( packet, port, 4 ); packet.push_back( 0 ); // packet length will be assigned later packet.push_back( 0 ); // packet length will be assigned later UTIL_AppendByteArray( packet, players, false ); UTIL_AppendByteArray( packet, playerSlots, false ); AssignLength( packet ); UTIL_AppendByteArray( packet, local_ip ); // DEBUG_Print( "SENT W3GS_RGC_REFRESHGAME" ); // DEBUG_Print( packet ); return packet; } /*<CND*/

// Port

// Players // Player Slots

gameprotocol.h :: (anywhere) --------------------------/*CND>*/ // BYTEARRAY SEND_W3GS_GAMEINFO( bool TFT, unsigned char war3Version, BY TEARRAY mapGameType, BYTEARRAY mapFlags, BYTEARRAY mapWidth, BYTEARRAY mapHeight , string gameName, string hostName, uint32_t upTime, string mapPath, BYTEARRAY m apCRC, uint32_t slotsTotal, uint32_t slotsOpen, uint16_t port, uint32_t hostCoun ter ); /*CND>*/ BYTEARRAY SEND_W3GS_GAMEINFO( bool TFT, unsigned char war3Version, BYTEA RRAY mapGameType, BYTEARRAY mapFlags, BYTEARRAY mapWidth, BYTEARRAY mapHeight, s tring gameName, string hostName, uint32_t upTime, string mapPath, BYTEARRAY mapC RC, uint32_t slotsTotal, uint32_t slotsOpen, uint16_t port, uint32_t hostCounter , BYTEARRAY local_ip ); /*<CND*/ /*<CND*/ gameprotocol.cpp :: SEND_W3GS_GAMEINFO() ---------------------------------------/*CND>*/ BYTEARRAY CGameProtocol :: SEND_W3GS_GAMEINFO( bool TFT, unsigned char war3Versi on, BYTEARRAY mapGameType, BYTEARRAY mapFlags, BYTEARRAY mapWidth, BYTEARRAY map Height, string gameName, string hostName, uint32_t upTime, string mapPath, BYTEA RRAY mapCRC, uint32_t slotsTotal, uint32_t slotsOpen, uint16_t port, uint32_t ho stCounter, BYTEARRAY local_ip ) /*<CND*/ { unsigned char ProductID_ROC[] = { 51, 82, 65, 87 }; // "WAR3 " unsigned char ProductID_TFT[] = { 80, 88, 51, 87 }; // "W3XP " unsigned char Version[] = { war3Version, 0, 0, 0 }; unsigned char Unknown1[] = { 1, 2, 3, 4 }; unsigned char Unknown2[] = { 1, 0, 0, 0 }; BYTEARRAY packet; if( mapGameType.size( ) == 4 && mapFlags.size( ) == 4 && mapWidth.size( ) == 2 && mapHeight.size( ) == 2 && !gameName.empty( ) && !hostName.empty( ) && !mapPath.empty( ) && mapCRC.size( ) == 4 ) {

// make the stat string BYTEARRAY StatString; UTIL_AppendByteArrayFast( StatString, mapFlags ); StatString.push_back( 0 ); UTIL_AppendByteArrayFast( StatString, mapWidth ); UTIL_AppendByteArrayFast( StatString, mapHeight ); UTIL_AppendByteArrayFast( StatString, mapCRC ); UTIL_AppendByteArrayFast( StatString, mapPath ); UTIL_AppendByteArrayFast( StatString, hostName ); StatString.push_back( 0 ); StatString = UTIL_EncodeStatString( StatString ); // make the rest of the packet packet.push_back( // W3GS header constant packet.push_back( // W3GS_GAMEINFO packet.push_back( // packet length will be assigned packet.push_back( // packet length will be assigned W3GS_HEADER_CONSTANT ); W3GS_GAMEINFO ); 0 ); later 0 ); later

if( TFT ) UTIL_AppendByteArray( packet, ProductID_TFT, 4 ); // Product ID (TFT) else UTIL_AppendByteArray( packet, ProductID_ROC, 4 ); // Product ID (ROC) UTIL_AppendByteArray( packet, Version, 4 ); // Version UTIL_AppendByteArray( packet, hostCounter, false ); // Host Counter UTIL_AppendByteArray( packet, Unknown1, 4 ); // ??? (this varies wildly even between two identical games created one after an other) UTIL_AppendByteArrayFast( packet, gameName ); // Game Name packet.push_back( 0 ); // ??? (maybe game password) UTIL_AppendByteArrayFast( packet, StatString ); // Stat String packet.push_back( 0 ); // Stat String null terminator (the stat string is encoded to remove all even nu mbers i.e. zeros) UTIL_AppendByteArray( packet, slotsTotal, false ); // Slots Total UTIL_AppendByteArrayFast( packet, mapGameType ); // Game Type UTIL_AppendByteArray( packet, Unknown2, 4 ); // ??? UTIL_AppendByteArray( packet, slotsOpen, false ); // Slots Open UTIL_AppendByteArray( packet, upTime, false ); // time since creation UTIL_AppendByteArray( packet, port, false ); // port AssignLength( packet );

/*CND>*/ UTIL_AppendByteArray( packet, local_ip ); /*<CND*/ } else CONSOLE_Print( "[GAMEPROTO] invalid parameters passed to SEND_W3 GS_GAMEINFO" ); // DEBUG_Print( "SENT W3GS_GAMEINFO" ); // DEBUG_Print( packet ); return packet; }

/* LATEST CHANGES !!! */ /* This is a protocol extension to support playerhosting and multiple users in L AN over one remote IP */ ghost.h :: (anywhere) --------------------/*CND>*/ BYTEARRAY m_LocalIP; /*<CND*/ ghost.cpp :: CGhost ------------------CGHost :: CGHost( CConfig *CFG ) { m_UDPSocket = new CUDPSocket( ); m_UDPSocket->SetBroadcastTarget( CFG->GetString( "udp_broadcasttarget", string( ) ) ); m_UDPSocket->SetDontRoute( CFG->GetInt( "udp_dontroute", 0 ) == 0 ? fals e : true ); /*CND>*/ m_UDPSocket->SetGHLProxy( CFG->GetString( "udp_ghlproxy", string( ) ) ); /*<CDN*/ m_ReconnectSocket = NULL; ghost.cpp :: CGhost ------------------/*CND>*/ for(vector<BYTEARRAY> :: iterator i = m_LocalAddresses.begin(); i != m_L ocalAddresses.end(); i++) { if( (*i)[0] == 127 && (*i)[1] == 0 && (*i)[2] == 0 && (*i)[3] == 1 ) continue; m_LocalIP = *i; break; } /*<CND*/ m_Language = NULL; socket.h :: CUDPSocket

---------------------class CUDPSocket : public CSocket { protected: struct in_addr m_BroadcastTarget; /*CND>*/ string m_ProxyDNS; /*<CND*/ public: CUDPSocket( ); virtual ~CUDPSocket( ); virtual bool SendTo( struct sockaddr_in sin, BYTEARRAY message ); virtual bool SendTo( string address, uint16_t port, BYTEARRAY message ); /*CND>*/ virtual bool Broadcast( uint16_t port, BYTEARRAY message, bool proxy = t rue ); /*<CND*/ virtual void SetBroadcastTarget( string subnet ); virtual void SetDontRoute( bool dontRoute ); /*CND>*/ virtual void SetGHLProxy( string dns ) { m_ProxyDNS = dns; } /*<CND*/ }; socket.cpp :: Broadcast() ------------------------/*CND>*/ bool CUDPSocket :: Broadcast( uint16_t port, BYTEARRAY message, bool proxy ) /*<CND*/ { if( m_Socket == INVALID_SOCKET || m_HasError ) return false; struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = m_BroadcastTarget.s_addr; sin.sin_port = htons( port ); string MessageString = string( message.begin( ), message.end( ) ); /*CND>*/ if( proxy && !m_ProxyDNS.empty( ) ) return SendTo( m_ProxyDNS, port, message ) ; /*<CND*/ if( sendto( m_Socket, MessageString.c_str( ), MessageString.size( ), 0, (struct sockaddr *)&sin, sizeof( sin ) ) == -1 ) { CONSOLE_Print( "[UDPSOCKET] failed to broadcast packet (port " + UTIL_ToString( port ) + ", size " + UTIL_ToString( MessageString.size( ) ) + " bytes)" ); return false; } return true;

} //Addtions for !open/!close commands bnet.cpp :: ProcessChatEvent() ------------------------// // !OPENALL // if( Command == "openall" && m_GHost->m_CurrentGa me ) { if( !m_GHost->m_CurrentGame->GetLocked( ) ) m_GHost->m_CurrentGame->OpenAllS lots( ); else QueueChatCommand( m_GHost->m_Lan guage->TheGameIsLockedBNET( ), User, Whisper ); } /*CND>*/ //Refresh RGC slots on any command that modifies the current/total slot information. I could only think of the following four. //If I missed any commands, please let me know a nd I shall make an entry in the holy grail about them. if( m_GHost->m_CurrentGame && ( Command == "clos e" || Command == "closeall" || Command == "open" || Command == "openall" ) ) m_GHost->m_CurrentGame->refreshSlots( ); /*<CND*/

game.cpp :: CGame :: EventPlayerBotCommand() -----------------------------// // !OPENALL // if( Command == "openall" && !m_GameLoading && !m_GameLoa ded ) OpenAllSlots( ); /*CND>*/ //Refresh RGC slots on any command that modifies the cur rent/total slot information. I could only think of the following four. //If I missed any commands, please let me know and I sha ll make an entry in the holy grail about them. if( !m_GameLoading && !m_GameLoaded && ( Command == "clo se" || Command == "closeall" || Command == "open" || Command == "openall" ) ) refreshSlots( ); /*<CND*/

Você também pode gostar