Skip to content

Commit

Permalink
Kick all replay viewers when an incompatible replay is loaded (#367)
Browse files Browse the repository at this point in the history
Rejoining was already required, since playing an incompatible replay
without rejoining would usually crash the client.
  • Loading branch information
blast007 authored Dec 8, 2024
1 parent d5e5ba7 commit 2e52264
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 65 deletions.
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ BZFlag 2.4.27
* Fix skewed textures that occurred when the window size changed
- Scott Wichser
* Add support for pasting text into text inputs - Scott Wichser
* Kick all replay viewers when an incompatible replay is loaded - Scott Wichser


BZFlag 2.4.26 "Tanksgiving" (2022-11-20)
Expand Down Expand Up @@ -1499,3 +1500,4 @@ BZFlag 1.7c release 1
---------------------

preliminary open source release.

137 changes: 72 additions & 65 deletions src/bzfs/RecordReplay.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ static bool savePacket(RRpacket *p, FILE *f);
static RRpacket *loadPacket(FILE *f); // makes a new packet

static bool saveHeader(int playerIndex, RRtime filetime, FILE *f);
static bool loadHeader(ReplayHeader *h, FILE *f);
static bool loadHeader(ReplayHeader *h, FILE *f, bool *incompatible);
static bool saveFileTime(RRtime filetime, FILE *f);
static bool loadFileTime(RRtime *filetime, FILE *f);
static bool replaceFlagTypes(ReplayHeader *h);
Expand Down Expand Up @@ -759,7 +759,8 @@ bool Replay::loadFile(int playerIndex, const char *filename)
return false;
}

if (!loadHeader(&header, ReplayFile))
bool incompatible = false;
if (!loadHeader(&header, ReplayFile, &incompatible))
{
snprintf(buffer, MessageLen, "Could not open header: %s", name.c_str());
sendMessage(ServerPlayer, playerIndex, buffer);
Expand Down Expand Up @@ -832,6 +833,71 @@ bool Replay::loadFile(int playerIndex, const char *filename)
snprintf(buffer, MessageLen, " end: %s", ctime(&endTime));
sendMessage(ServerPlayer, playerIndex, buffer);

// If this is an incompatible replay, kick all the viewers
if (incompatible)
{
/* FIXME -- having to rejoin when replay files are loaded
*
* Ok, this is where it gets a bit borked. The bzflag client
* has dynamic arrays for some of its objects (players, flags,
* shots, etc...) If the client array is too small, there will
* be memory overruns. The maxPlayers problem is already dealt
* with, because it is set to (MaxPlayers + ReplayObservers)
* as soon as the -replay flag is used. The rest of them are
* still an issue.
*
* Here are a few of options:
*
* 1) make the command line option -replay <filename>, and
* only allow loading of world DB's that match the one
* from the command line file. This is probably how this
* feature will get used for the most part anyways.
*
* 2) kick all observers off of the server if an incompatible
* record file is loaded (with an appropriate warning).
* then they can reload with the original DB upon rejoining
* (DB with modified maxPlayers).
*
* 3) make fixed sized arrays on the client side
* (but what if someone really needs 1000 flags?)
*
* 4) implement a world reload feature on the client side,
* so that if the server sends a MsgGetWorld to the client
* when it isn't expecting one, it reacquires and regenerates
* its world DB. this would be the slick way to do it.
*
* 5) implement a resizing command, but that's icky.
*
* 6) leave it be, and let clients fall where they may.
*
* 7) MAC: get to the client to use STL, so segv's aren't a problem
* (and kick 'em anyways, to force a map reload)
*
*
* maxPlayers [from WorldBuilder.cxx]
* world->players = new RemotePlayer*[world->maxPlayers];
*
* maxFlags [from WorldBuilder.cxx]
* world->flags = new Flag[world->maxFlags];
* world->flagNodes = new FlagSceneNode*[world->maxFlags];
* world->flagWarpNodes = new FlagWarpSceneNode*[world->maxFlags];
*
* maxShots [from RemotePlayer.cxx]
* numShots = World::getWorld()->getMaxShots();
* shots = new RemoteShotPath*[numShots];
*/

// Notify the players that they will need to rejoin
sendMessage(ServerPlayer, AllPlayers,
"An incompatible recording has been loaded");
sendMessage(ServerPlayer, AllPlayers,
"Please rejoin the replay server");

// Replay player IDs start at MaxPlayers.
for (int i = MaxPlayers; i < curMaxPlayers; i++)
removePlayer(i, "/replay load");
}

return true;
}

