Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Core/GameEngine/Include/GameNetwork/GameInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ enum
class GameSlot
{
public:
struct ProductInfo
{
enum CPP_11(: UnsignedInt)
Comment thread
Caball009 marked this conversation as resolved.
Outdated
{
NO_RETAIL = 1 << 0,
SHELLMAP_ENABLED = 1 << 1,
ZERO_MAPS_STARTED = 1 << 2
Comment thread
Caball009 marked this conversation as resolved.
Outdated
};

UnsignedInt flags;
UnsignedInt uptime;
UnsignedInt exeCRC;
UnsignedInt iniCRC;
UnsignedInt fpMathCRC;
UnicodeString productTitle;
UnicodeString productVersion;
UnicodeString productAuthor;
UnicodeString gitShortHash;
};

GameSlot();
virtual void reset();

Expand Down Expand Up @@ -125,6 +145,9 @@ class GameSlot

void mute( Bool isMuted ) { m_isMuted = isMuted; }
Bool isMuted( void ) const { return m_isMuted; }

void setProductInfo(const ProductInfo& productInfo) { m_productInfo = productInfo; }
const ProductInfo& getProductInfo() const { return m_productInfo; }
protected:
Comment thread
Caball009 marked this conversation as resolved.
SlotState m_state;
Bool m_isAccepted;
Expand All @@ -143,6 +166,7 @@ class GameSlot
FirewallHelperClass::FirewallBehaviorType m_NATBehavior; ///< The NAT behavior for this slot's player.
UnsignedInt m_lastFrameInGame; // only valid for human players
Bool m_disconnected; // only valid for human players
ProductInfo m_productInfo; ///< Community made product information
};

