-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.cpp
More file actions
162 lines (133 loc) · 4.66 KB
/
server.cpp
File metadata and controls
162 lines (133 loc) · 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include "Connection.hpp"
#include "hex_dump.hpp"
#include "Game.hpp"
#include <chrono>
#include <stdexcept>
#include <iostream>
#include <cassert>
#include <unordered_map>
#ifdef _WIN32
extern "C" { uint32_t GetACP(); }
#endif
int main(int argc, char **argv) {
#ifdef _WIN32
{ //when compiled on windows, check that code page is forced to utf-8 (makes file loading/saving work right):
//see: https://docs.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page
uint32_t code_page = GetACP();
if (code_page == 65001) {
std::cout << "Code page is properly set to UTF-8." << std::endl;
} else {
std::cout << "WARNING: code page is set to " << code_page << " instead of 65001 (UTF-8). Some file handling functions may fail." << std::endl;
}
}
//when compiled on windows, unhandled exceptions don't have their message printed, which can make debugging simple issues difficult.
try {
#endif
//------------ argument parsing ------------
if (argc < 2) {
std::cerr << "Usage:\n\t./server [<host>] <port>" << std::endl;
return 1;
}
std::string host;
std::string port;
if (argc >= 3) {
host = argv[1];
port = argv[2];
} else {
host = "127.0.0.1";
port = argv[1];
}
//------------ initialization ------------
Server server(host, port);
//------------ main loop ------------
//keep track of which connection is controlling which player:
std::unordered_map< Connection *, Player * > connection_to_player;
std::unordered_map< int8_t, Connection * > player_to_connection;
//keep track of game state:
Game game;
while (true) {
static auto next_tick = std::chrono::steady_clock::now() + std::chrono::duration< double >(Game::Tick);
//process incoming data from clients until a tick has elapsed:
while (true) {
auto now = std::chrono::steady_clock::now();
double remain = std::chrono::duration< double >(next_tick - now).count();
if (remain < 0.0) {
next_tick += std::chrono::duration< double >(Game::Tick);
break;
}
//helper used on client close (due to quit) and server close (due to error):
auto remove_connection = [&](Connection *c) {
// TODO: fix this
auto f = connection_to_player.find(c);
assert(f != connection_to_player.end());
Player *player = connection_to_player[c];
player_to_connection.erase(player->player_id);
game.remove_player(f->second);
connection_to_player.erase(f);
};
server.poll([&](Connection *c, Connection::Event evt){
if (evt == Connection::OnOpen) {
//client connected:
//create some player info for them:
Player *new_player = game.spawn_player();
connection_to_player.emplace(c, new_player);
player_to_connection.emplace(new_player->player_id, c);
game.send_message(c, new_player, MESSAGE::SERVER_INIT);
} else if (evt == Connection::OnClose) {
//client disconnected:
remove_connection(c);
} else { assert(evt == Connection::OnRecv);
//got data from client:
//std::cout << "current buffer:\n" << hex_dump(c->recv_buffer); std::cout.flush(); //DEBUG
//look up in players list:
auto f = connection_to_player.find(c);
assert(f != connection_to_player.end());
Player &player = *f->second;
//handle messages from client:
try {
bool handled_message;
do {
handled_message = false;
if (game.recv_message(c, &player, true) != MESSAGE::MSG_NONE) handled_message = true;
//TODO: extend for more message types as needed
} while (handled_message);
} catch (std::exception const &e) {
std::cout << "Disconnecting client:" << e.what() << std::endl;
c->close();
remove_connection(c);
}
}
}, remain);
}
// send messages in queue
while (game.message_queue.size() != 0) {
MessageInfo message = game.message_queue.front();
// send it to the other player
Connection *c = player_to_connection[!message.player_id];
game.send_message(c, &game.players[message.player_id], message.tag);
game.message_queue.pop_front();
}
// complete updates related to the messages (like input sync)
while (game.message_queue.size() != 0) {
MessageInfo message = game.message_queue.front();
game.process_action(&game.players[message.player_id], message.tag);
game.message_queue.pop_front();
}
//update current game state
game.update(Game::Tick);
//send updated game state to all clients
// for (auto &[c, player] : connection_to_player) {
// game.send_message(c, player);
// }
}
return 0;
#ifdef _WIN32
} catch (std::exception const &e) {
std::cerr << "Unhandled exception:\n" << e.what() << std::endl;
return 1;
} catch (...) {
std::cerr << "Unhandled exception (unknown type)." << std::endl;
throw;
}
#endif
}