Skip to content

Commit e128f45

Browse files
committed
i have a concept of a plan
Signed-off-by: tintinhamans <5984296+tintinhamans@users.noreply.github.com>
1 parent 05af998 commit e128f45

6 files changed

Lines changed: 623 additions & 7 deletions

File tree

Core/GameEngine/Include/GameNetwork/GameInfo.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,9 @@ UnsignedShort GameInfo::getSuperweaponRestriction() const { return m_superweapon
273273
Bool GameInfo::oldFactionsOnly() const { return m_oldFactionsOnly; }
274274
void GameInfo::setOldFactionsOnly( Bool oldFactionsOnly ) { m_oldFactionsOnly = oldFactionsOnly; }
275275

276-
AsciiString GameInfoToAsciiString( const GameInfo *game );
277-
Bool ParseAsciiStringToGameInfo( GameInfo *game, AsciiString options );
276+
// TheSuperHackers @info arcticdolphin 02/03/2026 Added includeSeed and requireSeed parameters, defaulted to retail behavior.
277+
AsciiString GameInfoToAsciiString( const GameInfo *game, bool includeSeed = true );
278+
Bool ParseAsciiStringToGameInfo( GameInfo *game, AsciiString options, bool requireSeed = true );
278279

279280

280281
/**

Core/GameEngine/Include/GameNetwork/LANAPI.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ struct LANMessage
173173
MSG_INACTIVE, ///< I've alt-tabbed out. Unaccept me cause I'm a poo-flinging monkey.
174174

175175
MSG_REQUEST_GAME_INFO, ///< For direct connect, get the game info from a specific IP Address
176+
177+
#if !RETAIL_COMPATIBLE_NETWORKING
178+
// TheSuperHackers @feature arcticdolphin 02/03/2026 Commit-reveal protocol message types.
179+
MSG_SEED_COMMIT, ///< Seed commitment broadcast
180+
MSG_SEED_REVEAL, ///< Seed reveal broadcast
181+
MSG_SEED_READY, ///< Seed protocol complete acknowledgment
182+
#endif
176183
} messageType;
177184

178185
WideChar name[g_lanPlayerNameLength+1]; ///< My name, for convenience
@@ -267,6 +274,24 @@ struct LANMessage
267274
char options[m_lanMaxOptionsLength+1];
268275
} GameOptions;
269276

277+
#if !RETAIL_COMPATIBLE_NETWORKING
278+
// TheSuperHackers @feature arcticdolphin 02/03/2026 Commit-reveal protocol message payloads.
279+
struct
280+
{
281+
BYTE commit[32]; ///< SHA-256(secret || senderIP)
282+
} SeedCommit;
283+
284+
struct
285+
{
286+
BYTE secret[16]; ///< The original 128-bit secret value
287+
} SeedReveal;
288+
289+
struct
290+
{
291+
BYTE roundNonce[4]; ///< First 4 bytes of host commit: ties this ack to the current round
292+
} SeedReady;
293+
#endif
294+
270295
};
271296
};
272297
#pragma pack(pop)
@@ -386,6 +411,43 @@ class LANAPI : public LANAPIInterface
386411

387412
Bool m_isActive; ///< is the game currently active?
388413

414+
#if !RETAIL_COMPATIBLE_NETWORKING
415+
// TheSuperHackers @feature arcticdolphin 02/03/2026 Commit-reveal protocol state.
416+
enum SeedPhase
417+
{
418+
SEED_PHASE_NONE = 0, ///< Not in protocol
419+
SEED_PHASE_AWAITING_COMMITS, ///< Host: waiting for all commits; Non-host: committed, awaiting reveal trigger
420+
SEED_PHASE_AWAITING_REVEALS, ///< Host: waiting for all reveals; Non-host: revealed, awaiting game start
421+
};
422+
static const UnsignedInt s_seedPhaseTimeoutMs; ///< Per-phase timeout
423+
SeedPhase m_seedPhase;
424+
Bool m_seedReady; ///< TRUE once seed protocol completed successfully
425+
BYTE m_localSeedSecret[16]; ///< Local random 128-bit secret
426+
BYTE m_localSeedCommit[32]; ///< Commitment hash of local secret
427+
BYTE m_slotSeedCommit[MAX_SLOTS][32];///< Received commits per slot
428+
BYTE m_slotSeedReveal[MAX_SLOTS][16];///< Received 128-bit secrets per slot
429+
Bool m_slotCommitReceived[MAX_SLOTS];
430+
Bool m_slotRevealReceived[MAX_SLOTS];
431+
Bool m_slotSeedReady[MAX_SLOTS]; ///< Which slots have acknowledged seed ready
432+
UnsignedInt m_seedPhaseDeadline; ///< Phase deadline
433+
434+
void resetSeedProtocolState(); ///< Clear all per-round seed protocol state
435+
void beginSeedCommitPhase(); ///< Generate secret, broadcast commit, enter commit phase
436+
void beginSeedRevealPhase(); ///< Enter reveal phase and broadcast own reveal
437+
void finalizeSeed(); ///< XOR secrets, set seed, notify readiness
438+
void abortSeedProtocol(const wchar_t *reason = nullptr);
439+
Bool checkSeedSlotFlags(const Bool flags[], Bool skipLocal) const;
440+
Bool allSeedCommitsReceived() { return checkSeedSlotFlags(m_slotCommitReceived, TRUE); }
441+
Bool allSeedRevealsReceived() { return checkSeedSlotFlags(m_slotRevealReceived, TRUE); }
442+
Bool allSeedReadyReceived() { return checkSeedSlotFlags(m_slotSeedReady, FALSE); } ///< Includes local slot
443+
static Bool generateLocalSecret(BYTE secret[16]);
444+
static Bool computeSeedCommitment(const BYTE secret[16], UnsignedInt playerIP, BYTE outCommit[32]);
445+
Int findSlotForIP(UnsignedInt ip) const; ///< Returns slot index matching ip, or -1 if not found
446+
void handleSeedCommit(LANMessage *msg, UnsignedInt senderIP);
447+
void handleSeedReveal(LANMessage *msg, UnsignedInt senderIP);
448+
void handleSeedReady(LANMessage *msg, UnsignedInt senderIP);
449+
#endif
450+
389451
protected:
390452
void sendMessage(LANMessage *msg, UnsignedInt ip = 0); // Convenience function
391453
void removePlayer(LANPlayer *player);

Core/GameEngine/Source/GameNetwork/GameInfo.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,8 @@ Bool GameInfo::isSandbox()
888888

889889
static const char slotListID = 'S';
890890

891-
AsciiString GameInfoToAsciiString( const GameInfo *game )
891+
// TheSuperHackers @info arcticdolphin 02/03/2026 Added includeSeed parameter.
892+
AsciiString GameInfoToAsciiString( const GameInfo *game, bool includeSeed )
892893
{
893894
if (!game)
894895
return AsciiString::TheEmptyString;
@@ -918,6 +919,25 @@ AsciiString GameInfoToAsciiString( const GameInfo *game )
918919
}
919920

920921
AsciiString optionsString;
922+
#if RETAIL_COMPATIBLE_NETWORKING
923+
// TheSuperHackers @info arcticdolphin 02/03/2026 includeSeed is unused in retail builds; seed is always included.
924+
(void)includeSeed;
925+
#else
926+
// TheSuperHackers @info arcticdolphin 02/03/2026 SD= excluded when includeSeed=false.
927+
if (!includeSeed)
928+
{
929+
#if RTS_GENERALS
930+
optionsString.format("M=%2.2x%s;MC=%X;MS=%d;C=%d;", game->getMapContentsMask(), newMapName.str(),
931+
game->getMapCRC(), game->getMapSize(), game->getCRCInterval());
932+
#else
933+
optionsString.format("US=%d;M=%2.2x%s;MC=%X;MS=%d;C=%d;SR=%u;SC=%u;O=%c;", game->getUseStats(), game->getMapContentsMask(), newMapName.str(),
934+
game->getMapCRC(), game->getMapSize(), game->getCRCInterval(), game->getSuperweaponRestriction(),
935+
game->getStartingCash().countMoney(), game->oldFactionsOnly() ? 'Y' : 'N');
936+
#endif
937+
}
938+
else
939+
#endif
940+
{
921941
#if RTS_GENERALS
922942
optionsString.format("M=%2.2x%s;MC=%X;MS=%d;SD=%d;C=%d;", game->getMapContentsMask(), newMapName.str(),
923943
game->getMapCRC(), game->getMapSize(), game->getSeed(), game->getCRCInterval());
@@ -926,6 +946,7 @@ AsciiString GameInfoToAsciiString( const GameInfo *game )
926946
game->getMapCRC(), game->getMapSize(), game->getSeed(), game->getCRCInterval(), game->getSuperweaponRestriction(),
927947
game->getStartingCash().countMoney(), game->oldFactionsOnly() ? 'Y' : 'N' );
928948
#endif
949+
}
929950

930951
//add player info for each slot
931952
optionsString.concat(slotListID);
@@ -1000,8 +1021,14 @@ static Int grabHexInt(const char *s)
10001021
Int b = strtol(tmp, nullptr, 16);
10011022
return b;
10021023
}
1003-
Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options)
1024+
1025+
// TheSuperHackers @info arcticdolphin 02/03/2026 Added requireSeed parameter.
1026+
Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options, bool requireSeed)
10041027
{
1028+
#if RETAIL_COMPATIBLE_NETWORKING
1029+
// TheSuperHackers @info arcticdolphin 02/03/2026 requireSeed is unused in retail builds; seed is always required.
1030+
(void)requireSeed;
1031+
#endif
10051032
// Parse game options
10061033
char *buf = strdup(options.str());
10071034
char *bufPtr = buf;
@@ -1482,7 +1509,12 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options)
14821509
// * StartingCash
14831510
// * OldFactionsOnly
14841511
// In Generals they never were.
1512+
#if !RETAIL_COMPATIBLE_NETWORKING
1513+
// TheSuperHackers @info arcticdolphin 02/03/2026 SD= may be absent when requireSeed=false.
1514+
if (optionsOk && sawMap && sawMapCRC && sawMapSize && (!requireSeed || sawSeed) && sawSlotlist && sawCRC)
1515+
#else
14851516
if (optionsOk && sawMap && sawMapCRC && sawMapSize && sawSeed && sawSlotlist && sawCRC)
1517+
#endif
14861518
{
14871519
// We were setting the Global Data directly here, but Instead, I'm now
14881520
// first setting the data in game. We'll set the global data when
@@ -1499,7 +1531,19 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options)
14991531
game->setMapCRC(mapCRC);
15001532
game->setMapSize(mapSize);
15011533
game->setMapContentsMask(mapContentsMask);
1502-
game->setSeed(seed);
1534+
// TheSuperHackers @info arcticdolphin 02/03/2026 Only apply seed when SD= was present.
1535+
if (sawSeed)
1536+
{
1537+
game->setSeed(seed);
1538+
}
1539+
#if !RETAIL_COMPATIBLE_NETWORKING
1540+
else
1541+
{
1542+
// SD= was omitted (non-retail commit-reveal path): clear any stale seed so the
1543+
// GameInfo cannot carry a value from a previous parse until the protocol finalizes it.
1544+
game->setSeed(0);
1545+
}
1546+
#endif
15031547
game->setCRCInterval(crc);
15041548
game->setUseStats(useStats);
15051549
game->setSuperweaponRestriction(restriction);

0 commit comments

Comments
 (0)