2
* OpenTyrian: A modern cross-platform port of Tyrian
3
* Copyright (C) 2007-2009 The OpenTyrian Development Team
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
37
* When I wrote this code I thought it was wonderful... that thought was very
38
* wrong. It works, but good luck understanding how... I don't anymore.
40
* Hopefully it'll be rewritten some day.
43
#define NET_VERSION 2 // increment whenever networking changes might create incompatability
44
#define NET_PORT 1333 // UDP
46
#define NET_PACKET_SIZE 256
47
#define NET_PACKET_QUEUE 16
49
#define NET_RETRY 640 // ticks to wait for packet acknowledgement before resending
50
#define NET_RESEND 320 // ticks to wait before requesting unreceived game packet
51
#define NET_KEEP_ALIVE 1600 // ticks to wait between keep-alive packets
52
#define NET_TIME_OUT 16000 // ticks to wait before considering connection dead
54
bool isNetworkGame = false;
55
int network_delay = 1 + 1; // minimum is 1 + 0
57
char *network_opponent_host = NULL;
59
Uint16 network_player_port = NET_PORT,
60
network_opponent_port = NET_PORT;
62
static char empty_string[] = "";
63
char *network_player_name = empty_string,
64
*network_opponent_name = empty_string;
67
static UDPsocket socket;
70
UDPpacket *packet_out_temp;
71
static UDPpacket *packet_temp;
73
UDPpacket *packet_in[NET_PACKET_QUEUE] = { NULL },
74
*packet_out[NET_PACKET_QUEUE] = { NULL };
76
static Uint16 last_out_sync = 0, queue_in_sync = 0, queue_out_sync = 0, last_ack_sync = 0;
77
static Uint32 last_in_tick = 0, last_out_tick = 0;
79
UDPpacket *packet_state_in[NET_PACKET_QUEUE] = { NULL };
80
static UDPpacket *packet_state_in_xor[NET_PACKET_QUEUE] = { NULL };
81
UDPpacket *packet_state_out[NET_PACKET_QUEUE] = { NULL };
83
static Uint16 last_state_in_sync = 0, last_state_out_sync = 0;
84
static Uint32 last_state_in_tick = 0;
86
static bool net_initialized = false;
87
static bool connected = false, quit = false;
90
uint thisPlayerNum = 0; /* Player number on this PC (1 or 2) */
92
JE_boolean haltGame = false;
96
/* Special Requests */
97
JE_boolean pauseRequest, skipLevelRequest, helpRequest, nortShipRequest;
98
JE_boolean yourInGameMenuRequest, inGameMenuRequest;
101
static void packet_copy( UDPpacket *dst, UDPpacket *src )
103
void *temp = dst->data;
104
memcpy(dst, src, sizeof(*dst));
106
memcpy(dst->data, src->data, src->len);
109
static void packets_shift_up( UDPpacket **packet, int max_packets )
113
SDLNet_FreePacket(packet[0]);
115
for (int i = 0; i < max_packets - 1; i++)
117
packet[i] = packet[i + 1];
119
packet[max_packets - 1] = NULL;
122
static void packets_shift_down( UDPpacket **packet, int max_packets )
124
if (packet[max_packets - 1])
126
SDLNet_FreePacket(packet[max_packets - 1]);
128
for (int i = max_packets - 1; i > 0; i--)
130
packet[i] = packet[i - 1];
135
// prepare new packet for sending
136
void network_prepare( Uint16 type )
138
SDLNet_Write16(type, &packet_out_temp->data[0]);
139
SDLNet_Write16(last_out_sync, &packet_out_temp->data[2]);
142
// send packet but don't expect acknoledgment of delivery
143
static bool network_send_no_ack( int len )
145
packet_out_temp->len = len;
147
if (!SDLNet_UDP_Send(socket, 0, packet_out_temp))
149
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
156
// send packet and place it in queue to be acknowledged
157
bool network_send( int len )
159
bool temp = network_send_no_ack(len);
161
Uint16 i = last_out_sync - queue_out_sync;
162
if (i < NET_PACKET_QUEUE)
164
packet_out[i] = SDLNet_AllocPacket(NET_PACKET_SIZE);
165
packet_copy(packet_out[i], packet_out_temp);
167
// connection is probably bad now
168
fprintf(stderr, "warning: outbound packet queue overflow\n");
174
if (network_is_sync())
175
last_out_tick = SDL_GetTicks();
180
// send acknowledgement packet
181
static int network_acknowledge( Uint16 sync )
183
SDLNet_Write16(PACKET_ACKNOWLEDGE, &packet_out_temp->data[0]);
184
SDLNet_Write16(sync, &packet_out_temp->data[2]);
185
network_send_no_ack(4);
191
static bool network_is_alive( void )
193
return (SDL_GetTicks() - last_in_tick < NET_TIME_OUT || SDL_GetTicks() - last_state_in_tick < NET_TIME_OUT);
196
// poll for new packets received, check that connection is alive, resend queued packets if necessary
197
int network_check( void )
199
if (!net_initialized)
205
if (!network_is_alive())
208
network_tyrian_halt(2, false);
212
static Uint32 keep_alive_tick = 0;
213
if (SDL_GetTicks() - keep_alive_tick > NET_KEEP_ALIVE)
215
network_prepare(PACKET_KEEP_ALIVE);
216
network_send_no_ack(4);
218
keep_alive_tick = SDL_GetTicks();
223
if (packet_out[0] && SDL_GetTicks() - last_out_tick > NET_RETRY)
225
if (!SDLNet_UDP_Send(socket, 0, packet_out[0]))
227
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
231
last_out_tick = SDL_GetTicks();
234
switch (SDLNet_UDP_Recv(socket, packet_temp))
237
printf("SDLNet_UDP_Recv: %s\n", SDL_GetError());
243
if (packet_temp->channel == 0 && packet_temp->len >= 4)
245
switch (SDLNet_Read16(&packet_temp->data[0]))
247
case PACKET_ACKNOWLEDGE:
248
if ((Uint16)(SDLNet_Read16(&packet_temp->data[2]) - last_ack_sync) < NET_PACKET_QUEUE)
250
last_ack_sync = SDLNet_Read16(&packet_temp->data[2]);
254
Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - queue_out_sync;
255
if (i < NET_PACKET_QUEUE)
259
SDLNet_FreePacket(packet_out[i]);
260
packet_out[i] = NULL;
265
// remove acknowledged packets from queue
266
while (packet_out[0] == NULL && (Uint16)(last_ack_sync - queue_out_sync) < NET_PACKET_QUEUE)
268
packets_shift_up(packet_out, NET_PACKET_QUEUE);
273
last_in_tick = SDL_GetTicks();
277
queue_in_sync = SDLNet_Read16(&packet_temp->data[2]);
279
for (int i = 0; i < NET_PACKET_QUEUE; i++)
283
SDLNet_FreePacket(packet_in[i]);
291
case PACKET_GAME_QUIT:
292
case PACKET_GAME_PAUSE:
293
case PACKET_GAME_MENU:
295
Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - queue_in_sync;
296
if (i < NET_PACKET_QUEUE)
298
if (packet_in[i] == NULL)
299
packet_in[i] = SDLNet_AllocPacket(NET_PACKET_SIZE);
300
packet_copy(packet_in[i], packet_temp);
302
// inbound packet queue overflow/underflow
303
// under normal circumstances, this is okay
307
network_acknowledge(SDLNet_Read16(&packet_temp->data[2]));
309
case PACKET_KEEP_ALIVE:
310
last_in_tick = SDL_GetTicks();
316
network_prepare(PACKET_QUIT);
317
network_send(4); // PACKET_QUIT
320
network_acknowledge(SDLNet_Read16(&packet_temp->data[2]));
323
network_tyrian_halt(1, true);
327
// place packet in queue if within limits
329
Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - last_state_in_sync + 1;
330
if (i < NET_PACKET_QUEUE)
332
if (packet_state_in[i] == NULL)
333
packet_state_in[i] = SDLNet_AllocPacket(NET_PACKET_SIZE);
334
packet_copy(packet_state_in[i], packet_temp);
339
case PACKET_STATE_XOR:
340
// place packet in queue if within limits
342
Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - last_state_in_sync + 1;
343
if (i < NET_PACKET_QUEUE)
345
if (packet_state_in_xor[i] == NULL)
347
packet_state_in_xor[i] = SDLNet_AllocPacket(NET_PACKET_SIZE);
348
packet_copy(packet_state_in_xor[i], packet_temp);
349
} else if (SDLNet_Read16(&packet_state_in_xor[i]->data[0]) != PACKET_STATE_XOR) {
350
for (int j = 4; j < packet_state_in_xor[i]->len; j++)
351
packet_state_in_xor[i]->data[j] ^= packet_temp->data[j];
352
SDLNet_Write16(PACKET_STATE_XOR, &packet_state_in_xor[i]->data[0]);
358
case PACKET_STATE_RESEND:
359
// resend requested state packet if still available
361
Uint16 i = last_state_out_sync - SDLNet_Read16(&packet_temp->data[2]);
362
if (i > 0 && i < NET_PACKET_QUEUE)
364
if (packet_state_out[i])
366
if (!SDLNet_UDP_Send(socket, 0, packet_state_out[i]))
368
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
377
fprintf(stderr, "warning: bad packet %d received\n", SDLNet_Read16(&packet_temp->data[0]));
390
// discard working packet, now processing next packet in queue
391
bool network_update( void )
395
packets_shift_up(packet_in, NET_PACKET_QUEUE);
405
// has opponent gotten all the packets we've sent?
406
bool network_is_sync( void )
408
return (queue_out_sync - last_ack_sync == 1);
412
// prepare new state for sending
413
void network_state_prepare( void )
415
if (packet_state_out[0])
417
fprintf(stderr, "warning: state packet overwritten (previous packet remains unsent)\n");
419
packet_state_out[0] = SDLNet_AllocPacket(NET_PACKET_SIZE);
420
packet_state_out[0]->len = 28;
423
SDLNet_Write16(PACKET_STATE, &packet_state_out[0]->data[0]);
424
SDLNet_Write16(last_state_out_sync, &packet_state_out[0]->data[2]);
425
memset(&packet_state_out[0]->data[4], 0, 28 - 4);
428
// send state packet, xor packet if applicable
429
int network_state_send( void )
431
if (!SDLNet_UDP_Send(socket, 0, packet_state_out[0]))
433
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
437
// send xor of last network_delay packets
438
if (network_delay > 1 && (last_state_out_sync + 1) % network_delay == 0 && packet_state_out[network_delay - 1] != NULL)
440
packet_copy(packet_temp, packet_state_out[0]);
441
SDLNet_Write16(PACKET_STATE_XOR, &packet_temp->data[0]);
442
for (int i = 1; i < network_delay; i++)
443
for (int j = 4; j < packet_temp->len; j++)
444
packet_temp->data[j] ^= packet_state_out[i]->data[j];
446
if (!SDLNet_UDP_Send(socket, 0, packet_temp))
448
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
453
packets_shift_down(packet_state_out, NET_PACKET_QUEUE);
455
last_state_out_sync++;
460
// receive state packet, wait until received
461
bool network_state_update( void )
463
if (network_state_is_reset())
467
packets_shift_up(packet_state_in, NET_PACKET_QUEUE);
469
packets_shift_up(packet_state_in_xor, NET_PACKET_QUEUE);
471
last_state_in_sync++;
473
// current xor packet index
474
int x = network_delay - (last_state_in_sync - 1) % network_delay - 1;
476
// loop until needed packet is available
477
while (!packet_state_in[0])
479
// xor the packet from thin air, if possible
480
if (packet_state_in_xor[x] && SDLNet_Read16(&packet_state_in_xor[x]->data[0]) == PACKET_STATE_XOR)
482
// check for all other required packets
484
for (int i = 1; i <= x; i++)
486
if (packet_state_in[i] == NULL)
494
packet_state_in[0] = SDLNet_AllocPacket(NET_PACKET_SIZE);
495
packet_copy(packet_state_in[0], packet_state_in_xor[x]);
496
for (int i = 1; i <= x; i++)
497
for (int j = 4; j < packet_state_in[0]->len; j++)
498
packet_state_in[0]->data[j] ^= packet_state_in[i]->data[j];
503
static Uint32 resend_tick = 0;
504
if (SDL_GetTicks() - last_state_in_tick > NET_RESEND && SDL_GetTicks() - resend_tick > NET_RESEND)
506
SDLNet_Write16(PACKET_STATE_RESEND, &packet_out_temp->data[0]);
507
SDLNet_Write16(last_state_in_sync - 1, &packet_out_temp->data[2]);
508
network_send_no_ack(4); // PACKET_RESEND
510
resend_tick = SDL_GetTicks();
513
if (network_check() == 0)
517
if (network_delay > 1)
519
// process the current in packet against the xor queue
520
if (packet_state_in_xor[x] == NULL)
522
packet_state_in_xor[x] = SDLNet_AllocPacket(NET_PACKET_SIZE);
523
packet_copy(packet_state_in_xor[x], packet_state_in[0]);
524
packet_state_in_xor[x]->status = 0;
526
for (int j = 4; j < packet_state_in_xor[x]->len; j++)
527
packet_state_in_xor[x]->data[j] ^= packet_state_in[0]->data[j];
531
last_state_in_tick = SDL_GetTicks();
537
// ignore first network_delay states of level
538
bool network_state_is_reset( void )
540
return (last_state_out_sync < network_delay);
543
// reset queues for new level
544
void network_state_reset( void )
546
last_state_in_sync = last_state_out_sync = 0;
548
for (int i = 0; i < NET_PACKET_QUEUE; i++)
550
if (packet_state_in[i])
552
SDLNet_FreePacket(packet_state_in[i]);
553
packet_state_in[i] = NULL;
556
for (int i = 0; i < NET_PACKET_QUEUE; i++)
558
if (packet_state_in_xor[i])
560
SDLNet_FreePacket(packet_state_in_xor[i]);
561
packet_state_in_xor[i] = NULL;
564
for (int i = 0; i < NET_PACKET_QUEUE; i++)
566
if (packet_state_out[i])
568
SDLNet_FreePacket(packet_state_out[i]);
569
packet_state_out[i] = NULL;
573
last_state_in_tick = SDL_GetTicks();
577
// attempt to punch through firewall by firing off UDP packets at the opponent
578
// exchange game information
579
int network_connect( void )
581
SDLNet_ResolveHost(&ip, network_opponent_host, network_opponent_port);
583
SDLNet_UDP_Bind(socket, 0, &ip);
585
Uint16 episodes = 0, episodes_local = 0;
586
assert(EPISODE_MAX <= 16);
587
for (int i = EPISODE_MAX - 1; i >= 0; i--)
590
episodes |= (episodeAvail[i] != 0);
592
episodes_local = episodes;
594
assert(NET_PACKET_SIZE - 12 >= 20 + 1);
595
if (strlen(network_player_name) > 20)
596
network_player_name[20] = '\0';
599
network_prepare(PACKET_CONNECT);
600
SDLNet_Write16(NET_VERSION, &packet_out_temp->data[4]);
601
SDLNet_Write16(network_delay, &packet_out_temp->data[6]);
602
SDLNet_Write16(episodes_local, &packet_out_temp->data[8]);
603
SDLNet_Write16(thisPlayerNum, &packet_out_temp->data[10]);
604
strcpy((char *)&packet_out_temp->data[12], network_player_name);
605
network_send(12 + strlen(network_player_name) + 1); // PACKET_CONNECT
607
// until opponent sends connect packet
610
push_joysticks_as_keyboard();
611
service_SDL_events(false);
613
if (newkey && lastkey_sym == SDLK_ESCAPE)
614
network_tyrian_halt(0, false);
617
last_in_tick = SDL_GetTicks();
619
if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_CONNECT)
629
if (SDLNet_Read16(&packet_in[0]->data[4]) != NET_VERSION)
631
fprintf(stderr, "error: network version did not match opponent's\n");
632
network_tyrian_halt(4, true);
634
if (SDLNet_Read16(&packet_in[0]->data[6]) != network_delay)
636
fprintf(stderr, "error: network delay did not match opponent's\n");
637
network_tyrian_halt(5, true);
639
if (SDLNet_Read16(&packet_in[0]->data[10]) == thisPlayerNum)
641
fprintf(stderr, "error: player number conflicts with opponent's\n");
642
network_tyrian_halt(6, true);
645
episodes = SDLNet_Read16(&packet_in[0]->data[8]);
646
for (int i = 0; i < EPISODE_MAX; i++) {
647
episodeAvail[i] &= (episodes & 1);
651
network_opponent_name = malloc(packet_in[0]->len - 12 + 1);
652
strcpy(network_opponent_name, (char *)&packet_in[0]->data[12]);
656
// until opponent has acknowledged
657
while (!network_is_sync())
659
service_SDL_events(false);
661
// got a duplicate packet; process it again (but why?)
662
if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_CONNECT)
667
// maybe opponent didn't get our packet
668
if (SDL_GetTicks() - last_out_tick > NET_RETRY)
674
// send another packet since sometimes the network syncs without both connect packets exchanged
675
// there should be a better way to handle this
676
network_prepare(PACKET_CONNECT);
677
SDLNet_Write16(NET_VERSION, &packet_out_temp->data[4]);
678
SDLNet_Write16(network_delay, &packet_out_temp->data[6]);
679
SDLNet_Write16(episodes_local, &packet_out_temp->data[8]);
680
SDLNet_Write16(thisPlayerNum, &packet_out_temp->data[10]);
681
strcpy((char *)&packet_out_temp->data[12], network_player_name);
682
network_send(12 + strlen(network_player_name) + 1); // PACKET_CONNECT
689
// something has gone wrong :(
690
void network_tyrian_halt( unsigned int err, bool attempt_sync )
692
const char *err_msg[] = {
694
"Other player quit the game.",
695
"Network connection was lost.",
696
"Network connection failed.",
697
"Network version mismatch.",
698
"Network delay mismatch.",
699
"Network player number conflict.",
704
if (err >= COUNTOF(err_msg))
709
VGAScreen = VGAScreenSeg;
711
JE_loadPic(VGAScreen, 2, false);
712
JE_dString(VGAScreen, JE_fontCenter(err_msg[err], SMALL_FONT_SHAPES), 140, err_msg[err], SMALL_FONT_SHAPES);
715
fade_palette(colors, 10, 0, 255);
719
while (!network_is_sync() && network_is_alive())
721
service_SDL_events(false);
730
while (!JE_anyButton())
741
int network_init( void )
743
printf("Initializing network...\n");
745
if (network_delay * 2 > NET_PACKET_QUEUE - 2)
747
fprintf(stderr, "error: network delay would overflow packet queue\n");
751
if (SDLNet_Init() == -1)
753
fprintf(stderr, "error: SDLNet_Init: %s\n", SDLNet_GetError());
757
socket = SDLNet_UDP_Open(network_player_port);
760
fprintf(stderr, "error: SDLNet_UDP_Open: %s\n", SDLNet_GetError());
764
packet_temp = SDLNet_AllocPacket(NET_PACKET_SIZE);
765
packet_out_temp = SDLNet_AllocPacket(NET_PACKET_SIZE);
767
if (!packet_temp || !packet_out_temp)
769
printf("SDLNet_AllocPacket: %s\n", SDLNet_GetError());
773
net_initialized = true;
780
void JE_clearSpecialRequests( void )
782
pauseRequest = false;
783
inGameMenuRequest = false;
784
skipLevelRequest = false;
786
nortShipRequest = false;