Zappy Server
C++ game server: world, rules, scheduler, network
Loading...
Searching...
No Matches
Server.cpp
Go to the documentation of this file.
1#include "core/Server.hpp"
2
3#include <chrono>
4#include <cmath>
5#include <csignal>
6#include <iostream>
7#include <memory>
8#include <string>
9#include <vector>
10
11namespace {
12
13 volatile std::sig_atomic_t g_stopRequested = 0;
14 extern "C" void requestStop(int) { g_stopRequested = 1; }
15
16} // namespace
17
20#include "logging/FileSink.hpp"
22
24 : _config(config),
25 _listener(config.port),
26 _clients(_listener),
27 _world(config.width, config.height, config.teamNames, config.seed),
28 _notifier(_clients),
29 _logObserver(_logger),
30 _dispatcher(_clients, _world, _notifier, _config, _scheduler)
31{
32 auto sinks = std::make_unique<CompositeSink>();
33 sinks->add(std::make_unique<ConsoleSink>(LogLevel::Info));
34 sinks->add(FileSink::forRun("server_p" + std::to_string(_config.port), LogLevel::Info));
35 _logger.setSink(std::move(sinks));
36
40
43}
44
46{
47 _scheduler.schedule(std::chrono::milliseconds(RESPAWN_INTERVAL_MS / _config.freq), [this] {
48 _world.spawnResources();
49 _scheduleRespawn();
50 });
51}
52
54{
55 std::cout << "Server listening on port " << _config.port << "\n";
56 std::cout << "Map size: " << _config.width << "x" << _config.height << "\n";
57 std::cout << "Seed: " << _config.seed << "\n";
58 std::cout << "Teams: ";
59 for (const auto& name : _config.teamNames) std::cout << name << " ";
60 std::cout << "\n";
61}
62
64{
65 _gameOverHandled = true;
67
68 std::vector<int> aiConns;
69 for (const auto& [pid, p] : _world.getPlayers()) aiConns.push_back(p.connectionId);
70 for (int conn : aiConns) _clients.disconnect(conn);
71
72 long long upSeconds = _dispatcher.gameElapsed().count() / 1000000;
73 long long upTicks = std::llround(_dispatcher.gameTicks());
74
75 std::string teams;
76 for (const auto& name : _config.teamNames) teams += (teams.empty() ? "" : " ") + name;
77 const std::string winner = _world.winner().value_or("?");
78
79 _logger.info("GAME", "========== GAME OVER ==========");
80 _logger.info("GAME", "Winner: " + winner);
81 _logger.info("GAME", "Teams: " + teams);
82 _logger.info("GAME", "Server uptime: " + std::to_string(upSeconds) + " s (" +
83 std::to_string(upTicks) + " ticks)");
84
85 if (auto join = _dispatcher.teamJoin(winner)) {
86 long long joinSeconds = join->elapsed.count() / 1000000;
87 long long joinTicks = std::llround(join->ticks);
88 _logger.info("GAME", winner + " joined at: " + std::to_string(joinSeconds) + " s (" +
89 std::to_string(joinTicks) + " ticks)");
90 _logger.info("GAME", winner + " took: " + std::to_string(upSeconds - joinSeconds) + " s (" +
91 std::to_string(upTicks - joinTicks) + " ticks) to win");
92 _notifier.broadcast(Serializer::gwt(winner, static_cast<int>(upSeconds - joinSeconds),
93 upTicks - joinTicks));
94 }
95 _logger.info("GAME", "===============================");
96}
97
99{
100 std::signal(SIGINT, requestStop);
101 std::signal(SIGTERM, requestStop);
102
104 _logStartup();
105
106 while (!g_stopRequested) {
107 try {
108 int timeout = _scheduler.msUntilNext();
109 PollResult pr = _clients.poll(timeout);
110
111 for (int id : pr.newConnections) _dispatcher.onNewConnection(id);
112 for (auto& [connId, line] : pr.lines) _dispatcher.dispatch(connId, line);
113 for (int id : pr.disconnectedIds) {
116 }
117
119 for (int id : _dispatcher.drainPendingDisconnects()) {
122 }
123
125 } catch (const std::exception& e) {
126 std::cerr << "[error] " << e.what() << "\n";
127 }
128 }
129
130 _logger.info("Server", "Shutdown signal received, closing server");
131}
void disconnect(int connectionId)
Close a client and drop its connection.
PollResult poll(int timeoutMs)
Run one poll cycle (blocks up to timeoutMs); returns this cycle's events.
void addNetworkObserver(INetworkObserver *observer)
Subscribe an observer to connect/disconnect/line events.
std::chrono::microseconds gameElapsed() const
Wall-clock time since server start (single source for stu and the win banner).
double gameTicks() const
Total game ticks elapsed (freq integrated over time). For the win banner.
void onNewConnection(int connectionId)
New socket connected: start its handshake.
void onDisconnect(int connectionId)
Socket dropped: clean up its queue and player.
void dispatch(int connectionId, const std::string &line)
Route one line from a client.
std::optional< GameClock::Stamp > teamJoin(const std::string &team) const
When team's first player joined, or nullopt if it never did.
std::vector< int > drainPendingDisconnects()
Take the list of ids the server should disconnect this cycle.
static std::unique_ptr< FileSink > forRun(const std::string &tag, LogLevel minLevel=LogLevel::Debug)
Definition FileSink.cpp:13
void broadcast(const std::string &msg)
void info(std::string_view component, std::string_view msg)
Definition Logger.cpp:55
void setSink(std::unique_ptr< ILogSink > sink)
Definition Logger.cpp:39
void schedule(std::chrono::milliseconds delay, std::function< void()> cb)
Definition Scheduler.cpp:3
void clear()
Drop all pending events (e.g. on game end, to freeze the world instantly).
Definition Scheduler.cpp:27
int msUntilNext() const
Milliseconds until the next event. Pass directly to poll() as timeout. Returns -1 if the queue is emp...
Definition Scheduler.cpp:18
void tick()
Definition Scheduler.cpp:8
bool _gameOverHandled
Definition Server.hpp:46
CommandDispatcher _dispatcher
Definition Server.hpp:45
void _logStartup() const
Definition Server.cpp:53
Server(const ServerConfig &config)
Definition Server.cpp:23
GuiNotifier _notifier
Definition Server.hpp:41
ClientManager _clients
Definition Server.hpp:39
static constexpr int RESPAWN_INTERVAL_MS
Resources respawn every 20 time units (subject spec).
Definition Server.hpp:35
Logger _logger
Definition Server.hpp:42
ServerConfig _config
Definition Server.hpp:37
void _scheduleRespawn()
(Re)schedule the periodic world resource respawn.
Definition Server.cpp:45
void run()
Run the game loop until the process is stopped.
Definition Server.cpp:98
LogObserver _logObserver
Definition Server.hpp:43
World _world
Definition Server.hpp:40
Scheduler _scheduler
Definition Server.hpp:44
void _handleGameOver()
Freeze the world and log the winner banner. Runs once when a team wins.
Definition Server.cpp:63
const std::unordered_map< int, Player > & getPlayers() const
Definition World.cpp:402
void spawnInitialEggs(int countPerTeam)
Spawn countPerTeam eggs for every team at random tiles (server startup).
Definition World.cpp:208
void addWorldObserver(IWorldObserver *observer)
Definition World.cpp:420
std::vector< std::pair< int, int > > spawnResources()
Definition World.cpp:28
bool isGameEnded() const
True once a team has won. Game logic stops reacting to AI commands.
Definition World.cpp:417
const std::optional< std::string > & winner() const
Winning team name, set when isGameEnded() becomes true.
Definition World.cpp:418
std::string gwt(const std::string &team, int seconds, long long ticks)
volatile std::sig_atomic_t g_stopRequested
Definition Server.cpp:13
std::vector< int > disconnectedIds
std::vector< int > newConnections
std::vector< std::pair< int, std::string > > lines
Represents the configuration for the server application, described by:
Definition Args.hpp:17
int height
Definition Args.hpp:20
int clientsNb
Definition Args.hpp:22
std::vector< std::string > teamNames
Definition Args.hpp:21
int width
Definition Args.hpp:19
unsigned int seed
Definition Args.hpp:24