Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions Core/GameEngine/Include/Common/MessageStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ class GameMessage : public MemoryPoolObject
MSG_CLEAR_GAME_DATA, ///< Clear all game data in memory
MSG_NEW_GAME, ///< Start a new game

MSG_LOGIC_CRC_PLAYBACK, ///< TheSuperHackers @tweak CRC from the logic used internally for replay playback.
Comment thread
Caball009 marked this conversation as resolved.

// "meta" messages should be thought of as "virtual keystrokes" -- they exist
// solely to provide an abstraction layer useful for keyboard/mouse remapping.
// they should NEVER be sent over the network.
Expand Down Expand Up @@ -601,8 +603,8 @@ class GameMessage : public MemoryPoolObject
MSG_SET_REPLAY_CAMERA, ///< Track camera pos for replays
MSG_SELF_DESTRUCT, ///< Destroys a player's units (for copy protection or to quit to observer)
MSG_CREATE_FORMATION, ///< Creates a formation.
MSG_LOGIC_CRC, ///< CRC from the logic passed around in a network game :)
MSG_SET_MINE_CLEARING_DETAIL, ///< CRC from the logic passed around in a network game :)
MSG_LOGIC_CRC, ///< CRC from the logic passed around in a network game
MSG_SET_MINE_CLEARING_DETAIL, ///< Order selected unit(s) to clear mines
MSG_ENABLE_RETALIATION_MODE, ///< Turn retaliation mode on or off.

MSG_BEGIN_DEBUG_NETWORK_MESSAGES = 1900, ///< network messages that exist only in debug/internal builds. all grouped separately.
Expand Down
1 change: 1 addition & 0 deletions Core/GameEngine/Source/Common/MessageStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ const char *GameMessage::getCommandTypeAsString(GameMessage::Type t)
CASE_LABEL(MSG_MOUSE_RIGHT_DOUBLE_CLICK)
CASE_LABEL(MSG_CLEAR_GAME_DATA)
CASE_LABEL(MSG_NEW_GAME)
CASE_LABEL(MSG_LOGIC_CRC_PLAYBACK)
CASE_LABEL(MSG_BEGIN_META_MESSAGES)
CASE_LABEL(MSG_META_SAVE_VIEW1)
CASE_LABEL(MSG_META_SAVE_VIEW2)
Expand Down
12 changes: 8 additions & 4 deletions Core/GameEngine/Source/GameClient/GameClientDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,18 @@ GameMessageDisposition GameClientMessageDispatcher::translateGameMessage(const G
{
if (msg->getType() >= GameMessage::MSG_BEGIN_NETWORK_MESSAGES && msg->getType() <= GameMessage::MSG_END_NETWORK_MESSAGES)
return KEEP_MESSAGE;
if (msg->getType() == GameMessage::MSG_NEW_GAME || msg->getType() == GameMessage::MSG_CLEAR_GAME_DATA)
return KEEP_MESSAGE;

if (msg->getType() == GameMessage::MSG_FRAME_TICK)
switch (msg->getType())
{
case GameMessage::MSG_FRAME_TICK:
case GameMessage::MSG_CLEAR_GAME_DATA:
case GameMessage::MSG_NEW_GAME:
case GameMessage::MSG_LOGIC_CRC_PLAYBACK:
return KEEP_MESSAGE;
}

//DEBUG_LOG(("GameClientMessageDispatcher::translateGameMessage() - eating a %s on frame %d",
//((GameMessage *)msg)->getCommandAsString(), TheGameClient->getFrame()));
//msg->getCommandAsString(), TheGameClient->getFrame()));

return DESTROY_MESSAGE;
}
40 changes: 16 additions & 24 deletions Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,6 @@ bool changeLogicTimeScale(FpsValueChange change)
}


static Bool isSystemMessage( const GameMessage *msg );

enum{ DROPPED_MAX_PARTICLE_COUNT = 1000};

