feat(network): Send product information to distinguish clients#1404
feat(network): Send product information to distinguish clients#1404Caball009 wants to merge 10 commits intoTheSuperHackers:mainfrom
Conversation
GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp
Outdated
Show resolved
Hide resolved
|
What is the current status of this change? I think this would be a nice change for the first release. |
The current implementation ought to be changed to make it more efficient / optimal, but that requires much more work. I did some of the work locally, but it didn't really work out iirc, so the PR is kind of stuck for now. |
9fb6a01 to
c4e3f1d
Compare
|
I've revisited this PR and was able to move away from the broadcast implementation. It feels better now. I put networking related changes in one commit and the color / status stuff in another. What sort of information do we want to exchange just to establish which clients are on a patched version? Maybe it's a good idea to send the commit hash as well. |
|
Short overview of the new implementation. Patch information is not broadcast and only sent when needed. Interacting with players in the lobby:
Interacting with game hosts in the lobby:
Interacting with players in the pre-match game:
|
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp
Outdated
Show resolved
Hide resolved
xezon
left a comment
There was a problem hiding this comment.
This change is going into the right direction but needs more polishing and wider scope.
Are the pictures of the first post still relevant?
The terminology of the network data needs changing from "Patch" to "Product", because "Patch" is a much more limited term than "Product" is.
The ProductInfo message needs to contain all relevant information to describe the Product that the remote player is using. A version number alone will not be enough to properly describe a Product. Think of 100 different products that the user could interact with in the lobby.
Yes, the colors are still like that; the status text will be slightly different. My current idea is that it shows the Git short hash string there (there isn't much room for additional text, unfortunately).
Fair enough. Everything should say 'product' instead of 'patch' now.
I added a couple of stuff (e.g. exe and ini crc values), but I don't feel like this is final version yet. |
|
I could use some feedback on this PR. |
xezon
left a comment
There was a problem hiding this comment.
Current thoughts on this. No exhaustive review.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp
Outdated
Show resolved
Hide resolved
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Include/GameNetwork/GameInfo.h | Added ProductInfo struct to GameSlot with flags, CRCs, and version strings - clean data structure design |
| Core/GameEngine/Include/GameNetwork/LANAPI.h | Added 6 new message types and helper methods for product info exchange - clear protocol extension |
| Core/GameEngine/Source/GameNetwork/LANAPI.cpp | Added message handling dispatch for 6 product info message types and cached product info message |
| Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp | Implemented product info request/response handlers for lobby, game, and match contexts with string serialization |
Sequence Diagram
sequenceDiagram
participant P1 as Player 1 (Lobby)
participant P2 as Player 2 (Lobby)
participant H as Host (Lobby)
participant PM as Players (In Match)
Note over P1,P2: Lobby: Player-to-Player
P1->>P2: MSG_LOBBY_REQUEST_PRODUCT_INFO
P2->>P1: MSG_LOBBY_RESPONSE_PRODUCT_INFO
Note over P1: P2 marked as patched
Note over P1,H: Lobby: Player-to-Host
P1->>H: MSG_GAME_REQUEST_PRODUCT_INFO
H->>P1: MSG_GAME_RESPONSE_PRODUCT_INFO
Note over P1: Host marked as patched
Note over PM: Pre-Match: Join Trigger
PM->>PM: Player joins match
PM->>PM: MSG_MATCH_REQUEST_PRODUCT_INFO (broadcast)
PM->>PM: MSG_MATCH_RESPONSE_PRODUCT_INFO (responses)
Note over PM: All players mark each other as patched
Last reviewed commit: 391bdf7
540c3e7 to
f2651b9
Compare
|
I'm slowly moving forward with this PR, though there are still several unresolved conversations. I've decided that the code for colored names is best suited for a follow-up PR. |
|
Please set to draft if this is still WIP. It is not clear to me if Review can continue. |
This PR is ready for review. Perhaps there are a few minor details that may still change. I hope #2100 won't take much longer so I can include it in this PR. For anyone who wants to test this, you can revert the reverted commit so that you can see the colored names and status again. |
xezon
left a comment
There was a problem hiding this comment.
Looks promising but there is still some polishing to do.
| // 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. |
There was a problem hiding this comment.
Is there precedence to this type of communication? Is it expected to be ok if there are 100 people in the Network lobby that receive and respond these new messages?
Will a response just reach the client that made the request or is the response broadcasted to everyone?
There was a problem hiding this comment.
Will a response just reach the client that made the request or is the response broadcasted to everyone?
Only the client that made the request. I'll update the comment to reflect that.
|
I just addressed most of the review feedback I'd like to reduce the string buffer size from 200 characters to 190. For reference, we barely even use 50 characters depending on the user name: |
How much is left now? Are there not more than 200 characters of spare space? |
| // - When a player detects a new game host in the lobby, it sends a product info request to that host. | ||
| // - 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 players in that match. |
There was a problem hiding this comment.
Do the previous players also receive Product Info from the new joined player?
| 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)) |
There was a problem hiding this comment.
Condition can be simplified by doing
if (m_currentGame->getIsDirectConnect())
broadcast = false;
before
| } | ||
|
|
||
| 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.
The term broadcast is confusing. Because when it is set to FALSE it will still broadcast to game room participants.
Perhaps split this function into multiple to make its usage more explicit.
For example have a LANAPI::sendMessageToGameRoomPlayers(LANMessage *msg)
| } | ||
| else | ||
| { | ||
| output[ARRAY_SIZE(output) - 1] = '\0'; |
There was a problem hiding this comment.
Would there not already be a guaranteed null via wcslcpy?
| // null terminate the output buffer to signal the end of the last input string | ||
| if (outputIndex < ARRAY_SIZE(output)) | ||
| { | ||
| output[outputIndex++] = '\0'; |
There was a problem hiding this comment.
Would there not already be a guaranteed null via wcslcpy?
Can we just do
return outputIndex < ARRAY_SIZE(output);
| const size_t length = etx_strlen_t(input + inputIndex); | ||
| output[i]->set(input + inputIndex, length); | ||
|
|
||
| inputIndex += length + 1; |
There was a problem hiding this comment.
Perhaps write length + sizeof('\3') to make clear what the 1 means.
| output[i]->set(input + inputIndex, length); | ||
|
|
||
| inputIndex += length + 1; | ||
| if (inputIndex >= ARRAY_SIZE(input)) |
There was a problem hiding this comment.
This condition looks as if it returns true when the input is exactly 200 characters, perfectly fitting?
Perhaps do inputIndex += sizeof('\3') after this condition only?
| void handleGameOptions( LANMessage *msg, UnsignedInt senderIP ); | ||
| void handleInActive( LANMessage *msg, UnsignedInt senderIP ); | ||
|
|
||
| static LANMessage createProductInfoMessage(); |
There was a problem hiding this comment.
buildProductInfoMessage to be consistent with buildProductInfoFlags
| m_logicTimeAccumulator = 0.0f; | ||
| m_quitting = FALSE; | ||
| m_isActive = FALSE; | ||
| m_launchTime = ::timeGetTime(); |
There was a problem hiding this comment.
What is the purpose of the launch time? I suspect to see for how long the game has been running? timeGetTime() return the milliseconds from Windows start until now. This is not a useful information for others.
What we likely need to send is a UTC time. With c++20 that would be chrono::utc_time, but with VC6 we would need gmtime https://en.cppreference.com/w/c/chrono/gmtime
| }; | ||
|
|
||
| UnsignedInt flags; | ||
| UnsignedInt launchTime; |
There was a problem hiding this comment.
If this is meant to represent unix time, then it needs to be 64 bits because 32 bits value wraps in 2038.
This PR makes our clients send product information to distinguish them from retail clients primarily. Retail clients will ignore the product information data that's sent to them.
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.
Examples of future use cases, visualizing who's not using the retail client
LAN Lobby:

Pre-match / Game Options:

Status in match:

TODO:
fpMathCRC.