https://github.com/libsdl-org/Maelstrom/commit/550304bb10b923caa0101dcc9585714d9563cbe0
From 550304bb10b923caa0101dcc9585714d9563cbe0 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 20 Nov 2011 07:29:24 -0500
Subject: [PATCH] Switched to a state flag system for pausing and aborting the
game. This allows us to pause and abort replays independently of pauses in
normal play.
---
game/MaelstromUI.cpp | 16 ++++++
game/controls.cpp | 123 +++++++++++++++++++++----------------------
game/controls.h | 1 -
game/game.cpp | 65 +++++++++++++++++++----
game/game.h | 3 +-
game/gameinfo.cpp | 35 +++++++++++-
game/gameinfo.h | 21 ++++++++
game/lobby.cpp | 14 +++++
game/main.cpp | 12 +++++
game/netplay.cpp | 18 +++++--
game/player.cpp | 50 ------------------
game/protocol.h | 3 --
game/replay.cpp | 47 ++++++++++++++---
game/replay.h | 1 +
14 files changed, 268 insertions(+), 141 deletions(-)
diff --git a/game/MaelstromUI.cpp b/game/MaelstromUI.cpp
index 4dc39f9c..daae29f8 100644
--- a/game/MaelstromUI.cpp
+++ b/game/MaelstromUI.cpp
@@ -306,6 +306,13 @@ UIElementControlButton::HandleEvent(const SDL_Event &event)
void
UIElementControlButton::OnMouseDown()
{
+ if (m_control == PAUSE_KEY) {
+ return;
+ }
+ if (m_control == ABORT_KEY) {
+ return;
+ }
+
Player *player = GetControlPlayer(CONTROL_LOCAL);
if (player) {
player->SetControl(m_control, true);
@@ -315,6 +322,15 @@ UIElementControlButton::OnMouseDown()
void
UIElementControlButton::OnMouseUp()
{
+ if (m_control == PAUSE_KEY) {
+ gGameInfo.ToggleLocalState(STATE_PAUSE);
+ return;
+ }
+ if (m_control == ABORT_KEY) {
+ gGameInfo.SetLocalState(STATE_ABORT, true);
+ return;
+ }
+
Player *player = GetControlPlayer(CONTROL_LOCAL);
if (player) {
player->SetControl(m_control, false);
diff --git a/game/controls.cpp b/game/controls.cpp
index eff5cf58..c7d4ede4 100644
--- a/game/controls.cpp
+++ b/game/controls.cpp
@@ -234,11 +234,6 @@ ControlsDialogDelegate::SetKeycode(int index, SDL_Keycode keycode)
}
}
-static Player *GetLocalPlayer()
-{
- return GetControlPlayer(CONTROL_LOCAL);
-}
-
static Player *GetKeyboardPlayer()
{
return GetControlPlayer(CONTROL_KEYBOARD);
@@ -318,84 +313,86 @@ static void HandleEvent(SDL_Event *event)
/* -- Handle key presses/releases */
case SDL_KEYDOWN:
- /* -- Handle ALT-ENTER hotkey */
+ key = event->key.keysym.sym;
+
player = GetKeyboardPlayer();
if (!player) {
break;
}
- if ( (event->key.keysym.sym == SDLK_RETURN) &&
- (event->key.keysym.mod & KMOD_ALT) ) {
+
+ /* Check for various control keys */
+ if ( key == controls.gFireControl )
+ player->SetControl(FIRE_KEY, 1);
+ else if ( key == controls.gTurnRControl )
+ player->SetControl(RIGHT_KEY, 1);
+ else if ( key == controls.gTurnLControl )
+ player->SetControl(LEFT_KEY, 1);
+ else if ( key == controls.gShieldControl )
+ player->SetControl(SHIELD_KEY, 1);
+ else if ( key == controls.gThrustControl )
+ player->SetControl(THRUST_KEY, 1);
+ break;
+
+ case SDL_KEYUP:
+ key = event->key.keysym.sym;
+
+ /* -- Handle special control keys */
+ if ( key == SDLK_F1 ) {
+ /* Special key --
+ Switch displayed player
+ */
+ RotatePlayerView();
+ break;
+ } else if ( key == SDLK_F3 ) {
+ /* Special key --
+ Do a screen dump here.
+ */
+ screen->ScreenDump("ScreenShot",
+ 0, 0, 0, 0);
+ break;
+ } else if ( key == SDLK_RETURN &&
+ (event->key.keysym.mod & KMOD_ALT) ) {
+ /* Special key --
+ Toggle fullscreen mode
+ */
screen->ToggleFullScreen();
break;
+ } else if ( key == controls.gPauseControl ) {
+ gGameInfo.ToggleLocalState(STATE_PAUSE);
+ break;
+ } else if ( key == controls.gQuitControl ) {
+ gGameInfo.SetLocalState(STATE_ABORT, true);
+ break;
}
- case SDL_KEYUP:
- /* -- Handle normal key bindings */
+
player = GetKeyboardPlayer();
if (!player) {
break;
}
- key = event->key.keysym.sym;
- if ( event->key.state == SDL_PRESSED ) {
- /* Check for various control keys */
- if ( key == controls.gFireControl )
- player->SetControl(FIRE_KEY, 1);
- else if ( key == controls.gTurnRControl )
- player->SetControl(RIGHT_KEY, 1);
- else if ( key == controls.gTurnLControl )
- player->SetControl(LEFT_KEY, 1);
- else if ( key == controls.gShieldControl )
- player->SetControl(SHIELD_KEY, 1);
- else if ( key == controls.gThrustControl )
- player->SetControl(THRUST_KEY, 1);
- else if ( key == controls.gPauseControl )
- player->SetControl(PAUSE_KEY, 1);
- else if ( key == controls.gQuitControl )
- player->SetControl(ABORT_KEY, 1);
- } else {
- /* Update control key status */
- if ( key == controls.gFireControl )
- player->SetControl(FIRE_KEY, 0);
- else if ( key == controls.gTurnRControl )
- player->SetControl(RIGHT_KEY, 0);
- else if ( key == controls.gTurnLControl )
- player->SetControl(LEFT_KEY, 0);
- else if ( key == controls.gShieldControl )
- player->SetControl(SHIELD_KEY, 0);
- else if ( key == controls.gThrustControl )
- player->SetControl(THRUST_KEY, 0);
- else if ( key == SDLK_F1 ) {
- /* Special key --
- Switch displayed player
- */
- RotatePlayerView();
- } else if ( key == SDLK_F3 ) {
- /* Special key --
- Do a screen dump here.
- */
- screen->ScreenDump("ScreenShot",
- 0, 0, 0, 0);
- }
- }
+
+ /* Update control key status */
+ if ( key == controls.gFireControl )
+ player->SetControl(FIRE_KEY, 0);
+ else if ( key == controls.gTurnRControl )
+ player->SetControl(RIGHT_KEY, 0);
+ else if ( key == controls.gTurnLControl )
+ player->SetControl(LEFT_KEY, 0);
+ else if ( key == controls.gShieldControl )
+ player->SetControl(SHIELD_KEY, 0);
+ else if ( key == controls.gThrustControl )
+ player->SetControl(THRUST_KEY, 0);
break;
case SDL_WINDOWEVENT:
- player = GetLocalPlayer();
- if (!player) {
- break;
- }
if (event->window.event == SDL_WINDOWEVENT_MINIMIZED) {
- player->SetControl(MINIMIZE_KEY, 1);
+ gGameInfo.SetLocalState(STATE_MINIMIZE, true);
} if (event->window.event == SDL_WINDOWEVENT_RESTORED) {
- player->SetControl(MINIMIZE_KEY, 0);
+ gGameInfo.SetLocalState(STATE_MINIMIZE, false);
}
break;
case SDL_QUIT:
- player = GetLocalPlayer();
- if (!player) {
- break;
- }
- player->SetControl(ABORT_KEY, 1);
+ gGameInfo.SetLocalState(STATE_ABORT, true);
break;
}
}
diff --git a/game/controls.h b/game/controls.h
index f7a9aa5d..a9d050d4 100644
--- a/game/controls.h
+++ b/game/controls.h
@@ -48,7 +48,6 @@ extern void ShowDawn(void);
#define FIRE_KEY 0x05
#define PAUSE_KEY 0x06
#define ABORT_KEY 0x07
-#define MINIMIZE_KEY 0x08
/* The controls structure */
class Controls
diff --git a/game/game.cpp b/game/game.cpp
index 7518e1f4..a7b90cdf 100644
--- a/game/game.cpp
+++ b/game/game.cpp
@@ -143,8 +143,6 @@ GamePanelDelegate::OnLoad()
int i;
char name[32];
- m_showingBonus = false;
-
/* Initialize our panel variables */
m_score = m_panel->GetElement<UIElement>("score");
m_shield = m_panel->GetElement<UIElement>("shield");
@@ -239,21 +237,28 @@ GamePanelDelegate::OnTick()
{
int i, j;
- /* -- Read in keyboard input for our ship */
- HandleEvents(0);
+ if (!gGameOn) {
+ // This generally shouldn't happen, but could if there were
+ // a consistency error during a replay at the bonus screen.
+ return;
+ }
- if ( m_showingBonus || !gGameOn ) {
+ // Delay processing until after bonus screen is done
+ if (gGameInfo.GetLocalState() & STATE_BONUS) {
return;
}
+ /* -- Read in keyboard input for our ship */
+ HandleEvents(0);
+
/* -- Send Sync! signal to all players, and handle keyboard. */
if (!gReplay.HandlePlayback()) {
gGameOn = 0;
return;
}
if ( SyncNetwork() < 0 ) {
- if ( gPaused & ~0x1 ) {
- /* One of the other players is minimized and may not
+ if ( gPaused ) {
+ /* One of the other players may be minimized and not
be able to send packets (iOS), so don't abort yet.
*/
return;
@@ -262,6 +267,10 @@ GamePanelDelegate::OnTick()
gGameOn = 0;
return;
}
+ if ( !UpdateGameState() ) {
+ gGameOn = 0;
+ return;
+ }
gReplay.HandleRecording();
OBJ_LOOP(i, MAX_PLAYERS) {
@@ -382,7 +391,7 @@ GamePanelDelegate::OnDraw()
/* Draw the status frame */
DrawStatus(false);
- if ( m_showingBonus ) {
+ if ( gGameInfo.GetLocalState() & STATE_BONUS ) {
return;
}
@@ -547,6 +556,36 @@ GamePanelDelegate::DrawStatus(Bool first)
} /* -- DrawStatus */
+/* ----------------------------------------------------------------- */
+/* -- Update game state based on node state */
+
+bool
+GamePanelDelegate::UpdateGameState()
+{
+ int i;
+ int paused;
+
+ // Check for game over
+ for (i = 0; i < gGameInfo.GetNumNodes(); ++i) {
+ if (gGameInfo.GetNodeState(i) & STATE_ABORT) {
+ return false;
+ }
+ }
+
+ // Check for pause status
+ paused = 0;
+ for (i = 0; i < gGameInfo.GetNumNodes(); ++i) {
+ paused |= gGameInfo.GetNodeState(i);
+ }
+ if ((paused & (STATE_PAUSE|STATE_MINIMIZE)) &&
+ !(gPaused & (STATE_PAUSE|STATE_MINIMIZE))) {
+ sound->PlaySound(gPauseSound, 5);
+ }
+ gPaused = paused;
+
+ return true;
+}
+
/* ----------------------------------------------------------------- */
/* -- Do some housekeeping! */
@@ -657,7 +696,13 @@ GamePanelDelegate::DoBonus()
label->Show();
}
- m_showingBonus = true;
+ gPlayers[0]->Multiplier(5);
+
+ gGameInfo.SetLocalState(STATE_BONUS, true);
+
+ // Update the state afterward so playback stops for replays
+ SyncNetwork();
+ UpdateGameState();
/* Fade out */
screen->FadeOut();
@@ -787,7 +832,7 @@ GamePanelDelegate::DoBonus()
ui->HidePanel(PANEL_BONUS);
- m_showingBonus = false;
+ gGameInfo.SetLocalState(STATE_BONUS, false);
/* Fade out and prepare for drawing the next wave */
screen->FadeOut();
diff --git a/game/game.h b/game/game.h
index ac8b801f..5f1bbb91 100644
--- a/game/game.h
+++ b/game/game.h
@@ -41,13 +41,12 @@ class GamePanelDelegate : public UIPanelDelegate
protected:
void DrawStatus(Bool first);
+ bool UpdateGameState();
void DoHousekeeping();
void DoBonus();
void NextWave();
protected:
- bool m_showingBonus;
-
UIElement *m_score;
UIElement *m_shield;
UIElement *m_wave;
diff --git a/game/gameinfo.cpp b/game/gameinfo.cpp
index 3d9d6905..15d78785 100644
--- a/game/gameinfo.cpp
+++ b/game/gameinfo.cpp
@@ -261,7 +261,8 @@ GameInfo::PrepareForReplay()
gameID = localID;
SDL_zero(nodes);
- numNodes = 0;
+ nodes[HOST_NODE].nodeID = localID;
+ numNodes = 1;
for (int i = 0; i < MAX_PLAYERS; ++i) {
if (IsValidPlayer(i)) {
@@ -408,6 +409,38 @@ GameInfo::IsFull() const
return true;
}
+void
+GameInfo::SetLocalState(Uint8 state, bool enabled)
+{
+ if (enabled) {
+ nodes[GetLocalIndex()].state |= state;
+ } else {
+ nodes[GetLocalIndex()].state &= ~state;
+ }
+}
+
+void
+GameInfo::ToggleLocalState(Uint8 state)
+{
+ if (GetLocalState() & state) {
+ SetLocalState(state, false);
+ } else {
+ SetLocalState(state, true);
+ }
+}
+
+void
+GameInfo::SetNodeState(int index, Uint8 state)
+{
+ nodes[index].state = state;
+}
+
+Uint8
+GameInfo::GetNodeState(int index) const
+{
+ return nodes[index].state;
+}
+
void
GameInfo::BindPlayerToUI(int index, UIElement *element)
{
diff --git a/game/gameinfo.h b/game/gameinfo.h
index 42f04c88..b5b320ca 100644
--- a/game/gameinfo.h
+++ b/game/gameinfo.h
@@ -41,6 +41,14 @@ enum PLAYER_CONTROL {
CONTROL_LOCAL = (CONTROL_KEYBOARD|CONTROL_JOYSTICK1)
};
+enum NODE_STATE_FLAG {
+ STATE_NONE = 0x00,
+ STATE_ABORT = 0x01,
+ STATE_PAUSE = 0x02,
+ STATE_BONUS = 0x04,
+ STATE_MINIMIZE = 0x08,
+};
+
enum PING_STATUS {
PING_LOCAL,
PING_GOOD,
@@ -55,6 +63,7 @@ struct GameInfoNode
{
Uint32 nodeID;
IPaddress address;
+ Uint8 state;
struct {
Uint32 lastPing;
@@ -142,6 +151,9 @@ class GameInfo
}
return -1;
}
+ int GetLocalIndex() const {
+ return GetNodeIndex(localID);
+ }
bool HasNode(Uint32 nodeID) const;
bool HasNode(const IPaddress &address) const;
@@ -169,6 +181,15 @@ class GameInfo
bool IsFull() const;
+ void SetNodeState(int index, Uint8 state);
+ Uint8 GetNodeState(int index) const;
+
+ void SetLocalState(Uint8 state, bool enabled);
+ void ToggleLocalState(Uint8 state);
+ Uint8 GetLocalState() const {
+ return GetNodeState(GetLocalIndex());
+ }
+
void BindPlayerToUI(int index, UIElement *element);
void UpdateUI();
void UpdateUI(GameInfoPlayer *player);
diff --git a/game/lobby.cpp b/game/lobby.cpp
index ed5e2ed0..99cc86a8 100644
--- a/game/lobby.cpp
+++ b/game/lobby.cpp
@@ -155,7 +155,21 @@ LobbyDialogDelegate::OnHide()
// Start the game!
if (m_dialog->GetDialogStatus() > 0) {
SetState(STATE_PLAYING);
+#define REPLAY_TEST
+#ifdef REPLAY_TEST
+gReplay.SetMode(REPLAY_RECORDING);
+#endif
NewGame();
+
+#ifdef REPLAY_TEST
+gReplay.SetMode(REPLAY_PLAYBACK);
+ HaltNetData();
+ if (InitNetData(false) < 0) {
+ return;
+ }
+ NewGame();
+#endif
+
} else {
SetState(STATE_NONE);
}
diff --git a/game/main.cpp b/game/main.cpp
index 9a6643fb..7237a675 100644
--- a/game/main.cpp
+++ b/game/main.cpp
@@ -67,7 +67,19 @@ static void RunSinglePlayerGame()
if (InitNetData(false) < 0) {
return;
}
+#define REPLAY_TEST
+#ifdef REPLAY_TEST
+gReplay.SetMode(REPLAY_RECORDING);
+#endif
NewGame();
+#ifdef REPLAY_TEST
+gReplay.SetMode(REPLAY_PLAYBACK);
+ HaltNetData();
+ if (InitNetData(false) < 0) {
+ return;
+ }
+ NewGame();
+#endif
HaltNetData();
}
static void RunPlayGame(void*)
diff --git a/game/netplay.cpp b/game/netplay.cpp
index a648999d..6c344420 100644
--- a/game/netplay.cpp
+++ b/game/netplay.cpp
@@ -32,7 +32,7 @@
#include "protocol.h"
// Set this to 1 for normal debug info, and 2 for verbose packet logging
-//#define DEBUG_NETWORK 1
+#define DEBUG_NETWORK 1
// Define this to simulate packet loss
//#define DEBUG_PACKETLOSS 10
@@ -161,14 +161,22 @@ void QueueInput(Uint8 value)
static bool ProcessSync(int index, DynamicPacket &packet)
{
Uint32 seed;
+ Uint8 state;
- if (!packet.Read(seed) || seed != GetRandSeed()) {
+ if (!packet.Read(seed) || !packet.Read(state)) {
+ error("Received short packet\r\n");
+ return false;
+ }
+
+ if (seed != GetRandSeed()) {
/* We're hosed, to correct this we would have to sync the complete game state */
error("Error!! \a Frame consistency problem, aborting!!\r\n");
return false;
}
- // FIXME: Should we validate that the input is for players from this node?
+ gGameInfo.SetNodeState(index, state);
+
+ // Should we validate that the input is for players from this node?
// ... nah... :)
FrameInput.Write(packet);
@@ -341,8 +349,6 @@ int SyncNetwork(void)
int nleft;
Uint32 waiting[MAX_NODES];
- ++NextFrame;
-
// Get the queued input
FrameInput.Reset();
QueuedInput.Seek(0);
@@ -366,6 +372,7 @@ int SyncNetwork(void)
CurrPacket.Write(gGameInfo.localID);
CurrPacket.Write(NextFrame);
CurrPacket.Write(GetRandSeed());
+ CurrPacket.Write(gGameInfo.GetLocalState());
QueuedInput.Seek(0);
CurrPacket.Write(QueuedInput);
@@ -381,6 +388,7 @@ int SyncNetwork(void)
}
QueuedInput.Reset();
+ ++NextFrame;
return(0);
}
diff --git a/game/player.cpp b/game/player.cpp
index 90fa0d20..fdbed83f 100644
--- a/game/player.cpp
+++ b/game/player.cpp
@@ -483,32 +483,6 @@ printf("\n");
return(Object::Move(Freeze));
}
-static void
-SetPaused(int bit, bool enabled)
-{
- if (enabled) {
- sound->PlaySound(gPauseSound, 5);
- gPaused |= bit;
- } else {
- gPaused &= ~bit;
- }
-}
-static void
-TogglePause()
-{
- if ( gPaused & 0x01 ) {
- SetPaused(0x01, false);
- } else {
- SetPaused(0x01, true);
- }
-}
-static void
-SetMinimized(int index, bool enabled)
-{
- int bit = (1 << (1+index));
- SetPaused(bit, enabled);
-}
-
Uint8
Player::EncodeInput(unsigned char which, bool enabled)
{
@@ -559,16 +533,7 @@ Player::HandleKeys(void)
}
if (down) {
- /* Only handle Pause and Abort while dead */
if ( ! Alive() || Exploding ) {
- if ( key == PAUSE_KEY ) {
- TogglePause();
- }
- if ( key == MINIMIZE_KEY ) {
- SetMinimized(Index, true);
- }
- if ( key == ABORT_KEY )
- gGameOn = 0;
break;
}
/* Regular key press handling */
@@ -588,15 +553,6 @@ Player::HandleKeys(void)
case FIRE_KEY:
Shooting = 1;
break;
- case PAUSE_KEY:
- TogglePause();
- break;
- case MINIMIZE_KEY:
- SetMinimized(Index, true);
- break;
- case ABORT_KEY:
- gGameOn = 0;
- break;
default:
break;
}
@@ -619,12 +575,6 @@ Player::HandleKeys(void)
case FIRE_KEY:
Shooting = 0;
break;
- case MINIMIZE_KEY:
- SetMinimized(Index, false);
- break;
- case ABORT_KEY:
- /* Do nothing on release */;
- break;
default:
break;
}
diff --git a/game/protocol.h b/game/protocol.h
index bdddb1eb..58e685fe 100644
--- a/game/protocol.h
+++ b/game/protocol.h
@@ -185,9 +185,6 @@ enum LobbyProtocol {
#define NEW_GAME_ACK 0x02 /* Sent by players at start */
#define SYNC_MSG 0x04 /* Sent during game */
-#define KEY_PRESS 0x01 /* Sent during game */
-#define KEY_RELEASE 0x02 /* Sent during game */
-
/* The default port for Maelstrom games */
#define LOBBY_PORT 0xAE00 /* port 44544 */
#define NETPLAY_PORT 0xAF00 /* port 44800 */
diff --git a/game/replay.cpp b/game/replay.cpp
index 05feeb42..b7548d97 100644
--- a/game/replay.cpp
+++ b/game/replay.cpp
@@ -24,6 +24,8 @@
#include "netplay.h"
#include "replay.h"
+// Define this to get extremely verbose debug printing
+#define DEBUG_REPLAY
#define DELTA_SIZEMASK 0x7F
#define DELTA_SEED 0x80
@@ -60,12 +62,14 @@ Replay::HandleNewGame()
if (m_mode == REPLAY_RECORDING) {
m_game.CopyFrom(gGameInfo);
m_game.PrepareForReplay();
+ m_data.Reset();
+ m_pausedInput.Reset();
} else if (m_mode == REPLAY_PLAYBACK) {
gGameInfo.CopyFrom(m_game);
gGameInfo.PrepareForReplay();
+ m_data.Seek(0);
}
m_seed = m_game.seed;
- m_data.Seek(0);
}
bool
@@ -75,9 +79,16 @@ Replay::HandlePlayback()
return true;
}
+ if (gPaused) {
+ return true;
+ }
+
Uint8 delta;
if (!m_data.Read(delta)) {
// That's it, end of recording
+#ifdef DEBUG_REPLAY
+printf("Replay complete!\n");
+#endif
return false;
}
@@ -100,10 +111,17 @@ Replay::HandlePlayback()
error("Error in replay, missing data\r\n");
return false;
}
+#ifdef DEBUG_REPLAY
+static int foo = 0;
+printf("Read %d bytes for frame %d", size, foo++);
+#endif
while (size--) {
m_data.Read(value);
QueueInput(value);
}
+#ifdef DEBUG_REPLAY
+printf(", pos = %d\n", m_data.Tell());
+#endif
return true;
}
@@ -115,11 +133,18 @@ Replay::HandleRecording()
}
// Get the input for this frame
- Uint8 delta;
Uint8 *data;
- int len = GetSyncBuf(&data);
- assert(len < DELTA_SIZEMASK);
- delta = (Uint8)len;
+ int size = GetSyncBuf(&data);
+
+ // If we're paused, save this data for the next unpaused frame
+ if (gPaused) {
+ m_pausedInput.Write(data, size);
+ return;
+ }
+ assert(size+m_pausedInput.Size() < DELTA_SIZEMASK);
+
+ Uint8 delta;
+ delta = (Uint8)size+m_pausedInput.Size();
// Add it to our data buffer
Uint32 seed = GetRandSeed();
@@ -131,5 +156,15 @@ Replay::HandleRecording()
} else {
m_data.Write(delta);
}
- m_data.Write(data, len);
+
+ if (m_pausedInput.Size() > 0) {
+ m_pausedInput.Seek(0);
+ m_data.Write(m_pausedInput);
+ m_pausedInput.Reset();
+ }
+ m_data.Write(data, size);
+#ifdef DEBUG_REPLAY
+static int foo = 0;
+printf("Wrote %d bytes for frame %d, size = %d\n", size, foo++, m_data.Size());
+#endif
}
diff --git a/game/replay.h b/game/replay.h
index 23bdd74a..4a223238 100644
--- a/game/replay.h
+++ b/game/replay.h
@@ -67,6 +67,7 @@ class Replay
GameInfo m_game;
Uint32 m_seed;
DynamicPacket m_data;
+ DynamicPacket m_pausedInput;
};
#endif // _replay_h