static Bool canSelectionSalvage( const Object *targetObj)
Expand Down Expand Up @@ -2538,11 +2536,24 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage
{
GameMessage::Type t = msg->getType();
GameMessageDisposition disp = KEEP_MESSAGE;

// We want to always be able to get to the options menu even during no input times and a clear game data message should always go through
if (t != GameMessage::MSG_META_OPTIONS && t != GameMessage::MSG_CLEAR_GAME_DATA &&
!TheInGameUI->getInputEnabled() && !isSystemMessage(msg))
switch (t)
{
return DESTROY_MESSAGE;
case GameMessage::MSG_FRAME_TICK:
case GameMessage::MSG_CLEAR_GAME_DATA:
case GameMessage::MSG_LOGIC_CRC_PLAYBACK:
case GameMessage::MSG_META_OPTIONS:
case GameMessage::MSG_META_DEMO_INSTANT_QUIT:
case GameMessage::MSG_DESTROY_SELECTED_GROUP:
case GameMessage::MSG_SET_REPLAY_CAMERA:
case GameMessage::MSG_LOGIC_CRC:
break;
default:
if (!TheInGameUI->getInputEnabled())
return DESTROY_MESSAGE;

break;
}

#if defined(RTS_DEBUG)
Expand Down Expand Up @@ -5599,22 +5610,3 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage
return disp;

}

