1 //
2 // Copyright(C) 2005-2014 Simon Howard
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // DESCRIPTION:
15 // Networking module which uses SDL_net
16 //
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21
22 #include "doomtype.h"
23 #include "i_system.h"
24 #include "m_argv.h"
25 #include "m_misc.h"
26 #include "net_defs.h"
27 #include "net_io.h"
28 #include "net_packet.h"
29 #include "net_sdl.h"
30 #include "z_zone.h"
31
32 //
33 // NETWORKING
34 //
35
36 #include <SDL_net.h>
37
38 #define DEFAULT_PORT 2342
39
40 static boolean initted = false;
41 static int port = DEFAULT_PORT;
42 static UDPsocket udpsocket;
43 static UDPpacket *recvpacket;
44
45 typedef struct
46 {
47 net_addr_t net_addr;
48 IPaddress sdl_addr;
49 } addrpair_t;
50
51 static addrpair_t **addr_table;
52 static int addr_table_size = -1;
53
54 // Initializes the address table
55
NET_SDL_InitAddrTable(void)56 static void NET_SDL_InitAddrTable(void)
57 {
58 addr_table_size = 16;
59
60 addr_table = Z_Malloc(sizeof(addrpair_t *) * addr_table_size,
61 PU_STATIC, 0);
62 memset(addr_table, 0, sizeof(addrpair_t *) * addr_table_size);
63 }
64
AddressesEqual(IPaddress * a,IPaddress * b)65 static boolean AddressesEqual(IPaddress *a, IPaddress *b)
66 {
67 return a->host == b->host
68 && a->port == b->port;
69 }
70
71 // Finds an address by searching the table. If the address is not found,
72 // it is added to the table.
73
NET_SDL_FindAddress(IPaddress * addr)74 static net_addr_t *NET_SDL_FindAddress(IPaddress *addr)
75 {
76 addrpair_t *new_entry;
77 int empty_entry = -1;
78 int i;
79
80 if (addr_table_size < 0)
81 {
82 NET_SDL_InitAddrTable();
83 }
84
85 for (i=0; i<addr_table_size; ++i)
86 {
87 if (addr_table[i] != NULL
88 && AddressesEqual(addr, &addr_table[i]->sdl_addr))
89 {
90 return &addr_table[i]->net_addr;
91 }
92
93 if (empty_entry < 0 && addr_table[i] == NULL)
94 empty_entry = i;
95 }
96
97 // Was not found in list. We need to add it.
98
99 // Is there any space in the table? If not, increase the table size
100
101 if (empty_entry < 0)
102 {
103 addrpair_t **new_addr_table;
104 int new_addr_table_size;
105
106 // after reallocing, we will add this in as the first entry
107 // in the new block of memory
108
109 empty_entry = addr_table_size;
110
111 // allocate a new array twice the size, init to 0 and copy
112 // the existing table in. replace the old table.
113
114 new_addr_table_size = addr_table_size * 2;
115 new_addr_table = Z_Malloc(sizeof(addrpair_t *) * new_addr_table_size,
116 PU_STATIC, 0);
117 memset(new_addr_table, 0, sizeof(addrpair_t *) * new_addr_table_size);
118 memcpy(new_addr_table, addr_table,
119 sizeof(addrpair_t *) * addr_table_size);
120 Z_Free(addr_table);
121 addr_table = new_addr_table;
122 addr_table_size = new_addr_table_size;
123 }
124
125 // Add a new entry
126
127 new_entry = Z_Malloc(sizeof(addrpair_t), PU_STATIC, 0);
128
129 new_entry->sdl_addr = *addr;
130 new_entry->net_addr.refcount = 0;
131 new_entry->net_addr.handle = &new_entry->sdl_addr;
132 new_entry->net_addr.module = &net_sdl_module;
133
134 addr_table[empty_entry] = new_entry;
135
136 return &new_entry->net_addr;
137 }
138
NET_SDL_FreeAddress(net_addr_t * addr)139 static void NET_SDL_FreeAddress(net_addr_t *addr)
140 {
141 int i;
142
143 for (i=0; i<addr_table_size; ++i)
144 {
145 if (addr == &addr_table[i]->net_addr)
146 {
147 Z_Free(addr_table[i]);
148 addr_table[i] = NULL;
149 return;
150 }
151 }
152
153 I_Error("NET_SDL_FreeAddress: Attempted to remove an unused address!");
154 }
155
NET_SDL_InitClient(void)156 static boolean NET_SDL_InitClient(void)
157 {
158 int p;
159
160 if (initted)
161 return true;
162
163 //!
164 // @category net
165 // @arg <n>
166 //
167 // Use the specified UDP port for communications, instead of
168 // the default (2342).
169 //
170
171 p = M_CheckParmWithArgs("-port", 1);
172 if (p > 0)
173 port = atoi(myargv[p+1]);
174
175 SDLNet_Init();
176
177 udpsocket = SDLNet_UDP_Open(0);
178
179 if (udpsocket == NULL)
180 {
181 I_Error("NET_SDL_InitClient: Unable to open a socket!");
182 }
183
184 recvpacket = SDLNet_AllocPacket(1500);
185
186 #ifdef DROP_PACKETS
187 srand(time(NULL));
188 #endif
189
190 initted = true;
191
192 return true;
193 }
194
NET_SDL_InitServer(void)195 static boolean NET_SDL_InitServer(void)
196 {
197 int p;
198
199 if (initted)
200 return true;
201
202 p = M_CheckParmWithArgs("-port", 1);
203 if (p > 0)
204 port = atoi(myargv[p+1]);
205
206 SDLNet_Init();
207
208 udpsocket = SDLNet_UDP_Open(port);
209
210 if (udpsocket == NULL)
211 {
212 I_Error("NET_SDL_InitServer: Unable to bind to port %i", port);
213 }
214
215 recvpacket = SDLNet_AllocPacket(1500);
216 #ifdef DROP_PACKETS
217 srand(time(NULL));
218 #endif
219
220 initted = true;
221
222 return true;
223 }
224
NET_SDL_SendPacket(net_addr_t * addr,net_packet_t * packet)225 static void NET_SDL_SendPacket(net_addr_t *addr, net_packet_t *packet)
226 {
227 UDPpacket sdl_packet;
228 IPaddress ip;
229
230 if (addr == &net_broadcast_addr)
231 {
232 SDLNet_ResolveHost(&ip, NULL, port);
233 ip.host = INADDR_BROADCAST;
234 }
235 else
236 {
237 ip = *((IPaddress *) addr->handle);
238 }
239
240 #if 0
241 {
242 static int this_second_sent = 0;
243 static int lasttime;
244
245 this_second_sent += packet->len + 64;
246
247 if (I_GetTime() - lasttime > TICRATE)
248 {
249 printf("%i bytes sent in the last second\n", this_second_sent);
250 lasttime = I_GetTime();
251 this_second_sent = 0;
252 }
253 }
254 #endif
255
256 #ifdef DROP_PACKETS
257 if ((rand() % 4) == 0)
258 return;
259 #endif
260
261 sdl_packet.channel = 0;
262 sdl_packet.data = packet->data;
263 sdl_packet.len = packet->len;
264 sdl_packet.address = ip;
265
266 if (!SDLNet_UDP_Send(udpsocket, -1, &sdl_packet))
267 {
268 I_Error("NET_SDL_SendPacket: Error transmitting packet: %s",
269 SDLNet_GetError());
270 }
271 }
272
NET_SDL_RecvPacket(net_addr_t ** addr,net_packet_t ** packet)273 static boolean NET_SDL_RecvPacket(net_addr_t **addr, net_packet_t **packet)
274 {
275 int result;
276
277 result = SDLNet_UDP_Recv(udpsocket, recvpacket);
278
279 if (result < 0)
280 {
281 I_Error("NET_SDL_RecvPacket: Error receiving packet: %s",
282 SDLNet_GetError());
283 }
284
285 // no packets received
286
287 if (result == 0)
288 return false;
289
290 // Put the data into a new packet structure
291
292 *packet = NET_NewPacket(recvpacket->len);
293 memcpy((*packet)->data, recvpacket->data, recvpacket->len);
294 (*packet)->len = recvpacket->len;
295
296 // Address
297
298 *addr = NET_SDL_FindAddress(&recvpacket->address);
299
300 return true;
301 }
302
NET_SDL_AddrToString(net_addr_t * addr,char * buffer,int buffer_len)303 void NET_SDL_AddrToString(net_addr_t *addr, char *buffer, int buffer_len)
304 {
305 IPaddress *ip;
306 uint32_t host;
307 uint16_t port;
308
309 ip = (IPaddress *) addr->handle;
310 host = SDLNet_Read32(&ip->host);
311 port = SDLNet_Read16(&ip->port);
312
313 M_snprintf(buffer, buffer_len, "%i.%i.%i.%i",
314 (host >> 24) & 0xff, (host >> 16) & 0xff,
315 (host >> 8) & 0xff, host & 0xff);
316
317 // If we are using the default port we just need to show the IP address,
318 // but otherwise we need to include the port. This is important because
319 // we use the string representation in the setup tool to provided an
320 // address to connect to.
321 if (port != DEFAULT_PORT)
322 {
323 char portbuf[10];
324 M_snprintf(portbuf, sizeof(portbuf), ":%i", port);
325 M_StringConcat(buffer, portbuf, buffer_len);
326 }
327 }
328
NET_SDL_ResolveAddress(const char * address)329 net_addr_t *NET_SDL_ResolveAddress(const char *address)
330 {
331 IPaddress ip;
332 char *addr_hostname;
333 int addr_port;
334 int result;
335 char *colon;
336
337 colon = strchr(address, ':');
338
339 addr_hostname = M_StringDuplicate(address);
340 if (colon != NULL)
341 {
342 addr_hostname[colon - address] = '\0';
343 addr_port = atoi(colon + 1);
344 }
345 else
346 {
347 addr_port = port;
348 }
349
350 result = SDLNet_ResolveHost(&ip, addr_hostname, addr_port);
351
352 free(addr_hostname);
353
354 if (result)
355 {
356 // unable to resolve
357
358 return NULL;
359 }
360 else
361 {
362 return NET_SDL_FindAddress(&ip);
363 }
364 }
365
366 // Complete module
367
368 net_module_t net_sdl_module =
369 {
370 NET_SDL_InitClient,
371 NET_SDL_InitServer,
372 NET_SDL_SendPacket,
373 NET_SDL_RecvPacket,
374 NET_SDL_AddrToString,
375 NET_SDL_FreeAddress,
376 NET_SDL_ResolveAddress,
377 };
378
379