|
1 /* $Id$ */ |
|
2 |
|
3 #ifdef ENABLE_NETWORK |
|
4 |
|
5 #include "../../stdafx.h" |
|
6 #include "../../date.h" |
|
7 #include "../../debug.h" |
|
8 #include "../../macros.h" |
|
9 #include "../../newgrf_config.h" |
|
10 |
|
11 #include "os_abstraction.h" |
|
12 #include "config.h" |
|
13 #include "game.h" |
|
14 #include "packet.h" |
|
15 #include "udp.h" |
|
16 |
|
17 /** |
|
18 * @file udp.c Basic functions to receive and send UDP packets. |
|
19 */ |
|
20 |
|
21 /** |
|
22 * Send a packet over UDP |
|
23 * @param udp the socket to send over |
|
24 * @param p the packet to send |
|
25 * @param recv the receiver (target) of the packet |
|
26 */ |
|
27 void NetworkSendUDP_Packet(SOCKET udp, Packet *p, struct sockaddr_in *recv) |
|
28 { |
|
29 int res; |
|
30 |
|
31 NetworkSend_FillPacketSize(p); |
|
32 |
|
33 /* Send the buffer */ |
|
34 res = sendto(udp, p->buffer, p->size, 0, (struct sockaddr *)recv, sizeof(*recv)); |
|
35 |
|
36 /* Check for any errors, but ignore it otherwise */ |
|
37 if (res == -1) DEBUG(net, 1, "[udp] sendto failed with: %i", GET_LAST_ERROR()); |
|
38 } |
|
39 |
|
40 /** |
|
41 * Start listening on the given host and port. |
|
42 * @param udp the place where the (references to the) UDP are stored |
|
43 * @param host the host (ip) to listen on |
|
44 * @param port the port to listen on |
|
45 * @param broadcast whether to allow broadcast sending/receiving |
|
46 * @return true if the listening succeeded |
|
47 */ |
|
48 bool NetworkUDPListen(SOCKET *udp, uint32 host, uint16 port, bool broadcast) |
|
49 { |
|
50 struct sockaddr_in sin; |
|
51 |
|
52 /* Make sure socket is closed */ |
|
53 closesocket(*udp); |
|
54 |
|
55 *udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
|
56 if (*udp == INVALID_SOCKET) { |
|
57 DEBUG(net, 0, "[udp] failed to start UDP listener"); |
|
58 return false; |
|
59 } |
|
60 |
|
61 /* set nonblocking mode for socket */ |
|
62 { |
|
63 unsigned long blocking = 1; |
|
64 #ifndef BEOS_NET_SERVER |
|
65 ioctlsocket(*udp, FIONBIO, &blocking); |
|
66 #else |
|
67 setsockopt(*udp, SOL_SOCKET, SO_NONBLOCK, &blocking, NULL); |
|
68 #endif |
|
69 } |
|
70 |
|
71 sin.sin_family = AF_INET; |
|
72 /* Listen on all IPs */ |
|
73 sin.sin_addr.s_addr = host; |
|
74 sin.sin_port = htons(port); |
|
75 |
|
76 if (bind(*udp, (struct sockaddr*)&sin, sizeof(sin)) != 0) { |
|
77 DEBUG(net, 0, "[udp] bind failed on %s:%i", inet_ntoa(*(struct in_addr *)&host), port); |
|
78 return false; |
|
79 } |
|
80 |
|
81 if (broadcast) { |
|
82 /* Enable broadcast */ |
|
83 unsigned long val = 1; |
|
84 #ifndef BEOS_NET_SERVER // will work around this, some day; maybe. |
|
85 setsockopt(*udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val)); |
|
86 #endif |
|
87 } |
|
88 |
|
89 DEBUG(net, 1, "[udp] listening on port %s:%d", inet_ntoa(*(struct in_addr *)&host), port); |
|
90 |
|
91 return true; |
|
92 } |
|
93 |
|
94 /** |
|
95 * Receive a packet at UDP level |
|
96 * @param udp the socket to receive the packet on |
|
97 */ |
|
98 void NetworkUDPReceive(SOCKET udp) |
|
99 { |
|
100 struct sockaddr_in client_addr; |
|
101 socklen_t client_len; |
|
102 int nbytes; |
|
103 Packet p; |
|
104 int packet_len; |
|
105 |
|
106 packet_len = sizeof(p.buffer); |
|
107 client_len = sizeof(client_addr); |
|
108 |
|
109 /* Try to receive anything */ |
|
110 nbytes = recvfrom(udp, p.buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len); |
|
111 |
|
112 /* We got some bytes for the base header of the packet. |
|
113 * Assume we received the whole packet. */ |
|
114 if (nbytes > 2) { |
|
115 NetworkRecv_ReadPacketSize(&p); |
|
116 |
|
117 /* Put the position on the right place */ |
|
118 p.pos = 2; |
|
119 p.next = NULL; |
|
120 |
|
121 /* Handle the packet */ |
|
122 NetworkHandleUDPPacket(&p, &client_addr); |
|
123 } |
|
124 } |
|
125 |
|
126 |
|
127 /** |
|
128 * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet |
|
129 * @param p the packet to write the data to |
|
130 * @param c the configuration to write the GRF ID and MD5 checksum from |
|
131 */ |
|
132 void NetworkSend_GRFIdentifier(Packet *p, const GRFConfig *c) |
|
133 { |
|
134 uint j; |
|
135 NetworkSend_uint32(p, c->grfid); |
|
136 for (j = 0; j < sizeof(c->md5sum); j++) { |
|
137 NetworkSend_uint8 (p, c->md5sum[j]); |
|
138 } |
|
139 } |
|
140 |
|
141 /** |
|
142 * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet |
|
143 * @param cs the client state (for closing connect on out-of-bounds reading etc) |
|
144 * @param p the packet to read the data from |
|
145 * @param c the configuration to write the GRF ID and MD5 checksum to |
|
146 */ |
|
147 void NetworkRecv_GRFIdentifier(NetworkClientState *cs, Packet *p, GRFConfig *c) |
|
148 { |
|
149 uint j; |
|
150 c->grfid = NetworkRecv_uint32(cs, p); |
|
151 for (j = 0; j < sizeof(c->md5sum); j++) { |
|
152 c->md5sum[j] = NetworkRecv_uint8(cs, p); |
|
153 } |
|
154 } |
|
155 |
|
156 |
|
157 /** |
|
158 * Serializes the NetworkGameInfo struct to the packet |
|
159 * @param p the packet to write the data to |
|
160 * @param info the NetworkGameInfo struct to serialize |
|
161 */ |
|
162 void NetworkSend_NetworkGameInfo(Packet *p, const NetworkGameInfo *info) |
|
163 { |
|
164 NetworkSend_uint8 (p, NETWORK_GAME_INFO_VERSION); |
|
165 |
|
166 /* |
|
167 * Please observe the order. |
|
168 * The parts must be read in the same order as they are sent! |
|
169 */ |
|
170 |
|
171 |
|
172 /* NETWORK_GAME_INFO_VERSION = 4 */ |
|
173 { |
|
174 /* Only send the GRF Identification (GRF_ID and MD5 checksum) of |
|
175 * the GRFs that are needed, i.e. the ones that the server has |
|
176 * selected in the NewGRF GUI and not the ones that are used due |
|
177 * to the fact that they are in [newgrf-static] in openttd.cfg */ |
|
178 const GRFConfig *c; |
|
179 uint count = 0; |
|
180 |
|
181 /* Count number of GRFs to send information about */ |
|
182 for (c = info->grfconfig; c != NULL; c = c->next) { |
|
183 if (!HASBIT(c->flags, GCF_STATIC)) count++; |
|
184 } |
|
185 NetworkSend_uint8 (p, count); // Send number of GRFs |
|
186 |
|
187 /* Send actual GRF Identifications */ |
|
188 for (c = info->grfconfig; c != NULL; c = c->next) { |
|
189 if (!HASBIT(c->flags, GCF_STATIC)) NetworkSend_GRFIdentifier(p, c); |
|
190 } |
|
191 } |
|
192 |
|
193 /* NETWORK_GAME_INFO_VERSION = 3 */ |
|
194 NetworkSend_uint32(p, info->game_date); |
|
195 NetworkSend_uint32(p, info->start_date); |
|
196 |
|
197 /* NETWORK_GAME_INFO_VERSION = 2 */ |
|
198 NetworkSend_uint8 (p, info->companies_max); |
|
199 NetworkSend_uint8 (p, info->companies_on); |
|
200 NetworkSend_uint8 (p, info->spectators_max); |
|
201 |
|
202 /* NETWORK_GAME_INFO_VERSION = 1 */ |
|
203 NetworkSend_string(p, info->server_name); |
|
204 NetworkSend_string(p, info->server_revision); |
|
205 NetworkSend_uint8 (p, info->server_lang); |
|
206 NetworkSend_uint8 (p, info->use_password); |
|
207 NetworkSend_uint8 (p, info->clients_max); |
|
208 NetworkSend_uint8 (p, info->clients_on); |
|
209 NetworkSend_uint8 (p, info->spectators_on); |
|
210 NetworkSend_string(p, info->map_name); |
|
211 NetworkSend_uint16(p, info->map_width); |
|
212 NetworkSend_uint16(p, info->map_height); |
|
213 NetworkSend_uint8 (p, info->map_set); |
|
214 NetworkSend_uint8 (p, info->dedicated); |
|
215 } |
|
216 |
|
217 /** |
|
218 * Deserializes the NetworkGameInfo struct from the packet |
|
219 * @param cs the client state (for closing connect on out-of-bounds reading etc) |
|
220 * @param p the packet to read the data from |
|
221 * @param info the NetworkGameInfo to deserialize into |
|
222 */ |
|
223 void NetworkRecv_NetworkGameInfo(NetworkClientState *cs, Packet *p, NetworkGameInfo *info) |
|
224 { |
|
225 info->game_info_version = NetworkRecv_uint8(cs, p); |
|
226 |
|
227 /* |
|
228 * Please observe the order. |
|
229 * The parts must be read in the same order as they are sent! |
|
230 */ |
|
231 |
|
232 switch (info->game_info_version) { |
|
233 case 4: { |
|
234 GRFConfig *c, **dst = &info->grfconfig; |
|
235 uint i; |
|
236 uint num_grfs = NetworkRecv_uint8(cs, p); |
|
237 |
|
238 for (i = 0; i < num_grfs; i++) { |
|
239 c = calloc(1, sizeof(*c)); |
|
240 NetworkRecv_GRFIdentifier(cs, p, c); |
|
241 HandleIncomingNetworkGameInfoGRFConfig(c); |
|
242 |
|
243 /* Append GRFConfig to the list */ |
|
244 *dst = c; |
|
245 dst = &c->next; |
|
246 } |
|
247 } /* Fallthrough */ |
|
248 case 3: |
|
249 info->game_date = NetworkRecv_uint32(cs, p); |
|
250 info->start_date = NetworkRecv_uint32(cs, p); |
|
251 /* Fallthrough */ |
|
252 case 2: |
|
253 info->companies_max = NetworkRecv_uint8 (cs, p); |
|
254 info->companies_on = NetworkRecv_uint8 (cs, p); |
|
255 info->spectators_max = NetworkRecv_uint8 (cs, p); |
|
256 /* Fallthrough */ |
|
257 case 1: |
|
258 NetworkRecv_string(cs, p, info->server_name, sizeof(info->server_name)); |
|
259 NetworkRecv_string(cs, p, info->server_revision, sizeof(info->server_revision)); |
|
260 info->server_lang = NetworkRecv_uint8 (cs, p); |
|
261 info->use_password = NetworkRecv_uint8 (cs, p); |
|
262 info->clients_max = NetworkRecv_uint8 (cs, p); |
|
263 info->clients_on = NetworkRecv_uint8 (cs, p); |
|
264 info->spectators_on = NetworkRecv_uint8 (cs, p); |
|
265 if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier |
|
266 info->game_date = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; |
|
267 info->start_date = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR; |
|
268 } |
|
269 NetworkRecv_string(cs, p, info->map_name, sizeof(info->map_name)); |
|
270 info->map_width = NetworkRecv_uint16(cs, p); |
|
271 info->map_height = NetworkRecv_uint16(cs, p); |
|
272 info->map_set = NetworkRecv_uint8 (cs, p); |
|
273 info->dedicated = NetworkRecv_uint8 (cs, p); |
|
274 } |
|
275 } |
|
276 |
|
277 #endif /* ENABLE_NETWORK */ |