|
group2 0.1.0
CSE 125 Group 2
|
TCP stream socket — receives client packets and echoes them back. More...
#include <Server.hpp>
Classes | |
| struct | ClientNetState |
| PR-2b (server-perf): bulk-snapshot every connected client's last-reported network state in one (mostly lock-free) operation. More... | |
| struct | Connection |
| Per-client connection state. More... | |
| struct | ClientRttSnapshot |
| Atomic-published snapshot of every connected client's network state. More... | |
Public Types | |
| using | ClientConnectedCallback = std::function<void(ClientId)> |
| Invoked on the network thread immediately after a new client is accepted. | |
| using | ClientDisconnectedCallback = std::function<void(ClientId)> |
| Invoked on the network thread immediately after a client is disconnected. | |
Public Member Functions | |
| bool | init (const char *addr, Uint16 port, const TransportConfig &transport={}, const GlobalDiscoveryConfig &discovery={}) |
| Bind a TCP socket to the given address and port. | |
| void | shutdown () |
| Close the socket and release resources. | |
| void | poll () |
| No-op since stage 3b moved I/O onto a dedicated network thread. | |
| bool | isEmpty () |
| Check whether the event queue is empty. | |
| Event | dequeueEvent () |
| Remove and return the next event from the queue. | |
| void | drainEvents (std::vector< Event > &out) |
PR-2b (server-perf): drain every queued event in FIFO order into out under a single mutex acquisition. | |
| bool | notifyPlayerClientId (ClientId clientId, entt::entity playerEntity) |
| Update client with new entity id. | |
| void | broadcastRegistry (const Registry ®istry) |
| Broadcast the full registry state to all clients. | |
| void | broadcastParticleEvents (const std::vector< NetParticleEvent > &events) |
| Broadcast particle events to all clients for effect replication. | |
| void | broadcastLobbyUpdate (const LobbyUpdateEvent &event) |
| Broadcast lobby status updates to clients. | |
| int | getClientCount () |
| Get the number of currently connected clients. | |
| uint32_t | globalDirectoryServerId () const noexcept |
| Directory-assigned global server id, or 0 before registration succeeds. | |
| uint16_t | getClientRttMs (ClientId clientId) |
| Phase 6: get this client's most-recently-reported smoothed RTT. | |
| void | snapshotClientNetStates (std::vector< ClientNetState > &out) |
| void | resetAppliedInputTicks () |
| Reset per-connection input deduplication for a new match session. | |
| void | broadcastMatchStatus (MatchStatePacket packet) |
| Broadcast match status updates to clients. | |
| void | broadcastKillEvents (const std::vector< NetKillEvent > &events) |
| Broadcast kill events to clients for kill feed updates. | |
| void | broadcastTextChat (ClientId sender, std::string_view message) |
| Broadcast a sanitized all-chat message from one client. | |
| bool | sendVoiceFrameToClient (ClientId recipient, ClientId speaker, std::uint16_t sequence, std::uint8_t frameMs, std::span< const std::uint8_t > opus) |
| Send one proximity-routed voice frame to a specific listener. | |
| bool | sendVoiceFrameToClients (std::span< const ClientId > recipients, ClientId speaker, std::uint16_t sequence, std::uint8_t frameMs, std::span< const std::uint8_t > opus) |
| Send one proximity-routed voice frame to multiple listeners. | |
| bool | sendToClient (const ClientId &clientId, const void *data, int len) |
| PR-20: unicast a serialized SHOT_DEBUG_REPORT (or any other already-framed payload) to a single client. | |
| bool | sendLobbyStateToClient (ClientId clientId, const std::vector< LobbyPlayer > &players) |
| Unicast a full lobby snapshot to a single client. | |
| bool | sendMatchConfigToClient (ClientId clientId, const MatchConfig &config) |
| Unicast the current match settings to a single client. | |
| void | broadcastRosterEventExcept (ClientId excluded, const PlayerRosterEvent &event) |
Broadcast an in-match join/leave event to all clients except excluded. | |
| void | setMaxPlayers (int maxPlayers) |
| Update the authoritative client admission cap. | |
| void | setAdvertiseServer (bool enabled) |
| Enable or disable global directory heartbeats at runtime. | |
| void | flushAllOutbound () |
| Drain every connection's outbound queue to its socket. | |
| void | onClientConnected (ClientConnectedCallback fn) |
| Register a callback fired whenever a client connects. | |
| void | onClientDisconnected (ClientDisconnectedCallback fn) |
| Register a callback fired whenever a client disconnects. | |
| uint16_t | listeningPort () const |
| Get the actual port the server is listening on. | |
| void | broadcastMatchConfig (const MatchConfig &config) |
Private Member Functions | |
| void | handleMessage (Connection &client, const void *data, Uint32 len) |
| Dispatch a single decoded message from a client. | |
| ClientId | acceptClients () |
| Accept up to one new client connection per call. | |
| void | disconnectClient (Connection &conn) |
| Disconnect a client and clean up resources. | |
| void | readClients () |
| Read and process pending messages from all connected clients. | |
| ClientId | getNextClientId () |
| Generate next unique client ID. | |
| bool | enqueueTo (const ClientId &clientId, uint8_t replaceKey, const void *data, int len) |
| Enqueue raw data for one client. | |
| void | enqueueBroadcast (uint8_t replaceKey, const void *data, int len) |
| Enqueue raw data for all currently-connected clients. | |
| void | enqueueReliableEvent (const void *data, int len) |
| Phase 3d-5: enqueue a reliable event for all clients. | |
| void | enqueueReliableEventExcept (std::span< const ClientId > excluded, const void *data, int len) |
Enqueue reliable event for all clients except those in excluded. | |
| void | networkLoop () |
| Network-thread main loop body. | |
| void | handleUdpUnreliable (std::uint64_t connId, const net::UdpEndpointAddr &from, const uint8_t *payload, uint32_t len) |
| Dispatch a UDP datagram received with channel == Unreliable. | |
| void | handleSessionPayload (std::uint64_t connId, net::ChannelId channel, const std::uint8_t *payload, std::uint32_t len) |
| void | handleVoiceFrame (Connection &conn, const std::uint8_t *payload, std::uint32_t len) |
| void | handleDirectoryEvent (const std::vector< std::uint8_t > &payload, const net::UdpEndpointAddr &from) |
| void | sendDirectoryHeartbeat (Uint64 nowMs) |
Private Attributes | |
| ClientConnectedCallback | clientConnectedFn_ |
| Fired by acceptClients() when a new client is accepted. | |
| ClientDisconnectedCallback | clientDisconnectedFn_ |
| Fired by disconnectClient() when a client drops. | |
| NET_Server * | server = nullptr |
| Underlying SDL_net server handle. | |
| std::unordered_map< ClientId, Connection > | clients |
| Currently connected clients. | |
| EventQueue | eventQueue |
| Incoming events awaiting processing. | |
| ClientId | nextClientId |
| Counter for assigning client IDs. | |
| net::UdpEndpoint | udpEndpoint_ |
| net::UdpSessionTransport | session_ |
| TransportConfig | transportConfig_ |
| GlobalDiscoveryConfig | discoveryConfig_ |
| net::UdpEndpointAddr | directoryAddr_ |
| std::atomic< std::uint32_t > | directoryServerId_ {0} |
| Uint64 | lastDirectoryHeartbeatMs_ = 0 |
| std::uint32_t | nextChatServerSeq_ = 0 |
| std::atomic< int > | maxPlayers_ {128} |
| std::atomic< bool > | advertiseServer_ {true} |
| Runtime global-directory heartbeat toggle. | |
| bool | usingUdpSession_ = false |
| Uint16 | listenPort_ = 0 |
| std::unordered_map< std::uint64_t, ClientId > | connIdToClient_ |
| UDP connection-id → ClientId lookup. | |
| std::shared_mutex | stateMutex_ |
| std::thread | networkThread_ |
| std::atomic< bool > | shouldStop_ {false} |
| std::shared_ptr< const std::vector< uint8_t > > | pendingSnapshotPayload_ |
| std::shared_ptr< const std::vector< uint8_t > > | pendingSnapshotFramed_ |
| std::vector< uint8_t > | keyframeRaw_ |
| std::uint32_t | keyframeTick_ = 0 |
| std::uint32_t | snapshotCounter_ = 0 |
| std::shared_ptr< const ClientRttSnapshot > | rttSnapshotAtomic_ |
| std::atomic< std::uint32_t > | clientCountAtomic_ {0} |
TCP stream socket — receives client packets and echoes them back.
Call poll() every tick to drain incoming messages. Extend handleMessage() with proper packet dispatch as the game protocol grows.
| using Server::ClientConnectedCallback = std::function<void(ClientId)> |
Invoked on the network thread immediately after a new client is accepted.
| using Server::ClientDisconnectedCallback = std::function<void(ClientId)> |
Invoked on the network thread immediately after a client is disconnected.
|
private |
| void Server::broadcastKillEvents | ( | const std::vector< NetKillEvent > & | events | ) |
Broadcast kill events to clients for kill feed updates.
| void Server::broadcastLobbyUpdate | ( | const LobbyUpdateEvent & | event | ) |
Broadcast lobby status updates to clients.
| void Server::broadcastMatchConfig | ( | const MatchConfig & | config | ) |
| void Server::broadcastMatchStatus | ( | MatchStatePacket | packet | ) |
Broadcast match status updates to clients.
| void Server::broadcastParticleEvents | ( | const std::vector< NetParticleEvent > & | events | ) |
Broadcast particle events to all clients for effect replication.
| void Server::broadcastRegistry | ( | const Registry & | registry | ) |
Broadcast the full registry state to all clients.
| void Server::broadcastRosterEventExcept | ( | ClientId | excluded, |
| const PlayerRosterEvent & | event ) |
Broadcast an in-match join/leave event to all clients except excluded.
The excluded client is the player who joined or left; they either know they joined already or are no longer connected.
| void Server::broadcastTextChat | ( | ClientId | sender, |
| std::string_view | message ) |
Broadcast a sanitized all-chat message from one client.
| Event Server::dequeueEvent | ( | ) |
Remove and return the next event from the queue.
|
private |
Disconnect a client and clean up resources.
PR-5b (server-perf): now takes a reference. Pre-PR-5b it took Connection by value, which was a quiet per-disconnect std::vector<...> + per-deque copy. Once Connection grew an internal std::mutex (in OutboundQueue) it stopped being copyable and the by-value form would no longer compile — switching to a reference is both faster and required. The function reads only fields it doesn't mutate beyond the final socket-destroy + udpAddr release; the caller is expected to erase the entry from clients after.
| void Server::drainEvents | ( | std::vector< Event > & | out | ) |
PR-2b (server-perf): drain every queued event in FIFO order into out under a single mutex acquisition.
Pre-PR-2b the game thread's tick loop did isEmpty()+dequeueEvent() per event, each acquiring the state mutex separately; at 100 bots × 128 Hz that was the dominant tick scope (12 ms p99). This collapses the per-event cost to a single lock + swap per tick.
|
private |
Enqueue raw data for all currently-connected clients.
| replaceKey | See enqueueTo. |
|
private |
Phase 3d-5: enqueue a reliable event for all clients.
The event is pushed to each client's reliableQueue with a fresh per-client sequence and k_reliableRedundancy send budget. Network loop ships entries via UDP each cycle and decrements the budget; entries with budget==0 get popped. Falls back to TCP OutboundQueue (with replaceKey=0) when the events-over-udp toggle is off, so the same broadcast helpers work in both modes.
|
private |
Enqueue reliable event for all clients except those in excluded.
Shared fan-out primitive for reliable one-shot notifications that should skip one or more recipients while preserving the existing UDP/TCP paths.
|
private |
Enqueue raw data for one client.
| replaceKey | See OutboundEntry::replaceKey (0 = always append, non-zero = replace existing entry with same key). |
| void Server::flushAllOutbound | ( | ) |
Drain every connection's outbound queue to its socket.
Call once per server tick, after all per-tick broadcasts. Disconnects any client whose socket reports an error during drain.
Stage 3a: runs on the game thread. Stage 3b moves the actual I/O to a dedicated network thread; this method's behaviour from the gameplay layer's perspective stays the same.
| int Server::getClientCount | ( | ) |
Get the number of currently connected clients.
| uint16_t Server::getClientRttMs | ( | ClientId | clientId | ) |
Phase 6: get this client's most-recently-reported smoothed RTT.
| clientId | Network client identifier. |
|
private |
Generate next unique client ID.
|
noexcept |
Directory-assigned global server id, or 0 before registration succeeds.
|
private |
|
private |
Dispatch a single decoded message from a client.
| client | The connection the message arrived on. |
| data | Pointer to the message payload. |
| len | Payload length in bytes. |
|
private |
|
private |
Dispatch a UDP datagram received with channel == Unreliable.
Reads the first byte of payload as a PacketType discriminator (mirrors the TCP wire format) and routes to the appropriate handler. Currently handles INPUT (Phase 3d-2) and PING (3d-3). All others are dropped silently — they're either meant for TCP or not yet ported to UDP.
|
private |
| bool Server::init | ( | const char * | addr, |
| Uint16 | port, | ||
| const TransportConfig & | transport = {}, | ||
| const GlobalDiscoveryConfig & | discovery = {} ) |
Bind a TCP socket to the given address and port.
| addr | Hostname or IP to bind to (e.g. "127.0.0.1"). |
| port | TCP port to listen on. The UDP sidecar binds to the same port (different protocol = different socket; OS handles the demux). |
| transport | Phase 3d: which UDP features to enable. |
| bool Server::isEmpty | ( | ) |
Check whether the event queue is empty.
| uint16_t Server::listeningPort | ( | ) | const |
Get the actual port the server is listening on.
Useful when the caller requested port 0 (auto-assign) and needs to know which port was assigned.
|
private |
Network-thread main loop body.
Runs continuously between init and shutdown, taking the mutex briefly for each I/O phase so the game thread isn't starved while (e.g.) draining 100 client outbound queues. Uses SDL_Delay(1) between cycles for a ~1 kHz tick — fast enough that game-thread enqueues turn into wire bytes within a millisecond, slow enough not to burn a full core.
| bool Server::notifyPlayerClientId | ( | ClientId | clientId, |
| entt::entity | playerEntity ) |
Update client with new entity id.
|
inline |
Register a callback fired whenever a client connects.
|
inline |
Register a callback fired whenever a client disconnects.
|
inline |
No-op since stage 3b moved I/O onto a dedicated network thread.
Kept as a public function so existing ServerGame code keeps compiling; the network thread (started by init and stopped by shutdown) does the real work continuously in the background. Safe to call from the game thread; just doesn't do anything.
|
private |
Read and process pending messages from all connected clients.
| void Server::resetAppliedInputTicks | ( | ) |
Reset per-connection input deduplication for a new match session.
|
private |
| bool Server::sendLobbyStateToClient | ( | ClientId | clientId, |
| const std::vector< LobbyPlayer > & | players ) |
Unicast a full lobby snapshot to a single client.
| bool Server::sendMatchConfigToClient | ( | ClientId | clientId, |
| const MatchConfig & | config ) |
Unicast the current match settings to a single client.
| bool Server::sendToClient | ( | const ClientId & | clientId, |
| const void * | data, | ||
| int | len ) |
PR-20: unicast a serialized SHOT_DEBUG_REPORT (or any other already-framed payload) to a single client.
Wraps the private enqueueTo so call sites in ServerGame can address the shooter without the broadcast cost paid by every player.
| bool Server::sendVoiceFrameToClient | ( | ClientId | recipient, |
| ClientId | speaker, | ||
| std::uint16_t | sequence, | ||
| std::uint8_t | frameMs, | ||
| std::span< const std::uint8_t > | opus ) |
Send one proximity-routed voice frame to a specific listener.
| bool Server::sendVoiceFrameToClients | ( | std::span< const ClientId > | recipients, |
| ClientId | speaker, | ||
| std::uint16_t | sequence, | ||
| std::uint8_t | frameMs, | ||
| std::span< const std::uint8_t > | opus ) |
Send one proximity-routed voice frame to multiple listeners.
| void Server::setAdvertiseServer | ( | bool | enabled | ) |
Enable or disable global directory heartbeats at runtime.
| void Server::setMaxPlayers | ( | int | maxPlayers | ) |
Update the authoritative client admission cap.
| void Server::shutdown | ( | ) |
Close the socket and release resources.
| void Server::snapshotClientNetStates | ( | std::vector< ClientNetState > & | out | ) |
|
private |
Runtime global-directory heartbeat toggle.
|
private |
Fired by acceptClients() when a new client is accepted.
|
private |
|
private |
Fired by disconnectClient() when a client drops.
|
private |
Currently connected clients.
|
private |
UDP connection-id → ClientId lookup.
|
private |
|
private |
|
private |
|
private |
Incoming events awaiting processing.
|
private |
|
private |
|
private |
|
private |
|
private |
|
private |
|
private |
|
private |
Counter for assigning client IDs.
|
private |
|
private |
|
private |
|
private |
Underlying SDL_net server handle.
|
private |
|
private |
|
private |
|
private |
|
private |
|
private |
|
private |