-
Notifications
You must be signed in to change notification settings - Fork 202
feat(network): Send product information to distinguish clients #1404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
296c768
7a7775e
f2651b9
765e5fa
8c71c3a
e834604
7a86f43
51636b6
fd39ae4
391bdf7
4302707
7feddc7
ae11852
1900b24
870bfd3
759efbc
d53d412
a8e1aa2
c4a34ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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. | ||
|
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, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| } messageType; | ||
|
|
||
| WideChar name[g_lanPlayerNameLength+1]; ///< My name, for convenience | ||
|
|
@@ -267,6 +286,16 @@ struct LANMessage | |
| char options[m_lanMaxOptionsLength+1]; | ||
| } GameOptions; | ||
|
|
||
| // ProductInfo is sent with REQUEST_PRODUCT_INFO and RESPONSE_PRODUCT_INFO | ||
|
xezon marked this conversation as resolved.
|
||
| struct | ||
| { | ||
| UnsignedInt flags; | ||
| UnsignedInt launchTime; | ||
| UnsignedInt exeCRC; | ||
| UnsignedInt iniCRC; | ||
| UnsignedInt fpMathCRC; | ||
| WideChar data[201]; | ||
|
xezon marked this conversation as resolved.
|
||
| } ProductInfo; | ||
|
Caball009 marked this conversation as resolved.
xezon marked this conversation as resolved.
|
||
| }; | ||
| }; | ||
|
Caball009 marked this conversation as resolved.
|
||
| #pragma pack(pop) | ||
|
|
@@ -386,14 +415,22 @@ class LANAPI : public LANAPIInterface | |
|
|
||
| Bool m_isActive; ///< is the game currently active? | ||
|
|
||
| LANMessage m_productInfoMessage; ///< store product info message to avoid having to recreate it multiple times | ||
|
|
||
| protected: | ||
| void sendMessage(LANMessage *msg, UnsignedInt ip = 0); // Convenience function | ||
| void sendMessage(LANMessage *msg, UnsignedInt ip = 0, Bool broadcast = TRUE); // Convenience function | ||
| void removePlayer(LANPlayer *player); | ||
| void removeGame(LANGameInfo *game); | ||
| void addPlayer(LANPlayer *player); | ||
| void addGame(LANGameInfo *game); | ||
| AsciiString createSlotString( void ); | ||
|
|
||
| static UnsignedInt buildProductInfoFlags(); | ||
| static void setProductInfoFromLocalData(GameSlot &slot); | ||
| static void setProductInfoFromMessage(GameSlot &slot, LANMessage *msg); | ||
| 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 ); | ||
|
|
@@ -412,4 +449,12 @@ class LANAPI : public LANAPIInterface | |
| void handleGameOptions( LANMessage *msg, UnsignedInt senderIP ); | ||
| void handleInActive( LANMessage *msg, UnsignedInt senderIP ); | ||
|
|
||
| static LANMessage createProductInfoMessage(); | ||
|
Caball009 marked this conversation as resolved.
Outdated
|
||
| 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); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,6 +87,7 @@ LANAPI::LANAPI( void ) : m_transport(nullptr) | |
| m_lastUpdate = 0; | ||
| m_transport = new Transport; | ||
| m_isActive = TRUE; | ||
| m_productInfoMessage = createProductInfoMessage(); | ||
| } | ||
|
|
||
| LANAPI::~LANAPI( void ) | ||
|
|
@@ -179,13 +180,13 @@ void LANAPI::reset( void ) | |
|
|
||
| } | ||
|
|
||
| void LANAPI::sendMessage(LANMessage *msg, UnsignedInt ip /* = 0 */) | ||
| void LANAPI::sendMessage(LANMessage *msg, UnsignedInt ip /* = 0 */, Bool broadcast /*= TRUE*/) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The term Perhaps split this function into multiple to make its usage more explicit. For example have a
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is getting outside the scope of this PR imo. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be simple enough. Can write like so: void LANAPI::sendMessage(LANMessage *msg, UnsignedInt ip /* = 0 */)
{
if (ip != 0)
{
m_transport->queueSend(ip, lobbyPort, (unsigned char *)msg, sizeof(LANMessage) /*, 0, 0 */);
}
else if ((m_currentGame != nullptr) && (m_currentGame->getIsDirectConnect()))
{
sendMessageToGameRoomPlayers(msg);
}
else
{
m_transport->queueSend(m_broadcastAddr, lobbyPort, (unsigned char *)msg, sizeof(LANMessage) /*, 0, 0 */);
}
}
void LANAPI::sendMessageToGameRoomPlayers(LANMessage *msg)
{
if (!m_currentGame)
return;
Int localSlot = m_currentGame->getLocalSlotNum();
for (Int i = 0; i < MAX_SLOTS; ++i)
{
if (i != localSlot) {
GameSlot *slot = m_currentGame->getSlot(i);
if ((slot != nullptr) && (slot->isHuman())) {
m_transport->queueSend(slot->getIP(), lobbyPort, (unsigned char *)msg, sizeof(LANMessage) /*, 0, 0 */);
}
}
}
}This way you can simply call |
||
| { | ||
| if (ip != 0) | ||
| { | ||
| m_transport->queueSend(ip, lobbyPort, (unsigned char *)msg, sizeof(LANMessage) /*, 0, 0 */); | ||
| } | ||
| else if ((m_currentGame != nullptr) && (m_currentGame->getIsDirectConnect())) | ||
| else if (m_currentGame != nullptr && (m_currentGame->getIsDirectConnect() || !broadcast)) | ||
|
xezon marked this conversation as resolved.
|
||
| { | ||
| Int localSlot = m_currentGame->getLocalSlotNum(); | ||
| for (Int i = 0; i < MAX_SLOTS; ++i) | ||
|
|
@@ -425,6 +426,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)); | ||
| } | ||
|
|
@@ -906,6 +927,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; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is meant to represent unix time, then it needs to be 64 bits because 32 bits value wraps in 2038.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above. That's just 4 wasted bytes as far as I'm concerned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm ok. You could lower the precision of the 64 bit time from seconds to minutes but that also seems a bit hacky. You could go back to uptime but then it is an accurate value, unless you also keep the arrival time of the uptime to add the delta on top.
Unclear to me what the most elegant solution here is. What is this time for? To see when someone has not restarted his game before a match?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is meant to tell whether a remote client has played other matches beforehand, then how about count the number of matches played instead of the uptime? Then the value can also be just 2 or 1 bytes. When match count shows 0, then we know it is a fresh client start. When uptime shows 3 hours, we can only suspect that the match count is larger than 0. It could be 0 even after 3 hours of uptime.