43 std::optional<uint32_t> mapSeed = std::nullopt);
79 uint32_t serverTick = 0;
84 struct RemotePlayerPresentation
86 std::shared_ptr<Player> playerSprite =
nullptr;
87 std::shared_ptr<Text> playerTag =
nullptr;
88 sim::TilePos authoritativePosQ{};
89 SnapshotSample previousSnapshot{};
90 SnapshotSample latestSnapshot{};
91 MovementDirection facingDirection = MovementDirection::Right;
92 float ticksSinceLatestSnapshot = 0.0f;
93 bool receivedSnapshotThisUpdate =
false;
94 uint8_t effectFlags = 0;
98 struct BombPresentation
100 std::shared_ptr<Sprite> bombSprite =
nullptr;
106 struct PowerupPresentation
108 std::shared_ptr<Sprite> powerupSprite =
nullptr;
113 struct ExplosionPresentation
115 std::shared_ptr<Sprite> explosionSprite =
nullptr;
116 uint32_t remainingLifetimeMs = 0;
120 struct BombSparkPresentation
122 std::shared_ptr<Sprite> sparkSprite =
nullptr;
123 uint32_t remainingLifetimeMs = 0;
127 struct PendingLocalBombPlacement
129 uint16_t cellKey = 0;
130 uint32_t remainingLifetimeMs = 0;
134 struct LivePredictionTelemetry
136 uint32_t lastAckedInputSeq = 0;
137 uint32_t lastCorrectionServerTick = 0;
138 uint32_t lastCorrectionDeltaQ = 0;
139 uint32_t lastReplayCount = 0;
140 uint32_t lastMissingInputs = 0;
141 uint32_t lastRemainingDeferredInputs = 0;
142 uint32_t maxPendingInputDepth = 0;
143 uint32_t recoveryCatchUpSeq = 0;
144 bool recoveryActive =
false;
153 net::NetClient* requireConnectedNetClient();
155 void initializeMultiplayerScenePresentation();
157 void ensureMatchLoadedAckSent(net::NetClient& netClient);
161 bool updatePreStartFlow(net::NetClient& netClient);
163 void updateMatchStartFlow(net::NetClient& netClient);
166 bool updateLobbyReturnFlow(net::NetClient& netClient);
168 void updateMatchResultFlow(net::NetClient& netClient);
171 void consumeAuthoritativeNetState(net::NetClient& netClient);
173 void applyLatestCorrectionIfAvailable(
const net::NetClient& netClient);
176 void collectPendingGameplayEvents(net::NetClient& netClient);
178 void applyNextPendingGameplayEvent();
182 bool allowsLocalGameplayInput()
const;
184 void finalizeFrameUpdate(
unsigned int delta);
187 void applySnapshot(
const net::MsgSnapshot& snapshot);
190 bool shouldApplyGameplayEvent(uint32_t gameplayEventTick,
const char* gameplayEventName)
const;
193 void applyExplosionResolvedTiles(
const net::MsgExplosionResolved& explosion);
195 void ensureLocalPresentation(uint8_t localId);
197 void seedLocalSpawnFromAssignedPlayerId();
199 void applyAuthoritativeCorrection(
const net::MsgCorrection& correction);
201 static void logCorrectionReplayOutcome(
const net::MsgCorrection& correction,
202 const net::CorrectionReplayResult& replayResult);
204 void storeCorrectionTelemetry(
const net::MsgCorrection& correction,
205 const net::CorrectionReplayResult& replayResult);
207 void applyBootstrapLocalSnapshot(
const net::MsgSnapshot::PlayerEntry& entry);
209 void updateLocalPresentationFromInputButtons(uint8_t buttons);
211 void syncLocalPresentationFromOwnedState(
const net::LocalPlayerState& localState);
213 void updateSceneObjects(
unsigned int delta);
220 void updateGameplayConnectionHealth(
const net::NetClient& netClient);
222 void setGameplayConnectionDegraded(
bool degraded, uint32_t silenceMs = 0);
224 void logLivePredictionTelemetry(
unsigned int delta);
226 void updateMaxPendingInputDepth();
229 uint32_t pendingInputDepth()
const;
231 void logPredictionSummary()
const;
234 bool shouldProcessOwnerPrediction()
const;
237 bool shouldOwnLocalStateFromPrediction()
const;
240 uint32_t currentAuthoritativeGameplayTick(
const net::NetClient& netClient)
const;
243 void showCenterBanner(std::string_view message, SDL_Color color);
245 void showCenterBanner(std::string_view mainMessage, std::string_view detailMessage, SDL_Color color);
247 void hideCenterBanner();
250 void applyAuthoritativeLocalSnapshot(
const net::MsgSnapshot::PlayerEntry& entry);
252 void updateDeathBannerFlow();
254 void setLocalPlayerAlivePresentation(
bool alive);
256 void setLocalPlayerInputLock(
bool locked);
258 void updateLocalPlayerTagPosition();
260 void updatePowerupEffectPresentations(
unsigned int delta);
262 void ensureDebugHudPresentations();
264 void updateDebugHud(
unsigned int delta);
266 void removeDebugHudPresentations();
268 void removeRemotePlayerPresentation(uint8_t playerId);
273 void applySnapshotToRemotePlayers(
const net::MsgSnapshot& snapshot, uint8_t localId);
275 void applySnapshotBombs(
const net::MsgSnapshot& snapshot);
277 void applySnapshotPowerups(
const net::MsgSnapshot& snapshot);
279 void updateOrCreateRemotePlayer(uint8_t playerId, int16_t xQ, int16_t yQ, uint32_t snapshotTick, uint8_t flags);
281 void updateOrCreateBombPresentation(
const net::MsgSnapshot::BombEntry& entry);
283 void updateOrCreatePowerupPresentation(
const net::MsgSnapshot::PowerupEntry& entry);
285 void pruneMissingRemotePlayers(
const std::unordered_set<uint8_t>& seenRemoteIds);
287 void pruneMissingBombPresentations(
const std::unordered_set<uint16_t>& seenBombCells);
289 void pruneMissingPowerupPresentations(
const std::unordered_set<uint16_t>& seenPowerupCells);
291 void removeAllRemotePlayers();
293 void removeAllSnapshotBombs();
295 void removeAllSnapshotPowerups();
297 void applyBombPlacedEvent(
const net::MsgBombPlaced& bombPlaced);
299 void applyExplosionResolvedEvent(
const net::MsgExplosionResolved& explosion);
301 void destroyBrickPresentation(uint8_t col, uint8_t row);
303 void removeBombPresentation(uint8_t col, uint8_t row);
306 bool canSpawnLocalBombSparkPresentation()
const;
308 void spawnLocalBombSparkPresentation();
310 void spawnExplosionPresentation(
const net::MsgCell& cell);
312 void updatePendingLocalBombPlacements(
unsigned int delta);
314 void updateLocalBombSparkPresentations(
unsigned int delta);
316 void updateExplosionPresentations(
unsigned int delta);
318 void removeAllLocalBombSparkPresentations();
320 void removeAllExplosionPresentations();
323 static void recordSnapshotSample(RemotePlayerPresentation& presentation,
326 uint32_t serverTick);
329 static uint32_t gameplayEventServerTick(
const net::NetClient::GameplayEvent& gameplayEvent);
331 static void updateRemoteAnimationFromSnapshotDelta(RemotePlayerPresentation& presentation);
333 void updateRemotePlayerPresentations(
unsigned int delta);
336 sim::TilePos computeRemotePresentedPosition(
const RemotePlayerPresentation& presentation)
const;
338 static void updateRemotePlayerTagPosition(RemotePlayerPresentation& presentation);
341 static std::string formatPlayerTag(uint8_t playerId);
343 void returnToMenu(
bool disconnectClient, std::string_view reason);
345 void returnToLobby(std::string_view reason);
347 void onCollisionObjectSpawned(
Tile tile,
const std::shared_ptr<Object>&
object)
override;
353 uint32_t lastAppliedSnapshotTick_ = 0;
354 uint32_t lastAppliedCorrectionTick_ = 0;
355 std::optional<uint8_t> localPlayerId_{};
356 uint32_t matchId_ = 0;
357 uint32_t matchBootstrapStartedMs_ = 0;
358 bool matchStarted_ =
true;
359 bool gameplayUnlocked_ =
true;
360 bool matchLoadedAckSent_ =
true;
361 uint32_t goBannerHideTick_ = 0;
362 std::optional<net::MsgMatchStart> currentMatchStart_{};
363 std::optional<net::MsgMatchResult> currentMatchResult_{};
364 std::shared_ptr<Text> localPlayerTag_ =
nullptr;
365 std::shared_ptr<Text> gameplayStatusText_ =
nullptr;
366 std::shared_ptr<Text> centerBannerText_ =
nullptr;
367 std::shared_ptr<Text> centerBannerDetailText_ =
nullptr;
368 std::shared_ptr<Text> debugHudNetText_ =
nullptr;
369 std::shared_ptr<Text> debugHudPredictionText_ =
nullptr;
370 std::shared_ptr<Text> debugHudSimulationText_ =
nullptr;
371 net::ClientPrediction localPrediction_{};
372 LivePredictionTelemetry livePredictionTelemetry_{};
373 MovementDirection localFacingDirection_ = MovementDirection::Right;
374 uint32_t livePredictionLogAccumulatorMs_ = 0;
375 uint32_t debugHudRefreshAccumulatorMs_ = 0;
376 uint32_t lastAppliedGameplayEventTick_ = 0;
377 bool localPlayerAlive_ =
true;
378 bool localPlayerInputLocked_ =
false;
379 bool localBombHeldOnLastQueuedInput_ =
false;
380 uint8_t localPlayerEffectFlags_ = 0;
381 uint32_t powerupBlinkAccumulatorMs_ = 0;
382 std::shared_ptr<Sound> explosionSound_ =
nullptr;
384 std::unordered_map<uint8_t, RemotePlayerPresentation> remotePlayerPresentations_;
385 std::unordered_map<uint16_t, BombPresentation> bombPresentations_;
386 std::unordered_map<uint16_t, PowerupPresentation> powerupPresentations_;
387 std::unordered_map<uint16_t, std::shared_ptr<Object>> brickPresentations_;
388 std::vector<BombSparkPresentation> bombSparkPresentations_;
389 std::vector<PendingLocalBombPlacement> pendingLocalBombPlacements_;
390 std::vector<ExplosionPresentation> explosionPresentations_;
391 std::deque<net::NetClient::GameplayEvent> pendingGameplayEvents_;
392 bool gameplayConnectionDegraded_ =
false;
393 bool exited_ =
false;
394 bool returningToMenu_ =
false;