Maelstrom: Switched to a state flag system for pausing and aborting the game.

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