Skip to content

Commit 2e52264

Browse files
authored
Kick all replay viewers when an incompatible replay is loaded (#367)
Rejoining was already required, since playing an incompatible replay without rejoining would usually crash the client.
1 parent d5e5ba7 commit 2e52264

File tree

2 files changed

+74
-65
lines changed

2 files changed

+74
-65
lines changed

ChangeLog

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ BZFlag 2.4.27
1919
* Fix skewed textures that occurred when the window size changed
2020
- Scott Wichser
2121
* Add support for pasting text into text inputs - Scott Wichser
22+
* Kick all replay viewers when an incompatible replay is loaded - Scott Wichser
2223

2324

2425
BZFlag 2.4.26 "Tanksgiving" (2022-11-20)
@@ -1499,3 +1500,4 @@ BZFlag 1.7c release 1
14991500
---------------------
15001501

15011502
preliminary open source release.
1503+

src/bzfs/RecordReplay.cxx

+72-65
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ static bool savePacket(RRpacket *p, FILE *f);
153153
static RRpacket *loadPacket(FILE *f); // makes a new packet
154154

155155
static bool saveHeader(int playerIndex, RRtime filetime, FILE *f);
156-
static bool loadHeader(ReplayHeader *h, FILE *f);
156+
static bool loadHeader(ReplayHeader *h, FILE *f, bool *incompatible);
157157
static bool saveFileTime(RRtime filetime, FILE *f);
158158
static bool loadFileTime(RRtime *filetime, FILE *f);
159159
static bool replaceFlagTypes(ReplayHeader *h);
@@ -759,7 +759,8 @@ bool Replay::loadFile(int playerIndex, const char *filename)
759759
return false;
760760
}
761761

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

836+
// If this is an incompatible replay, kick all the viewers
837+
if (incompatible)
838+
{
839+
/* FIXME -- having to rejoin when replay files are loaded
840+
*
841+
* Ok, this is where it gets a bit borked. The bzflag client
842+
* has dynamic arrays for some of its objects (players, flags,
843+
* shots, etc...) If the client array is too small, there will
844+
* be memory overruns. The maxPlayers problem is already dealt
845+
* with, because it is set to (MaxPlayers + ReplayObservers)
846+
* as soon as the -replay flag is used. The rest of them are
847+
* still an issue.
848+
*
849+
* Here are a few of options:
850+
*
851+
* 1) make the command line option -replay <filename>, and
852+
* only allow loading of world DB's that match the one
853+
* from the command line file. This is probably how this
854+
* feature will get used for the most part anyways.
855+
*
856+
* 2) kick all observers off of the server if an incompatible
857+
* record file is loaded (with an appropriate warning).
858+
* then they can reload with the original DB upon rejoining
859+
* (DB with modified maxPlayers).
860+
*
861+
* 3) make fixed sized arrays on the client side
862+
* (but what if someone really needs 1000 flags?)
863+
*
864+
* 4) implement a world reload feature on the client side,
865+
* so that if the server sends a MsgGetWorld to the client
866+
* when it isn't expecting one, it reacquires and regenerates
867+
* its world DB. this would be the slick way to do it.
868+
*
869+
* 5) implement a resizing command, but that's icky.
870+
*
871+
* 6) leave it be, and let clients fall where they may.
872+
*
873+
* 7) MAC: get to the client to use STL, so segv's aren't a problem
874+
* (and kick 'em anyways, to force a map reload)
875+
*
876+
*
877+
* maxPlayers [from WorldBuilder.cxx]
878+
* world->players = new RemotePlayer*[world->maxPlayers];
879+
*
880+
* maxFlags [from WorldBuilder.cxx]
881+
* world->flags = new Flag[world->maxFlags];
882+
* world->flagNodes = new FlagSceneNode*[world->maxFlags];
883+
* world->flagWarpNodes = new FlagWarpSceneNode*[world->maxFlags];
884+
*
885+
* maxShots [from RemotePlayer.cxx]
886+
* numShots = World::getWorld()->getMaxShots();
887+
* shots = new RemoteShotPath*[numShots];
888+
*/
889+
890+
// Notify the players that they will need to rejoin
891+
sendMessage(ServerPlayer, AllPlayers,
892+
"An incompatible recording has been loaded");
893+
sendMessage(ServerPlayer, AllPlayers,
894+
"Please rejoin the replay server");
895+
896+
// Replay player IDs start at MaxPlayers.
897+
for (int i = MaxPlayers; i < curMaxPlayers; i++)
898+
removePlayer(i, "/replay load");
899+
}
900+
835901
return true;
836902
}
837903

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