static Bool isSystemMessage( const GameMessage *msg )
{
if (!msg) {
return false;
}
GameMessage::Type msgType = msg->getType();

switch (msgType)
{
case GameMessage::MSG_DESTROY_SELECTED_GROUP:
case GameMessage::MSG_LOGIC_CRC:
case GameMessage::MSG_SET_REPLAY_CAMERA:
case GameMessage::MSG_FRAME_TICK:
case GameMessage::MSG_META_DEMO_INSTANT_QUIT:
return TRUE;
}
return FALSE;
}
22 changes: 19 additions & 3 deletions Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,19 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
GameMessage::Type msgType = msg->getType();
switch( msgType )
{
case GameMessage::MSG_CLEAR_GAME_DATA:
{
onClearGameData(msg, currentlySelectedGroup);
break;
}
case GameMessage::MSG_NEW_GAME:
{
onNewGame(msg);
break;
}
case GameMessage::MSG_CLEAR_GAME_DATA:
case GameMessage::MSG_LOGIC_CRC_PLAYBACK:
{
onClearGameData(msg, currentlySelectedGroup);
onLogicCrcForPlayback(msg);
break;
}
case GameMessage::MSG_META_BEGIN_PATH_BUILD:
Expand Down Expand Up @@ -917,6 +922,17 @@ bool GameLogic::onClearGameData(MAYBE_UNUSED GameMessage *msg, AIGroupPtr &curre
return true;
}

bool GameLogic::onLogicCrcForPlayback(MAYBE_UNUSED GameMessage *msg)
{
const UnsignedInt newCRC = msg->getArgument(0)->integer;
Comment thread
Caball009 marked this conversation as resolved.
//DEBUG_LOG(("Saw CRC of %X from playback. Our CRC is %X. Arg count is %d",
//newCRC, getCRC(), msg->getArgumentCount()));

TheRecorder->handleCRCMessage(newCRC, -1, true);

return true;
}

bool GameLogic::onBeginPathBuild(MAYBE_UNUSED GameMessage *msg)
{
DEBUG_LOG(("META: begin path build"));
Expand Down Expand Up @@ -2418,7 +2434,7 @@ bool GameLogic::onLogicCrc(MAYBE_UNUSED GameMessage *msg)
//DEBUG_LOG(("Saw CRC of %X from player %d. Our CRC is %X. Arg count is %d",
//newCRC, msgPlayer->getPlayerIndex(), getCRC(), msg->getArgumentCount()));

TheRecorder->handleCRCMessage(newCRC, msgPlayer->getPlayerIndex(), (msg->getArgument(1)->boolean));
TheRecorder->handleCRCMessage(newCRC, msgPlayer->getPlayerIndex(), false);
}

return true;
Expand Down
1 change: 1 addition & 0 deletions Generals/Code/GameEngine/Include/GameLogic/GameLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ class GameLogic : public SubsystemInterface, public Snapshot

bool onNewGame(GameMessage *msg);
bool onClearGameData(GameMessage *msg, AIGroupPtr &currentlySelectedGroup);
bool onLogicCrcForPlayback(GameMessage *msg);
bool onBeginPathBuild(GameMessage *msg);
bool onEndPathBuild(GameMessage *msg);
bool onSetRallyPoint(GameMessage *msg);
Expand Down
7 changes: 4 additions & 3 deletions Generals/Code/GameEngine/Source/Common/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1584,9 +1584,10 @@ RecorderClass::CullBadCommandsResult RecorderClass::cullBadCommands() {

while (msg != nullptr) {
next = msg->next();
if ((msg->getType() > GameMessage::MSG_BEGIN_NETWORK_MESSAGES) &&
(msg->getType() < GameMessage::MSG_END_NETWORK_MESSAGES) &&
(msg->getType() != GameMessage::MSG_LOGIC_CRC)) {
if (msg->getType() > GameMessage::MSG_BEGIN_NETWORK_MESSAGES && msg->getType() < GameMessage::MSG_END_NETWORK_MESSAGES)
{
DEBUG_ASSERTCRASH(msg->getType() != GameMessage::MSG_LOGIC_CRC,
("Use MSG_LOGIC_CRC_PLAYBACK instead of MSG_LOGIC_CRC during replay playback"));

deleteInstance(msg);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3241,11 +3241,14 @@ void GameLogic::update()
if (generateForSolo || generateForMP)
{
m_CRC = getCRC( CRC_RECALC );
bool isPlayback = (TheRecorder && TheRecorder->isPlaybackMode());

GameMessage *msg = newInstance(GameMessage)(GameMessage::MSG_LOGIC_CRC);
// TheSuperHackers @tweak Caball009 30/09/2026 Playback CRC is now separate from regular CRC,
// removing the need for the second parameter used to distinguish them. Older clients can still
// parse newer replays because GameMessage::getArgument returns false for the missing playback argument.
const bool isPlayback = TheRecorder && TheRecorder->isPlaybackMode();
const GameMessage::Type msgType = isPlayback ? GameMessage::MSG_LOGIC_CRC_PLAYBACK : GameMessage::MSG_LOGIC_CRC;
GameMessage *msg = newInstance(GameMessage)(msgType);
msg->appendIntegerArgument(m_CRC);
msg->appendBooleanArgument(isPlayback);

// TheSuperHackers @info helmutbuhler 13/04/2025
// During replay simulation, we bypass TheMessageStream and instead put the CRC message
Expand Down
1 change: 1 addition & 0 deletions GeneralsMD/Code/GameEngine/Include/GameLogic/GameLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ class GameLogic : public SubsystemInterface, public Snapshot

bool onNewGame(GameMessage *msg);
bool onClearGameData(GameMessage *msg, AIGroupPtr &currentlySelectedGroup);
bool onLogicCrcForPlayback(GameMessage *msg);
bool onBeginPathBuild(GameMessage *msg);
bool onEndPathBuild(GameMessage *msg);
bool onSetRallyPoint(GameMessage *msg);
Expand Down
7 changes: 4 additions & 3 deletions GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1587,9 +1587,10 @@ RecorderClass::CullBadCommandsResult RecorderClass::cullBadCommands() {

while (msg != nullptr) {
next = msg->next();
if ((msg->getType() > GameMessage::MSG_BEGIN_NETWORK_MESSAGES) &&
(msg->getType() < GameMessage::MSG_END_NETWORK_MESSAGES) &&
(msg->getType() != GameMessage::MSG_LOGIC_CRC)) {
if (msg->getType() > GameMessage::MSG_BEGIN_NETWORK_MESSAGES && msg->getType() < GameMessage::MSG_END_NETWORK_MESSAGES)
Comment thread
xezon marked this conversation as resolved.
{
DEBUG_ASSERTCRASH(msg->getType() != GameMessage::MSG_LOGIC_CRC,
("Use MSG_LOGIC_CRC_PLAYBACK instead of MSG_LOGIC_CRC during replay playback"));

deleteInstance(msg);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3768,11 +3768,14 @@ void GameLogic::update()
if (generateForSolo || generateForMP)
{
m_CRC = getCRC( CRC_RECALC );
bool isPlayback = (TheRecorder && TheRecorder->isPlaybackMode());

GameMessage *msg = newInstance(GameMessage)(GameMessage::MSG_LOGIC_CRC);
// TheSuperHackers @tweak Caball009 30/09/2026 Playback CRC is now separate from regular CRC,
// removing the need for the second parameter used to distinguish them. Older clients can still
// parse newer replays because GameMessage::getArgument returns false for the missing playback argument.
const bool isPlayback = TheRecorder && TheRecorder->isPlaybackMode();
const GameMessage::Type msgType = isPlayback ? GameMessage::MSG_LOGIC_CRC_PLAYBACK : GameMessage::MSG_LOGIC_CRC;
GameMessage *msg = newInstance(GameMessage)(msgType);
Comment thread
Caball009 marked this conversation as resolved.
msg->appendIntegerArgument(m_CRC);
msg->appendBooleanArgument(isPlayback);

// TheSuperHackers @info helmutbuhler 13/04/2025
// During replay simulation, we bypass TheMessageStream and instead put the CRC message
Expand Down
Loading