/**
Expand Down
43 changes: 43 additions & 0 deletions Core/GameEngine/Include/GameNetwork/LANAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,25 @@ struct LANMessage
MSG_INACTIVE, ///< I've alt-tabbed out. Unaccept me cause I'm a poo-flinging monkey.

MSG_REQUEST_GAME_INFO, ///< For direct connect, get the game info from a specific IP Address

// TheSuperHackers @feature Caball009 05/02/2026 Product information is exchanged on demand and never broadcast.
// A client is considered 'patched' if it responds to a product info request (or, in pre-match, if it sends one).
// The implementation consists of three parts.
// 1. player - player in lobby:
// - When a player detects a new player in the lobby, it sends a product info request.
Comment thread
Caball009 marked this conversation as resolved.
Outdated
// - If the other player responds with an acknowledgement, they are considered patched.
// 2. player - host in lobby:
// - When a player detects a new game host in the lobby, it sends a product info request.
// - If the host responds with an acknowledgement, it is considered patched.
// 3. players in pre-match:
// - When a player joins a match, it sends a product info request to all existing players.
// - Existing players treat this request as confirmation that the joining player is patched (no explicit acknowledgement required).
MSG_GAME_REQUEST_PRODUCT_INFO = 1000,
MSG_GAME_RESPONSE_PRODUCT_INFO,
MSG_LOBBY_REQUEST_PRODUCT_INFO,
MSG_LOBBY_RESPONSE_PRODUCT_INFO,
MSG_MATCH_REQUEST_PRODUCT_INFO,
MSG_MATCH_RESPONSE_PRODUCT_INFO,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in the other file, perhaps a better terminology for these are:

GAME to LOBBY_ROOM aka a Game Room in the Lobby
LOBBY to LOBBY_PLAYER aka a Player in the Lobby
MATCH to ROOM_PLAYER aka a Player in the Game Room

} messageType;

WideChar name[g_lanPlayerNameLength+1]; ///< My name, for convenience
Expand Down Expand Up @@ -267,6 +286,17 @@ struct LANMessage
char options[m_lanMaxOptionsLength+1];
} GameOptions;

// ProductInfo is sent with REQUEST_PRODUCT_INFO and RESPONSE_PRODUCT_INFO
Comment thread
xezon marked this conversation as resolved.
struct
{
UnsignedInt flags;
UnsignedInt uptime;
UnsignedInt exeCRC;
UnsignedInt iniCRC;
UnsignedInt fpMathCRC;
WideChar data[201];
Comment thread
xezon marked this conversation as resolved.
Byte padding[20];
Comment thread
Caball009 marked this conversation as resolved.
Outdated
} ProductInfo;
Comment thread
Caball009 marked this conversation as resolved.
Comment thread
xezon marked this conversation as resolved.
};
};
Comment thread
Caball009 marked this conversation as resolved.
#pragma pack(pop)
Expand Down Expand Up @@ -394,6 +424,12 @@ class LANAPI : public LANAPIInterface
void addGame(LANGameInfo *game);
AsciiString createSlotString( void );

static UnsignedInt getProductInfoFlags();
static void setProductInfoFromLocalData(GameSlot *slot);
static void setProductInfoFromMessage(LANMessage *msg, GameSlot *slot);
static Bool setProductInfoStrings(const UnicodeString(&input)[4], WideChar(&output)[201]);
static Bool getProductInfoStrings(WideChar(&input)[201], UnicodeString*(&output)[4]);

// Functions to handle incoming messages -----------------------------------
void handleRequestLocations( LANMessage *msg, UnsignedInt senderIP );
void handleGameAnnounce( LANMessage *msg, UnsignedInt senderIP );
Expand All @@ -412,4 +448,11 @@ class LANAPI : public LANAPIInterface
void handleGameOptions( LANMessage *msg, UnsignedInt senderIP );
void handleInActive( LANMessage *msg, UnsignedInt senderIP );

void sendProductInfoMessage(LANMessage::Type messageType, UnsignedInt senderIP);
void handleGameProductInfoRequest(LANMessage *msg, UnsignedInt senderIP);
void handleGameProductInfoResponse(LANMessage *msg, UnsignedInt senderIP);
void handleLobbyProductInfoRequest(LANMessage *msg, UnsignedInt senderIP);
void handleLobbyProductInfoResponse(LANMessage *msg, UnsignedInt senderIP);
void handleMatchProductInfoRequest(LANMessage *msg, UnsignedInt senderIP);
void handleMatchProductInfoResponse(LANMessage *msg, UnsignedInt senderIP);
};
5 changes: 4 additions & 1 deletion Core/GameEngine/Include/GameNetwork/LANPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
class LANPlayer
{
public:
LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = nullptr; m_IP = 0; }
LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = nullptr; m_IP = 0; m_productInfoFlags = 0; }

// Access functions
UnicodeString getName( void ) { return m_name; }
Expand All @@ -52,6 +52,8 @@ class LANPlayer
void setNext( LANPlayer *next ) { m_next = next; }
UnsignedInt getIP( void ) { return m_IP; }
void setIP( UnsignedInt IP ) { m_IP = IP; }
void setProductInfoFlags(UnsignedInt productInfoFlags) { m_productInfoFlags = productInfoFlags; }
UnsignedInt getProductInfoFlags() const { return m_productInfoFlags; }
Comment thread
Caball009 marked this conversation as resolved.

protected:
UnicodeString m_name; ///< Player name
Expand All @@ -60,4 +62,5 @@ class LANPlayer
UnsignedInt m_lastHeard; ///< The last time we heard from this player (for timeout purposes)
LANPlayer *m_next; ///< Linked list pointer
UnsignedInt m_IP; ///< Player's IP
UnsignedInt m_productInfoFlags; ///< Community made product information flags
};
7 changes: 7 additions & 0 deletions Core/GameEngine/Source/GameNetwork/GameInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ void GameSlot::reset()
m_origPlayerTemplate = -1;
m_origStartPos = -1;
m_origColor = -1;
m_productInfo = ProductInfo();
Comment thread
Caball009 marked this conversation as resolved.
}

void GameSlot::saveOffOriginalInfo( void )
Expand Down Expand Up @@ -1493,7 +1494,13 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options)
//DEBUG_LOG(("ParseAsciiStringToGameInfo - game options all good, setting info"));

for(Int i = 0; i<MAX_SLOTS; i++)
{
// retain the product information if a slot is still occupied by the same player
if (game->getConstSlot(i)->getState() == SLOT_PLAYER && newSlot[i].getState() == SLOT_PLAYER)
newSlot[i].setProductInfo(game->getConstSlot(i)->getProductInfo());
Comment thread
Caball009 marked this conversation as resolved.

game->setSlot(i,newSlot[i]);
}

game->setMap(mapName);
game->setMapCRC(mapCRC);
Expand Down
23 changes: 23 additions & 0 deletions Core/GameEngine/Source/GameNetwork/LANAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,26 @@ void LANAPI::update( void )
handleInActive( msg, senderIP );
break;

// exchange product information with other players
case LANMessage::MSG_GAME_REQUEST_PRODUCT_INFO:
handleGameProductInfoRequest(msg, senderIP);
break;
case LANMessage::MSG_GAME_RESPONSE_PRODUCT_INFO:
handleGameProductInfoResponse(msg, senderIP);
break;
case LANMessage::MSG_LOBBY_REQUEST_PRODUCT_INFO:
handleLobbyProductInfoRequest(msg, senderIP);
break;
case LANMessage::MSG_LOBBY_RESPONSE_PRODUCT_INFO:
handleLobbyProductInfoResponse(msg, senderIP);
break;
case LANMessage::MSG_MATCH_REQUEST_PRODUCT_INFO:
handleMatchProductInfoRequest(msg, senderIP);
break;
case LANMessage::MSG_MATCH_RESPONSE_PRODUCT_INFO:
handleMatchProductInfoResponse(msg, senderIP);
break;

default:
DEBUG_LOG(("Unknown LAN message type %d", msg->messageType));
}
Expand Down Expand Up @@ -906,6 +926,9 @@ void LANAPI::RequestGameCreate( UnicodeString gameName, Bool isDirectConnect )
newSlot.setLogin(m_userName);
newSlot.setHost(m_hostName);

// set product information for local game slot
setProductInfoFromLocalData(&newSlot);

myGame->setSlot(0,newSlot);
myGame->setNext(nullptr);
LANPreferences pref;
Expand Down
Loading
Loading