21092175

2110-
static bool loadHeader(ReplayHeader *h, FILE *f)
2176+
static bool loadHeader(ReplayHeader *h, FILE *f, bool *incompatible)
21112177
{
21122178
char buffer[ReplayHeaderSize];
21132179
const void *buf;
@@ -2154,81 +2220,22 @@ static bool loadHeader(ReplayHeader *h, FILE *f)
21542220
ReplayFileStart = ftell(f);
21552221

21562222
// do the worldDatabase or flagTypes need to be replaced?
2157-
bool replaced = false;
21582223
if (replaceFlagTypes(h))
21592224
{
21602225
logDebugMessage(1,"Replay: replaced flags\n");
2161-
replaced = true;
2226+
*incompatible = true;
21622227
}
21632228
if (replaceSettings(h))
21642229
{
21652230
logDebugMessage(1,"Replay: replaced settings\n");
2166-
replaced = true;
2231+
*incompatible = true;
21672232
}
21682233
if (replaceWorldDatabase(h))
21692234
{
21702235
logDebugMessage(1,"Replay: replaced world database\n");
2171-
replaced = true;
2236+
*incompatible = true;
21722237
}
21732238

2174-
if (replaced)
2175-
{
2176-
sendMessage(ServerPlayer, AllPlayers,
2177-
"An incompatible recording has been loaded");
2178-
sendMessage(ServerPlayer, AllPlayers,
2179-
"Please rejoin or face the consequences (client crashes)");
2180-
}
2181-
/* FIXME -- having to rejoin when replay files are loaded
2182-
*
2183-
* Ok, this is where it gets a bit borked. The bzflag client
2184-
* has dynamic arrays for some of its objects (players, flags,
2185-
* shots, etc...) If the client array is too small, there will
2186-
* be memory overruns. The maxPlayers problem is already dealt
2187-
* with, because it is set to (MaxPlayers + ReplayObservers)
2188-
* as soon as the -replay flag is used. The rest of them are
2189-
* still an issue.
2190-
*
2191-
* Here are a few of options:
2192-
*
2193-
* 1) make the command line option -replay <filename>, and
2194-
* only allow loading of world DB's that match the one
2195-
* from the command line file. This is probably how this
2196-
* feature will get used for the most part anyways.
2197-
*
2198-
* 2) kick all observers off of the server if an incompatible
2199-
* record file is loaded (with an appropriate warning).
2200-
* then they can reload with the original DB upon rejoining
2201-
* (DB with modified maxPlayers).
2202-
*
2203-
* 3) make fixed sized arrays on the client side
2204-
* (but what if someone really needs 1000 flags?)
2205-
*
2206-
* 4) implement a world reload feature on the client side,
2207-
* so that if the server sends a MsgGetWorld to the client
2208-
* when it isn't expecting one, it reacquires and regenerates
2209-
* its world DB. this would be the slick way to do it.
2210-
*
2211-
* 5) implement a resizing command, but that's icky.
2212-
*
2213-
* 6) leave it be, and let clients fall where they may.
2214-
*
2215-
* 7) MAC: get to the client to use STL, so segv's aren't a problem
2216-
* (and kick 'em anyways, to force a map reload)
2217-
*
2218-
*
2219-
* maxPlayers [from WorldBuilder.cxx]
2220-
* world->players = new RemotePlayer*[world->maxPlayers];
2221-
*
2222-
* maxFlags [from WorldBuilder.cxx]
2223-
* world->flags = new Flag[world->maxFlags];
2224-
* world->flagNodes = new FlagSceneNode*[world->maxFlags];
2225-
* world->flagWarpNodes = new FlagWarpSceneNode*[world->maxFlags];
2226-
*
2227-
* maxShots [from RemotePlayer.cxx]
2228-
* numShots = World::getWorld()->getMaxShots();
2229-
* shots = new RemoteShotPath*[numShots];
2230-
*/
2231-
22322239
return true;
22332240
}
22342241

0 commit comments

Comments
 (0)