Expand Down Expand Up @@ -2107,7 +2173,7 @@ static bool saveHeader(int p, RRtime filetime, FILE *f)
}


static bool loadHeader(ReplayHeader *h, FILE *f)
static bool loadHeader(ReplayHeader *h, FILE *f, bool *incompatible)
{
char buffer[ReplayHeaderSize];
const void *buf;
Expand Down Expand Up @@ -2154,81 +2220,22 @@ static bool loadHeader(ReplayHeader *h, FILE *f)
ReplayFileStart = ftell(f);

// do the worldDatabase or flagTypes need to be replaced?
bool replaced = false;
if (replaceFlagTypes(h))
{
logDebugMessage(1,"Replay: replaced flags\n");
replaced = true;
*incompatible = true;
}
if (replaceSettings(h))
{
logDebugMessage(1,"Replay: replaced settings\n");
replaced = true;
*incompatible = true;
}
if (replaceWorldDatabase(h))
{
logDebugMessage(1,"Replay: replaced world database\n");
replaced = true;
*incompatible = true;
}

if (replaced)
{
sendMessage(ServerPlayer, AllPlayers,
"An incompatible recording has been loaded");
sendMessage(ServerPlayer, AllPlayers,
"Please rejoin or face the consequences (client crashes)");
}
/* FIXME -- having to rejoin when replay files are loaded
*
* Ok, this is where it gets a bit borked. The bzflag client
* has dynamic arrays for some of its objects (players, flags,
* shots, etc...) If the client array is too small, there will
* be memory overruns. The maxPlayers problem is already dealt
* with, because it is set to (MaxPlayers + ReplayObservers)
* as soon as the -replay flag is used. The rest of them are
* still an issue.
*
* Here are a few of options:
*
* 1) make the command line option -replay <filename>, and
* only allow loading of world DB's that match the one
* from the command line file. This is probably how this
* feature will get used for the most part anyways.
*
* 2) kick all observers off of the server if an incompatible
* record file is loaded (with an appropriate warning).
* then they can reload with the original DB upon rejoining
* (DB with modified maxPlayers).
*
* 3) make fixed sized arrays on the client side
* (but what if someone really needs 1000 flags?)
*
* 4) implement a world reload feature on the client side,
* so that if the server sends a MsgGetWorld to the client
* when it isn't expecting one, it reacquires and regenerates
* its world DB. this would be the slick way to do it.
*
* 5) implement a resizing command, but that's icky.
*
* 6) leave it be, and let clients fall where they may.
*
* 7) MAC: get to the client to use STL, so segv's aren't a problem
* (and kick 'em anyways, to force a map reload)
*
*
* maxPlayers [from WorldBuilder.cxx]
* world->players = new RemotePlayer*[world->maxPlayers];
*
* maxFlags [from WorldBuilder.cxx]
* world->flags = new Flag[world->maxFlags];
* world->flagNodes = new FlagSceneNode*[world->maxFlags];
* world->flagWarpNodes = new FlagWarpSceneNode*[world->maxFlags];
*
* maxShots [from RemotePlayer.cxx]
* numShots = World::getWorld()->getMaxShots();
* shots = new RemoteShotPath*[numShots];
*/

return true;
}

Expand Down

0 comments on commit 2e52264

Please sign in to comment.