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