group2 0.1.0
CSE 125 Group 2
Loading...
Searching...
No Matches
Bot.hpp
Go to the documentation of this file.
1
14
15#pragma once
16
19#include "network/Client.hpp"
20// PR-23: prediction parity with the real client. Header-only, so just
21// pulling in InputRingBuffer is enough at the .hpp level.
23
24#include <SDL3/SDL_stdinc.h>
25
26#include <atomic>
27#include <cstdint>
28#include <glm/vec3.hpp>
29#include <string>
30#include <thread>
31
32// (Bot members keep the trailing-underscore convention used elsewhere
33// in this file; the .clang-tidy member rule trips on it but the existing
34// code base treats trailing-underscore as the established style.)
35
36class Bot
37{
38public:
40 static constexpr int k_tickHz = 128;
41
42 Bot() = default;
43 Bot(const Bot&) = delete;
44 Bot& operator=(const Bot&) = delete;
45 Bot(Bot&&) = delete;
46 Bot& operator=(Bot&&) = delete;
47 ~Bot();
48
54 bool init(const std::string& host, Uint16 port, int botId);
55
62 void setSimulatedLatencyMs(int totalMs);
63
67 void setSimulatedLossPercent(int percent);
68
71 void start(const std::atomic<bool>& stopFlag);
72
75 void join();
76
84 [[nodiscard]] float getCurrentRttMs() const;
85
89 [[nodiscard]] bool isReady() const { return ready_.load(std::memory_order_relaxed); }
90
91private:
93 void runLoop(const std::atomic<bool>& stopFlag);
94
97 std::thread thread_;
99 uint32_t predictTick_ = 0;
100 int botId_ = 0;
101 bool initialized_ = false;
102
106 std::atomic<bool> ready_{false};
107
108 // ── PR-23: prediction + reconciliation (parity with real client) ────
109 //
110 // Client-side prediction lets the bot simulate its OWN movement
111 // locally each physics tick. Pre-PR-23 the bot's `Position` only
112 // moved when a server snapshot applied, which meant the bot's view
113 // of its own position lagged the server's by `RTT/2 + interpDelay`.
114 // For lag-comp / hit-reg testing under simulated RTT that drift was
115 // a measurement artifact (not a real-client behaviour), polluting
116 // the PR-22 ray-origin-desync metric with self-position lag the
117 // real client doesn't have.
118 //
119 // The bot now mirrors `client/game/Game.cpp::iterate`:
120 // * each physics tick, push (predictTick_, input_) into ring,
121 // run `systems::runPrediction` on the bot's local entity
122 // * after `client_.poll`, if a snapshot just applied, call
123 // `systems::runReconciliation` from `serverAckedClientTick`
124 // forward to `predictTick_`.
125 // Same code paths the real client runs — divergence between bot
126 // and real client is now data-only (different inputs), not
127 // architectural.
129
134 bool localPlayerReady_ = false;
135
143
144 // ── PR-18: per-bot snapshot observation log ──────────────────────────
145 //
146 // Opened from `GROUP2_BOT_OBS_CSV_PREFIX` at init() if set; one CSV
147 // file per bot, named `<prefix><botId>.csv`. Each row records one
148 // remote-entity sighting from the bot's perspective:
149 //
150 // wallTimeNs,observerBotId,observedClientId,posX,posY,posZ
151 //
152 // Written ONCE per snapshot apply (not once per tick) — gated by
153 // `Client::consumeSnapshotApplied()` so the row count matches the
154 // snapshot stream, not the tick stream.
155 //
156 // The companion server-side `GROUP2_SERVER_TRUTH_CSV` log records the
157 // SAME columns from the server's perspective (with `serverTick` for
158 // alignment). An offline Python tool (`scripts/netsync-analyze.py`)
159 // joins the two by wall-clock + clientId, interpolates between
160 // adjacent server samples, and reports euclidean desync per bot per
161 // entity per (sim RTT, sim loss) bucket.
162 //
163 // This framework would have caught the FragmentReassembler stuck-
164 // state bug (PR-17) instantly: bot's observation log freezes while
165 // server truth keeps moving → enormous desync values. Going forward
166 // it lets us A/B-test future netcode changes (quantization, AoI
167 // culling, snapshot-rate changes) with hard numbers, not feel.
168 std::FILE* obsCsv_ = nullptr;
169
170 // ── PR-21: bot-side shot-intent log ──────────────────────────────────
171 //
172 // Opened from `GROUP2_BOT_SHOTS_CSV_PREFIX` at init() if set. One
173 // CSV per bot, named `<prefix><botId>.csv`. Each row records ONE
174 // intentional fire (rising edge of `input.shooting`) with the
175 // information needed to join against the server-side
176 // `server_shots.csv` produced by PR-18b's `perf::shotlog`:
177 //
178 // wallTimeNs,shooterClientId,shotInputTick,
179 // originX,originY,originZ,
180 // dirX,dirY,dirZ,
181 // intendedTargetClientId,
182 // intendedTargetX,intendedTargetY,intendedTargetZ,
183 // intendedTargetDist
184 //
185 // The matching key is `(shooterClientId, shotInputTick)` — the
186 // server stamps the same pair on every shot in `server_shots.csv`.
187 // The Python analyzer joins them and reports per-RTT hit rate,
188 // intended-vs-actual target distribution, region match, etc.
189 std::FILE* shotsCsv_ = nullptr;
190 bool prevShootingForLog_ = false;
191
194 void openShotsLog();
195
208 void writeShotIntent(std::uint16_t shooterClientId,
209 std::uint32_t shotInputTick,
210 const glm::vec3& origin,
211 const glm::vec3& direction,
212 std::uint16_t intendedTargetClientId,
213 const glm::vec3& intendedTargetPos,
214 float intendedTargetDist,
215 bool botRayHit,
216 std::uint16_t botHitClientId,
217 const glm::vec3& botHitPos,
218 float botHitDist);
219
221 void closeShotsLog() noexcept;
222
225 void openObservationLog();
226
231 void writeObservationLog();
232
234 void closeObservationLog() noexcept;
235};
TCP client for connecting to the game server.
Per-tick history of stamped input snapshots, keyed by clientPredictTick.
Per-tick player input snapshot for networking and prediction.
Shared ECS registry type alias for the game engine.
entt::registry Registry
Shared ECS registry type alias.
Definition Registry.hpp:11
void setSimulatedLossPercent(int percent)
Apply a simulated UDP packet-loss percentage to the bot's UDP path.
Definition Bot.cpp:259
void join()
Block until the worker thread exits.
Definition Bot.cpp:325
std::FILE * obsCsv_
Definition Bot.hpp:168
void writeShotIntent(std::uint16_t shooterClientId, std::uint32_t shotInputTick, const glm::vec3 &origin, const glm::vec3 &direction, std::uint16_t intendedTargetClientId, const glm::vec3 &intendedTargetPos, float intendedTargetDist, bool botRayHit, std::uint16_t botHitClientId, const glm::vec3 &botHitPos, float botHitDist)
Append a shot-intent row.
Definition Bot.cpp:139
bool initialized_
True once init() succeeded; gates run().
Definition Bot.hpp:101
Registry registry_
Snapshot apply target — never read.
Definition Bot.hpp:96
void writeObservationLog()
Walk the registry for view<Position, ClientId>, write one row per replicated player entity to the bot...
Definition Bot.cpp:72
~Bot()
Definition Bot.cpp:41
int botId_
Log prefix.
Definition Bot.hpp:100
std::FILE * shotsCsv_
Definition Bot.hpp:189
std::thread thread_
Worker thread; joined in dtor or join().
Definition Bot.hpp:97
Bot(const Bot &)=delete
static constexpr int k_tickHz
Default tick rate; matches the server's physics tick rate.
Definition Bot.hpp:40
void setSimulatedLatencyMs(int totalMs)
Apply a simulated round-trip latency on the bot's UDP path.
Definition Bot.cpp:254
bool localPlayerReady_
Set true once localPlayerReadyFn fires for this bot.
Definition Bot.hpp:134
bool prevShootingForLog_
Rising-edge detector for fire log.
Definition Bot.hpp:190
bool isReady() const
PR-1: true once the bot's worker has logged at least one finished tick.
Definition Bot.hpp:89
uint32_t predictTick_
Monotonic tick counter, stamped onto each input.
Definition Bot.hpp:99
void runLoop(const std::atomic< bool > &stopFlag)
Worker-thread main loop: send input + poll, sleep to next tick.
Definition Bot.cpp:341
void closeObservationLog() noexcept
Flush + close the CSV if open. Safe to call from dtor.
Definition Bot.cpp:105
void openObservationLog()
Open the CSV if GROUP2_BOT_OBS_CSV_PREFIX is set.
Definition Bot.cpp:53
Bot & operator=(const Bot &)=delete
bool init(const std::string &host, Uint16 port, int botId)
Connect to the server.
Definition Bot.cpp:226
Bot()=default
std::atomic< bool > ready_
PR-1 (server-perf): set true after the first successful poll inside runLoop.
Definition Bot.hpp:106
Bot(Bot &&)=delete
InputRingBuffer inputRing_
Definition Bot.hpp:128
void openShotsLog()
Open the shot-intent log if GROUP2_BOT_SHOTS_CSV_PREFIX is set.
Definition Bot.cpp:115
Bot & operator=(Bot &&)=delete
float getCurrentRttMs() const
PR-1 (server-perf): current smoothed RTT in ms.
Definition Bot.cpp:331
void start(const std::atomic< bool > &stopFlag)
Spawn the worker thread.
Definition Bot.cpp:264
Client client_
Underlying TCP client (TCP today; UDP after Phase 3).
Definition Bot.hpp:95
InputSnapshot input_
Reused per-tick input scratch.
Definition Bot.hpp:98
void closeShotsLog() noexcept
Flush + close the shot log if open. Safe from dtor.
Definition Bot.cpp:180
void setupLocalPlayerCallback()
PR-23: set up the onLocalPlayerReady callback that emplaces LocalPlayer + InputSnapshot + PreviousPos...
Definition Bot.cpp:209
TCP stream client — sends input to the server and receives state updates.
Definition Client.hpp:47
Definition InputRingBuffer.hpp:27
One tick of player input, stamped with the tick it was sampled on.
Definition InputSnapshot.hpp:17