Bomberman Multiplayer
Authoritative multiplayer networking layer for Bomberman.
Loading...
Searching...
No Matches
NetClientInternal.h
1#ifndef BOMBERMAN_NET_NETCLIENTINTERNAL_H
2#define BOMBERMAN_NET_NETCLIENTINTERNAL_H
3
5
8
9#include <chrono>
10#include <ctime>
11#include <deque>
12#include <filesystem>
13#include <iomanip>
14#include <sstream>
15
16#include <enet/enet.h>
17
18namespace bomberman::net::net_client_internal
19{
20 constexpr int kConnectTimeoutMs = 5000;
21 constexpr int kDisconnectTimeoutMs = 5000;
22 constexpr int kDisconnectPollTimeoutMs = 100;
23 // Preserve the previous combined backlog headroom (64 per event type)
24 // while moving both reliable gameplay types into one ordered queue.
25 constexpr std::size_t kMaxPendingGameplayEvents = 128;
26
27 using SteadyClock = std::chrono::steady_clock;
28 using TimePoint = SteadyClock::time_point;
29
30 inline int elapsedMs(const TimePoint& since)
31 {
32 return static_cast<int>(
33 std::chrono::duration_cast<std::chrono::milliseconds>(SteadyClock::now() - since).count());
34 }
35
36 inline uint32_t elapsedSinceOrZero(const TimePoint& latest, const TimePoint& fallback)
37 {
38 const TimePoint since = latest != TimePoint{} ? latest : fallback;
39 if (since == TimePoint{})
40 {
41 return 0;
42 }
43
44 return static_cast<uint32_t>(elapsedMs(since));
45 }
46
47 inline std::string currentLocalTimeTagForFilename()
48 {
49 const auto now = std::chrono::system_clock::now();
50 const auto nowTimeT = std::chrono::system_clock::to_time_t(now);
51
52 std::tm localTm{};
53#if defined(_WIN32)
54 localtime_s(&localTm, &nowTimeT);
55#else
56 localtime_r(&nowTimeT, &localTm);
57#endif
58
59 std::ostringstream out;
60 out << std::put_time(&localTm, "%H%M%S");
61 return out.str();
62 }
63
64 inline std::string makeUniqueJsonReportPath(const std::string_view basePathWithoutExtension)
65 {
66 std::string candidate = std::string(basePathWithoutExtension) + ".json";
67 if (!std::filesystem::exists(candidate))
68 {
69 return candidate;
70 }
71
72 for (uint32_t suffix = 2; suffix < 1000; ++suffix)
73 {
74 candidate = std::string(basePathWithoutExtension) + "_" + std::to_string(suffix) + ".json";
75 if (!std::filesystem::exists(candidate))
76 {
77 return candidate;
78 }
79 }
80
81 return std::string(basePathWithoutExtension) + "_overflow.json";
82 }
83
84 constexpr bool isHandshakeControlMessage(EMsgType type)
85 {
86 using enum EMsgType;
87
88 switch (type)
89 {
90 case Welcome:
91 case Reject:
92 return true;
93 case Hello:
94 case Input:
95 case Snapshot:
96 case Correction:
97 case BombPlaced:
99 case LobbyState:
100 case LevelInfo:
101 case MatchLoaded:
102 case MatchStart:
103 case MatchCancelled:
104 case MatchResult:
105 default:
106 return false;
107 }
108 }
109} // namespace bomberman::net::net_client_internal
110
111namespace bomberman::net
112{
113 using net_client_internal::SteadyClock;
114 using net_client_internal::TimePoint;
115
117 {
118 ENetHost* host = nullptr;
119 ENetPeer* peer = nullptr;
121
122 // Store the next seq as first-valid minus one so the first sent input is exactly kFirstInputSeq.
123 uint32_t nextInputSeq = kFirstInputSeq - 1u;
124 uint8_t inputHistory[kMaxInputBatchSize]{};
125
126 // Async connect and handshake state. Cleared by resetSessionState().
127 std::string pendingPlayerName;
128 TimePoint connectStartTime;
129 TimePoint handshakeStartTime;
130 TimePoint disconnectStartTime;
131 TimePoint connectedStartTime;
132 TimePoint lastLobbyStateReceiveTime;
133 TimePoint lastGameplayReceiveTime;
134 TimePoint lastSnapshotReceiveTime;
135 TimePoint lastCorrectionReceiveTime;
136 TimePoint lastTransportSampleTime;
137 TimePoint lastInputSendTime;
138
139 ClientDiagnostics diagnostics{};
140 ClientLiveStats liveStats{};
141 bool diagnosticsEnabled = false;
142 bool diagnosticsSessionActive = false;
143 bool diagnosticsPredictionEnabled = true;
144 bool diagnosticsRemoteSmoothingEnabled = true;
145
146 std::optional<MsgLobbyState> cachedLobbyState{};
147
149 {
150 MsgLevelInfo levelInfo{};
151 bool hasLevelInfo = false;
152 bool levelInfoPending = false;
153 bool brokenGameplayEventStream = false;
154 std::optional<MsgMatchStart> matchStart{};
155 std::optional<uint32_t> cancelledMatchId{};
156 std::optional<MsgMatchResult> matchResult{};
157 std::optional<MsgSnapshot> snapshot{};
158 std::optional<MsgCorrection> correction{};
159 std::deque<GameplayEvent> pendingGameplayEvents{};
160
161 void resetRuntimeState()
162 {
163 snapshot = {};
164 correction = {};
165 matchStart.reset();
166 cancelledMatchId.reset();
167 matchResult.reset();
168 brokenGameplayEventStream = false;
169 pendingGameplayEvents.clear();
170 }
171
172 void beginBootstrap(const MsgLevelInfo& newLevelInfo)
173 {
174 resetRuntimeState();
175 levelInfo = newLevelInfo;
176 hasLevelInfo = true;
177 levelInfoPending = true;
178 }
179
180 [[nodiscard]]
181 bool isActiveMatch(const uint32_t matchId) const
182 {
183 return hasLevelInfo && levelInfo.matchId == matchId;
184 }
185
186 [[nodiscard]]
187 uint32_t currentMatchId() const
188 {
189 return hasLevelInfo ? levelInfo.matchId : 0u;
190 }
191
192 void reset()
193 {
194 resetRuntimeState();
195 levelInfo = {};
196 hasLevelInfo = false;
197 levelInfoPending = false;
198 }
199 };
200 MatchFlowState matchFlow{};
201
202 static void onWelcome(NetClient& client,
203 const PacketHeader& /*header*/,
204 const uint8_t* payload,
205 std::size_t payloadSize)
206 {
207 client.handleWelcome(payload, payloadSize);
208 }
209
210 static void onReject(NetClient& client,
211 const PacketHeader& /*header*/,
212 const uint8_t* payload,
213 std::size_t payloadSize)
214 {
215 client.handleReject(payload, payloadSize);
216 }
217
218 static void onLevelInfo(NetClient& client,
219 const PacketHeader& /*header*/,
220 const uint8_t* payload,
221 std::size_t payloadSize)
222 {
223 client.handleLevelInfo(payload, payloadSize);
224 }
225
226 static void onLobbyState(NetClient& client,
227 const PacketHeader& /*header*/,
228 const uint8_t* payload,
229 std::size_t payloadSize)
230 {
231 client.handleLobbyState(payload, payloadSize);
232 }
233
234 static void onMatchStart(NetClient& client,
235 const PacketHeader& /*header*/,
236 const uint8_t* payload,
237 std::size_t payloadSize)
238 {
239 client.handleMatchStart(payload, payloadSize);
240 }
241
242 static void onMatchCancelled(NetClient& client,
243 const PacketHeader& /*header*/,
244 const uint8_t* payload,
245 std::size_t payloadSize)
246 {
247 client.handleMatchCancelled(payload, payloadSize);
248 }
249
250 static void onMatchResult(NetClient& client,
251 const PacketHeader& /*header*/,
252 const uint8_t* payload,
253 std::size_t payloadSize)
254 {
255 client.handleMatchResult(payload, payloadSize);
256 }
257
258 static void onSnapshot(NetClient& client,
259 const PacketHeader& /*header*/,
260 const uint8_t* payload,
261 std::size_t payloadSize)
262 {
263 client.handleSnapshot(payload, payloadSize);
264 }
265
266 static void onCorrection(NetClient& client,
267 const PacketHeader& /*header*/,
268 const uint8_t* payload,
269 std::size_t payloadSize)
270 {
271 client.handleCorrection(payload, payloadSize);
272 }
273
274 static void onBombPlaced(NetClient& client,
275 const PacketHeader& /*header*/,
276 const uint8_t* payload,
277 std::size_t payloadSize)
278 {
279 client.handleBombPlaced(payload, payloadSize);
280 }
281
282 static void onExplosionResolved(NetClient& client,
283 const PacketHeader& /*header*/,
284 const uint8_t* payload,
285 std::size_t payloadSize)
286 {
287 client.handleExplosionResolved(payload, payloadSize);
288 }
289 };
290} // namespace bomberman::net
291
292#endif // BOMBERMAN_NET_NETCLIENTINTERNAL_H
Client-side multiplayer diagnostics data model and recorder.
Client-side multiplayer connection lifecycle and packet endpoint.
Shared packet parsing and type-safe dispatch helpers.
Client-side multiplayer diagnostics recorder owned by NetClient.
Definition ClientDiagnostics.h:104
ENet client connection and protocol endpoint.
Definition NetClient.h:75
NetClient()
Constructs an idle client with no active transport.
Definition NetClient.cpp:11
spdlog::logger * client()
Client bootstrap and top-level runtime.
Definition Log.cpp:363
Shared multiplayer protocol types and transport-facing wire helpers.
Definition ClientPrediction.cpp:13
constexpr uint8_t kMaxInputBatchSize
Maximum number of inputs in a single batched MsgInput packet.
Definition NetCommon.h:67
EMsgType
Message type identifiers used in packet headers.
Definition NetCommon.h:316
@ Input
Client message containing a batch of input bitmasks and their base sequence number.
@ MatchStart
Server message indicating the authoritative server tick for round start and the unlock tick for early...
@ LevelInfo
Server message containing map seed and match id for an upcoming round.
@ LobbyState
Server message containing current lobby state.
@ BombPlaced
Server message indicating that a bomb has been placed by a player.
@ MatchResult
Server message containing the result of a completed match.
@ MatchCancelled
Server message indicating that the current match has been cancelled due to a player disconnect.
@ Welcome
Server handshake acceptance message.
@ Hello
Client-initiated handshake message.
@ Correction
Server message containing an authoritative correction to the local player's state.
@ ExplosionResolved
Server message containing the authoritative result of a bomb explosion.
@ MatchLoaded
Server message indicating that all players have loaded the level and the match is ready to start.
@ Snapshot
Server message containing the authoritative game state for a single tick.
@ Reject
Server handshake rejection message.
constexpr uint32_t kFirstInputSeq
First valid input sequence number. Seq 0 means "no input received yet".
Definition NetCommon.h:64
Round-start payload sent reliably by the server when a match begins.
Definition NetCommon.h:546
uint32_t matchId
Monotonic authoritative match identifier for this round start.
Definition NetCommon.h:547
Live multiplayer/network HUD state updated during gameplay.
Definition NetClient.h:79
Definition NetClientInternal.h:149
Definition NetClientInternal.h:117
Fixed-size message type to handler lookup table.
Definition PacketDispatch.h:65
Packet metadata prefix present on every wire message.
Definition NetCommon.h:492