1 #include <assert.h>
2
3 #include <boost/array.hpp>
4 #include <boost/asio.hpp>
5 #include <boost/bind.hpp>
6 #include <boost/regex.hpp>
7 #include <boost/shared_ptr.hpp>
8 #include <boost/thread.hpp>
9
10 #include <string>
11 #include <vector>
12
13 #include <iostream>
14 #include <boost/cstdint.hpp>
15
16 #include "asserts.hpp"
17 #include "foreach.hpp"
18
19 using boost::asio::ip::tcp;
20 using boost::asio::ip::udp;
21
22 typedef boost::shared_ptr<tcp::socket> socket_ptr;
23 typedef boost::shared_ptr<boost::array<char, 1024> > buffer_ptr;
24
25 class server
26 {
27 public:
server(boost::asio::io_service & io_service)28 explicit server(boost::asio::io_service& io_service)
29 : acceptor_(io_service, tcp::endpoint(tcp::v4(), 17002)),
30 next_id_(0),
31 udp_socket_(io_service, udp::endpoint(udp::v4(), 17001))
32 {
33 start_accept();
34 start_udp_receive();
35 }
36
37 private:
start_accept()38 void start_accept()
39 {
40 #if BOOST_VERSION < 107000
41 socket_ptr socket(new tcp::socket(acceptor_.get_io_service()));
42 #else
43 socket_ptr socket(new tcp::socket(acceptor_.get_executor()));
44 #endif
45 acceptor_.async_accept(*socket, boost::bind(&server::handle_accept, this, socket, boost::asio::placeholders::error));
46 }
47
handle_accept(socket_ptr socket,const boost::system::error_code & error)48 void handle_accept(socket_ptr socket, const boost::system::error_code& error)
49 {
50 if(!error) {
51 SocketInfo info;
52 info.id = next_id_++;
53
54 boost::array<char, 4> buf;
55 memcpy(&buf[0], &info.id, 4);
56
57 boost::asio::async_write(*socket, boost::asio::buffer(buf),
58 boost::bind(&server::handle_send, this, socket, _1, _2));
59
60 sockets_info_[socket] = info;
61 id_to_socket_[info.id] = socket;
62
63 sockets_.push_back(socket);
64 start_receive(socket);
65 start_accept();
66 } else {
67 std::cerr << "ERROR IN ACCEPT!\n";
68 }
69 }
70
handle_send(socket_ptr socket,const boost::system::error_code & e,size_t nbytes)71 void handle_send(socket_ptr socket, const boost::system::error_code& e, size_t nbytes)
72 {
73 if(e) {
74 disconnect(socket);
75 }
76 }
77
start_receive(socket_ptr socket)78 void start_receive(socket_ptr socket)
79 {
80 buffer_ptr buf(new boost::array<char, 1024>);
81 socket->async_read_some(boost::asio::buffer(*buf), boost::bind(&server::handle_receive, this, socket, buf, _1, _2));
82 }
83
handle_receive(socket_ptr socket,buffer_ptr buf,const boost::system::error_code & e,size_t nbytes)84 void handle_receive(socket_ptr socket, buffer_ptr buf, const boost::system::error_code& e, size_t nbytes)
85 {
86 if(!e) {
87 std::string str(buf->data(), buf->data() + nbytes);
88 std::cerr << "RECEIVE {{{" << str << "}}}\n";
89
90 static boost::regex ready("READY/(.+)/(\\d+)/(.* \\d+)");
91
92 boost::cmatch match;
93 if(boost::regex_match(str.c_str(), match, ready)) {
94 std::string level_id(match[1].first, match[1].second);
95
96 GameInfoPtr& game = games_[level_id];
97 if(!game) {
98 game.reset(new GameInfo);
99 }
100
101 SocketInfo& info = sockets_info_[socket];
102 info.local_addr = std::string(match[3].first, match[3].second);
103 if(info.game) {
104 //if the player is already in a game, remove them from it.
105 std::vector<socket_ptr>& v = info.game->players;
106 v.erase(std::remove(v.begin(), v.end(), socket), v.end());
107 }
108
109 info.game = game;
110
111 fprintf(stderr, "ADDING PLAYER TO GAME: %p\n", socket.get());
112 game->players.push_back(socket);
113
114
115 const size_t nplayers = atoi(match[2].first);
116 game->nplayers = nplayers;
117
118 }
119
120 start_receive(socket);
121 } else {
122 disconnect(socket);
123 }
124 }
125
disconnect(socket_ptr socket)126 void disconnect(socket_ptr socket) {
127
128 SocketInfo& info = sockets_info_[socket];
129 if(info.game) {
130 std::vector<socket_ptr>& v = info.game->players;
131 v.erase(std::remove(v.begin(), v.end(), socket), v.end());
132 }
133
134 std::cerr << "CLOSING SOCKET: ";
135 socket->close();
136 id_to_socket_.erase(sockets_info_[socket].id);
137 sockets_.erase(std::remove(sockets_.begin(), sockets_.end(), socket), sockets_.end());
138
139 sockets_info_.erase(socket);
140 }
141
142 tcp::acceptor acceptor_;
143
144 std::vector<socket_ptr> sockets_;
145
146 typedef boost::shared_ptr<udp::endpoint> udp_endpoint_ptr;
147
148 struct GameInfo;
149 typedef boost::shared_ptr<GameInfo> GameInfoPtr;
150
151 struct SocketInfo {
152 uint32_t id;
153 udp_endpoint_ptr udp_endpoint;
154 GameInfoPtr game;
155 std::string local_addr;
156 };
157
158 std::map<socket_ptr, SocketInfo> sockets_info_;
159 std::map<uint32_t, socket_ptr> id_to_socket_;
160
161 struct GameInfo {
GameInfoserver::GameInfo162 GameInfo() : started(false) {}
163 std::vector<socket_ptr> players;
164 size_t nplayers;
165 bool started;
166 };
167
168 std::map<std::string, GameInfoPtr> games_;
169
170 uint32_t next_id_;
171
start_udp_receive()172 void start_udp_receive() {
173 udp_endpoint_ptr endpoint(new udp::endpoint);
174 udp_socket_.async_receive_from(
175 boost::asio::buffer(udp_buf_), *endpoint,
176 boost::bind(&server::handle_udp_receive, this, endpoint, _1, _2));
177 }
178
handle_udp_receive(udp_endpoint_ptr endpoint,const boost::system::error_code & error,size_t len)179 void handle_udp_receive(udp_endpoint_ptr endpoint, const boost::system::error_code& error, size_t len)
180 {
181 fprintf(stderr, "RECEIVED UDP PACKET: %u\n", len);
182 if(len >= 5) {
183 uint32_t id;
184 memcpy(&id, &udp_buf_[1], 4);
185 std::map<uint32_t, socket_ptr>::iterator socket_it = id_to_socket_.find(id);
186 if(socket_it != id_to_socket_.end()) {
187 assert(sockets_info_.count(socket_it->second));
188 sockets_info_[socket_it->second].udp_endpoint = endpoint;
189
190 GameInfoPtr& game = sockets_info_[socket_it->second].game;
191 if(udp_buf_[0] == 'Z' && game.get() != NULL && !game->started && game->players.size() >= game->nplayers) {
192 bool have_sockets = true;
193 foreach(const socket_ptr& sock, game->players) {
194 const SocketInfo& info = sockets_info_[sock];
195 if(info.udp_endpoint.get() == NULL) {
196 have_sockets = false;
197 }
198 }
199
200 if(have_sockets) {
201
202 foreach(socket_ptr socket, game->players) {
203 const SocketInfo& send_socket_info = sockets_info_[socket];
204
205 std::ostringstream msg;
206 msg << "START " << game->players.size() << "\n";
207 foreach(socket_ptr s, game->players) {
208 if(s == socket) {
209 msg << "SLOT\n";
210 continue;
211 }
212
213 const SocketInfo& sock_info = sockets_info_[s];
214 if(send_socket_info.udp_endpoint->address().to_string() != sock_info.udp_endpoint->address().to_string()) {
215 //the hosts are not from the same address,
216 //so send them each other's network address.
217 msg << sock_info.udp_endpoint->address().to_string().c_str() << " " << sock_info.udp_endpoint->port() << "\n";
218 } else {
219 //the hosts are from the same address,
220 //which means they are likely behind the
221 //same NAT device. Send them their local
222 //addresses, behind their devices.
223 msg << sock_info.local_addr << "\n";
224 }
225 }
226
227 const std::string msg_str = msg.str();
228
229 boost::asio::async_write(*socket, boost::asio::buffer(msg_str),
230 boost::bind(&server::handle_send, this, socket, _1, _2));
231 }
232
233 game->started = true;
234 for(std::map<std::string, GameInfoPtr>::iterator i = games_.begin(); i != games_.end(); ++i) {
235 if(game == i->second) {
236 i->second.reset();
237 }
238 }
239 }
240 }
241
242 if(udp_buf_[0] != 'Z') {
243 GameInfoPtr game = sockets_info_[socket_it->second].game;
244
245 if(game.get() != NULL) {
246 for(int n = 0; n != game->players.size(); ++n) {
247 if(game->players[n] == socket_it->second) {
248 continue;
249 }
250
251 std::cerr << "GOT FROM: " << endpoint->port() << " RELAYING TO...\n";
252 std::map<socket_ptr, SocketInfo>::iterator socket_info = sockets_info_.find(game->players[n]);
253 if(socket_info != sockets_info_.end()) {
254 std::cerr << " RELAY TO " << socket_info->second.udp_endpoint->port() << "\n";
255 udp_socket_.async_send_to(boost::asio::buffer(&udp_buf_[0], len), *socket_info->second.udp_endpoint,
256 boost::bind(&server::handle_udp_send, this, socket_info->second.udp_endpoint, _1, _2));
257 }
258 }
259 }
260 }
261 }
262 }
263 start_udp_receive();
264 }
265
handle_udp_send(udp_endpoint_ptr endpoint,const boost::system::error_code & error,size_t len)266 void handle_udp_send(udp_endpoint_ptr endpoint, const boost::system::error_code& error, size_t len)
267 {
268 if(error) {
269 fprintf(stderr, "ERROR IN UDP SEND!\n");
270 } else {
271 fprintf(stderr, "UDP: SENT %d BYTES\n", (int)len);
272 }
273 }
274
275 udp::socket udp_socket_;
276 boost::array<char, 1024> udp_buf_;
277 };
278
main(int argc,char ** argv)279 int main(int argc, char** argv)
280 {
281 boost::asio::io_service io_service;
282
283 server srv(io_service);
284 io_service.run();
285 }
286