forked from KolibriOS/kolibrios
a27452493c
git-svn-id: svn://kolibrios.org@9169 a494cfbc-eb01-0410-851d-a64ba20cac60
792 lines
19 KiB
C
792 lines
19 KiB
C
/*
|
|
* OpenTyrian: A modern cross-platform port of Tyrian
|
|
* Copyright (C) 2007-2009 The OpenTyrian Development Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
#include "network.h"
|
|
|
|
#include "episodes.h"
|
|
#include "fonthand.h"
|
|
#include "helptext.h"
|
|
#include "joystick.h"
|
|
#include "keyboard.h"
|
|
#include "mainint.h"
|
|
#include "nortvars.h"
|
|
#include "opentyr.h"
|
|
#include "picload.h"
|
|
#include "sprite.h"
|
|
#include "varz.h"
|
|
#include "video.h"
|
|
|
|
#include <assert.h>
|
|
|
|
/* HERE BE DRAGONS!
|
|
*
|
|
* When I wrote this code I thought it was wonderful... that thought was very
|
|
* wrong. It works, but good luck understanding how... I don't anymore.
|
|
*
|
|
* Hopefully it'll be rewritten some day.
|
|
*/
|
|
|
|
#define NET_VERSION 2 // increment whenever networking changes might create incompatability
|
|
#define NET_PORT 1333 // UDP
|
|
|
|
#define NET_PACKET_SIZE 256
|
|
#define NET_PACKET_QUEUE 16
|
|
|
|
#define NET_RETRY 640 // ticks to wait for packet acknowledgement before resending
|
|
#define NET_RESEND 320 // ticks to wait before requesting unreceived game packet
|
|
#define NET_KEEP_ALIVE 1600 // ticks to wait between keep-alive packets
|
|
#define NET_TIME_OUT 16000 // ticks to wait before considering connection dead
|
|
|
|
bool isNetworkGame = false;
|
|
int network_delay = 1 + 1; // minimum is 1 + 0
|
|
|
|
char *network_opponent_host = NULL;
|
|
|
|
Uint16 network_player_port = NET_PORT,
|
|
network_opponent_port = NET_PORT;
|
|
|
|
static char empty_string[] = "";
|
|
char *network_player_name = empty_string,
|
|
*network_opponent_name = empty_string;
|
|
|
|
#ifdef WITH_NETWORK
|
|
static UDPsocket socket;
|
|
static IPaddress ip;
|
|
|
|
UDPpacket *packet_out_temp;
|
|
static UDPpacket *packet_temp;
|
|
|
|
UDPpacket *packet_in[NET_PACKET_QUEUE] = { NULL },
|
|
*packet_out[NET_PACKET_QUEUE] = { NULL };
|
|
|
|
static Uint16 last_out_sync = 0, queue_in_sync = 0, queue_out_sync = 0, last_ack_sync = 0;
|
|
static Uint32 last_in_tick = 0, last_out_tick = 0;
|
|
|
|
UDPpacket *packet_state_in[NET_PACKET_QUEUE] = { NULL };
|
|
static UDPpacket *packet_state_in_xor[NET_PACKET_QUEUE] = { NULL };
|
|
UDPpacket *packet_state_out[NET_PACKET_QUEUE] = { NULL };
|
|
|
|
static Uint16 last_state_in_sync = 0, last_state_out_sync = 0;
|
|
static Uint32 last_state_in_tick = 0;
|
|
|
|
static bool net_initialized = false;
|
|
static bool connected = false, quit = false;
|
|
#endif
|
|
|
|
uint thisPlayerNum = 0; /* Player number on this PC (1 or 2) */
|
|
|
|
JE_boolean haltGame = false;
|
|
|
|
JE_boolean moveOk;
|
|
|
|
/* Special Requests */
|
|
JE_boolean pauseRequest, skipLevelRequest, helpRequest, nortShipRequest;
|
|
JE_boolean yourInGameMenuRequest, inGameMenuRequest;
|
|
|
|
#ifdef WITH_NETWORK
|
|
static void packet_copy( UDPpacket *dst, UDPpacket *src )
|
|
{
|
|
void *temp = dst->data;
|
|
memcpy(dst, src, sizeof(*dst));
|
|
dst->data = temp;
|
|
memcpy(dst->data, src->data, src->len);
|
|
}
|
|
|
|
static void packets_shift_up( UDPpacket **packet, int max_packets )
|
|
{
|
|
if (packet[0])
|
|
{
|
|
SDLNet_FreePacket(packet[0]);
|
|
}
|
|
for (int i = 0; i < max_packets - 1; i++)
|
|
{
|
|
packet[i] = packet[i + 1];
|
|
}
|
|
packet[max_packets - 1] = NULL;
|
|
}
|
|
|
|
static void packets_shift_down( UDPpacket **packet, int max_packets )
|
|
{
|
|
if (packet[max_packets - 1])
|
|
{
|
|
SDLNet_FreePacket(packet[max_packets - 1]);
|
|
}
|
|
for (int i = max_packets - 1; i > 0; i--)
|
|
{
|
|
packet[i] = packet[i - 1];
|
|
}
|
|
packet[0] = NULL;
|
|
}
|
|
|
|
// prepare new packet for sending
|
|
void network_prepare( Uint16 type )
|
|
{
|
|
SDLNet_Write16(type, &packet_out_temp->data[0]);
|
|
SDLNet_Write16(last_out_sync, &packet_out_temp->data[2]);
|
|
}
|
|
|
|
// send packet but don't expect acknoledgment of delivery
|
|
static bool network_send_no_ack( int len )
|
|
{
|
|
packet_out_temp->len = len;
|
|
|
|
if (!SDLNet_UDP_Send(socket, 0, packet_out_temp))
|
|
{
|
|
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// send packet and place it in queue to be acknowledged
|
|
bool network_send( int len )
|
|
{
|
|
bool temp = network_send_no_ack(len);
|
|
|
|
Uint16 i = last_out_sync - queue_out_sync;
|
|
if (i < NET_PACKET_QUEUE)
|
|
{
|
|
packet_out[i] = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
packet_copy(packet_out[i], packet_out_temp);
|
|
} else {
|
|
// connection is probably bad now
|
|
fprintf(stderr, "warning: outbound packet queue overflow\n");
|
|
return false;
|
|
}
|
|
|
|
last_out_sync++;
|
|
|
|
if (network_is_sync())
|
|
last_out_tick = SDL_GetTicks();
|
|
|
|
return temp;
|
|
}
|
|
|
|
// send acknowledgement packet
|
|
static int network_acknowledge( Uint16 sync )
|
|
{
|
|
SDLNet_Write16(PACKET_ACKNOWLEDGE, &packet_out_temp->data[0]);
|
|
SDLNet_Write16(sync, &packet_out_temp->data[2]);
|
|
network_send_no_ack(4);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// activity lately?
|
|
static bool network_is_alive( void )
|
|
{
|
|
return (SDL_GetTicks() - last_in_tick < NET_TIME_OUT || SDL_GetTicks() - last_state_in_tick < NET_TIME_OUT);
|
|
}
|
|
|
|
// poll for new packets received, check that connection is alive, resend queued packets if necessary
|
|
int network_check( void )
|
|
{
|
|
if (!net_initialized)
|
|
return -1;
|
|
|
|
if (connected)
|
|
{
|
|
// timeout
|
|
if (!network_is_alive())
|
|
{
|
|
if (!quit)
|
|
network_tyrian_halt(2, false);
|
|
}
|
|
|
|
// keep-alive
|
|
static Uint32 keep_alive_tick = 0;
|
|
if (SDL_GetTicks() - keep_alive_tick > NET_KEEP_ALIVE)
|
|
{
|
|
network_prepare(PACKET_KEEP_ALIVE);
|
|
network_send_no_ack(4);
|
|
|
|
keep_alive_tick = SDL_GetTicks();
|
|
}
|
|
}
|
|
|
|
// retry
|
|
if (packet_out[0] && SDL_GetTicks() - last_out_tick > NET_RETRY)
|
|
{
|
|
if (!SDLNet_UDP_Send(socket, 0, packet_out[0]))
|
|
{
|
|
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
|
|
last_out_tick = SDL_GetTicks();
|
|
}
|
|
|
|
switch (SDLNet_UDP_Recv(socket, packet_temp))
|
|
{
|
|
case -1:
|
|
printf("SDLNet_UDP_Recv: %s\n", SDL_GetError());
|
|
return -1;
|
|
break;
|
|
case 0:
|
|
break;
|
|
default:
|
|
if (packet_temp->channel == 0 && packet_temp->len >= 4)
|
|
{
|
|
switch (SDLNet_Read16(&packet_temp->data[0]))
|
|
{
|
|
case PACKET_ACKNOWLEDGE:
|
|
if ((Uint16)(SDLNet_Read16(&packet_temp->data[2]) - last_ack_sync) < NET_PACKET_QUEUE)
|
|
{
|
|
last_ack_sync = SDLNet_Read16(&packet_temp->data[2]);
|
|
}
|
|
|
|
{
|
|
Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - queue_out_sync;
|
|
if (i < NET_PACKET_QUEUE)
|
|
{
|
|
if (packet_out[i])
|
|
{
|
|
SDLNet_FreePacket(packet_out[i]);
|
|
packet_out[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove acknowledged packets from queue
|
|
while (packet_out[0] == NULL && (Uint16)(last_ack_sync - queue_out_sync) < NET_PACKET_QUEUE)
|
|
{
|
|
packets_shift_up(packet_out, NET_PACKET_QUEUE);
|
|
|
|
queue_out_sync++;
|
|
}
|
|
|
|
last_in_tick = SDL_GetTicks();
|
|
break;
|
|
|
|
case PACKET_CONNECT:
|
|
queue_in_sync = SDLNet_Read16(&packet_temp->data[2]);
|
|
|
|
for (int i = 0; i < NET_PACKET_QUEUE; i++)
|
|
{
|
|
if (packet_in[i])
|
|
{
|
|
SDLNet_FreePacket(packet_in[i]);
|
|
packet_in[i] = NULL;
|
|
}
|
|
}
|
|
// fall through
|
|
|
|
case PACKET_DETAILS:
|
|
case PACKET_WAITING:
|
|
case PACKET_BUSY:
|
|
case PACKET_GAME_QUIT:
|
|
case PACKET_GAME_PAUSE:
|
|
case PACKET_GAME_MENU:
|
|
{
|
|
Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - queue_in_sync;
|
|
if (i < NET_PACKET_QUEUE)
|
|
{
|
|
if (packet_in[i] == NULL)
|
|
packet_in[i] = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
packet_copy(packet_in[i], packet_temp);
|
|
} else {
|
|
// inbound packet queue overflow/underflow
|
|
// under normal circumstances, this is okay
|
|
}
|
|
}
|
|
|
|
network_acknowledge(SDLNet_Read16(&packet_temp->data[2]));
|
|
// fall through
|
|
|
|
case PACKET_KEEP_ALIVE:
|
|
last_in_tick = SDL_GetTicks();
|
|
break;
|
|
|
|
case PACKET_QUIT:
|
|
if (!quit)
|
|
{
|
|
network_prepare(PACKET_QUIT);
|
|
network_send(4); // PACKET_QUIT
|
|
}
|
|
|
|
network_acknowledge(SDLNet_Read16(&packet_temp->data[2]));
|
|
|
|
if (!quit)
|
|
network_tyrian_halt(1, true);
|
|
break;
|
|
|
|
case PACKET_STATE:
|
|
// place packet in queue if within limits
|
|
{
|
|
Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - last_state_in_sync + 1;
|
|
if (i < NET_PACKET_QUEUE)
|
|
{
|
|
if (packet_state_in[i] == NULL)
|
|
packet_state_in[i] = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
packet_copy(packet_state_in[i], packet_temp);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PACKET_STATE_XOR:
|
|
// place packet in queue if within limits
|
|
{
|
|
Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - last_state_in_sync + 1;
|
|
if (i < NET_PACKET_QUEUE)
|
|
{
|
|
if (packet_state_in_xor[i] == NULL)
|
|
{
|
|
packet_state_in_xor[i] = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
packet_copy(packet_state_in_xor[i], packet_temp);
|
|
} else if (SDLNet_Read16(&packet_state_in_xor[i]->data[0]) != PACKET_STATE_XOR) {
|
|
for (int j = 4; j < packet_state_in_xor[i]->len; j++)
|
|
packet_state_in_xor[i]->data[j] ^= packet_temp->data[j];
|
|
SDLNet_Write16(PACKET_STATE_XOR, &packet_state_in_xor[i]->data[0]);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PACKET_STATE_RESEND:
|
|
// resend requested state packet if still available
|
|
{
|
|
Uint16 i = last_state_out_sync - SDLNet_Read16(&packet_temp->data[2]);
|
|
if (i > 0 && i < NET_PACKET_QUEUE)
|
|
{
|
|
if (packet_state_out[i])
|
|
{
|
|
if (!SDLNet_UDP_Send(socket, 0, packet_state_out[i]))
|
|
{
|
|
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "warning: bad packet %d received\n", SDLNet_Read16(&packet_temp->data[0]));
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// discard working packet, now processing next packet in queue
|
|
bool network_update( void )
|
|
{
|
|
if (packet_in[0])
|
|
{
|
|
packets_shift_up(packet_in, NET_PACKET_QUEUE);
|
|
|
|
queue_in_sync++;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// has opponent gotten all the packets we've sent?
|
|
bool network_is_sync( void )
|
|
{
|
|
return (queue_out_sync - last_ack_sync == 1);
|
|
}
|
|
|
|
|
|
// prepare new state for sending
|
|
void network_state_prepare( void )
|
|
{
|
|
if (packet_state_out[0])
|
|
{
|
|
fprintf(stderr, "warning: state packet overwritten (previous packet remains unsent)\n");
|
|
} else {
|
|
packet_state_out[0] = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
packet_state_out[0]->len = 28;
|
|
}
|
|
|
|
SDLNet_Write16(PACKET_STATE, &packet_state_out[0]->data[0]);
|
|
SDLNet_Write16(last_state_out_sync, &packet_state_out[0]->data[2]);
|
|
memset(&packet_state_out[0]->data[4], 0, 28 - 4);
|
|
}
|
|
|
|
// send state packet, xor packet if applicable
|
|
int network_state_send( void )
|
|
{
|
|
if (!SDLNet_UDP_Send(socket, 0, packet_state_out[0]))
|
|
{
|
|
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
|
|
// send xor of last network_delay packets
|
|
if (network_delay > 1 && (last_state_out_sync + 1) % network_delay == 0 && packet_state_out[network_delay - 1] != NULL)
|
|
{
|
|
packet_copy(packet_temp, packet_state_out[0]);
|
|
SDLNet_Write16(PACKET_STATE_XOR, &packet_temp->data[0]);
|
|
for (int i = 1; i < network_delay; i++)
|
|
for (int j = 4; j < packet_temp->len; j++)
|
|
packet_temp->data[j] ^= packet_state_out[i]->data[j];
|
|
|
|
if (!SDLNet_UDP_Send(socket, 0, packet_temp))
|
|
{
|
|
printf("SDLNet_UDP_Send: %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
packets_shift_down(packet_state_out, NET_PACKET_QUEUE);
|
|
|
|
last_state_out_sync++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// receive state packet, wait until received
|
|
bool network_state_update( void )
|
|
{
|
|
if (network_state_is_reset())
|
|
{
|
|
return 0;
|
|
} else {
|
|
packets_shift_up(packet_state_in, NET_PACKET_QUEUE);
|
|
|
|
packets_shift_up(packet_state_in_xor, NET_PACKET_QUEUE);
|
|
|
|
last_state_in_sync++;
|
|
|
|
// current xor packet index
|
|
int x = network_delay - (last_state_in_sync - 1) % network_delay - 1;
|
|
|
|
// loop until needed packet is available
|
|
while (!packet_state_in[0])
|
|
{
|
|
// xor the packet from thin air, if possible
|
|
if (packet_state_in_xor[x] && SDLNet_Read16(&packet_state_in_xor[x]->data[0]) == PACKET_STATE_XOR)
|
|
{
|
|
// check for all other required packets
|
|
bool okay = true;
|
|
for (int i = 1; i <= x; i++)
|
|
{
|
|
if (packet_state_in[i] == NULL)
|
|
{
|
|
okay = false;
|
|
break;
|
|
}
|
|
}
|
|
if (okay)
|
|
{
|
|
packet_state_in[0] = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
packet_copy(packet_state_in[0], packet_state_in_xor[x]);
|
|
for (int i = 1; i <= x; i++)
|
|
for (int j = 4; j < packet_state_in[0]->len; j++)
|
|
packet_state_in[0]->data[j] ^= packet_state_in[i]->data[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Uint32 resend_tick = 0;
|
|
if (SDL_GetTicks() - last_state_in_tick > NET_RESEND && SDL_GetTicks() - resend_tick > NET_RESEND)
|
|
{
|
|
SDLNet_Write16(PACKET_STATE_RESEND, &packet_out_temp->data[0]);
|
|
SDLNet_Write16(last_state_in_sync - 1, &packet_out_temp->data[2]);
|
|
network_send_no_ack(4); // PACKET_RESEND
|
|
|
|
resend_tick = SDL_GetTicks();
|
|
}
|
|
|
|
if (network_check() == 0)
|
|
uSDL_Delay(1);
|
|
}
|
|
|
|
if (network_delay > 1)
|
|
{
|
|
// process the current in packet against the xor queue
|
|
if (packet_state_in_xor[x] == NULL)
|
|
{
|
|
packet_state_in_xor[x] = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
packet_copy(packet_state_in_xor[x], packet_state_in[0]);
|
|
packet_state_in_xor[x]->status = 0;
|
|
} else {
|
|
for (int j = 4; j < packet_state_in_xor[x]->len; j++)
|
|
packet_state_in_xor[x]->data[j] ^= packet_state_in[0]->data[j];
|
|
}
|
|
}
|
|
|
|
last_state_in_tick = SDL_GetTicks();
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// ignore first network_delay states of level
|
|
bool network_state_is_reset( void )
|
|
{
|
|
return (last_state_out_sync < network_delay);
|
|
}
|
|
|
|
// reset queues for new level
|
|
void network_state_reset( void )
|
|
{
|
|
last_state_in_sync = last_state_out_sync = 0;
|
|
|
|
for (int i = 0; i < NET_PACKET_QUEUE; i++)
|
|
{
|
|
if (packet_state_in[i])
|
|
{
|
|
SDLNet_FreePacket(packet_state_in[i]);
|
|
packet_state_in[i] = NULL;
|
|
}
|
|
}
|
|
for (int i = 0; i < NET_PACKET_QUEUE; i++)
|
|
{
|
|
if (packet_state_in_xor[i])
|
|
{
|
|
SDLNet_FreePacket(packet_state_in_xor[i]);
|
|
packet_state_in_xor[i] = NULL;
|
|
}
|
|
}
|
|
for (int i = 0; i < NET_PACKET_QUEUE; i++)
|
|
{
|
|
if (packet_state_out[i])
|
|
{
|
|
SDLNet_FreePacket(packet_state_out[i]);
|
|
packet_state_out[i] = NULL;
|
|
}
|
|
}
|
|
|
|
last_state_in_tick = SDL_GetTicks();
|
|
}
|
|
|
|
|
|
// attempt to punch through firewall by firing off UDP packets at the opponent
|
|
// exchange game information
|
|
int network_connect( void )
|
|
{
|
|
SDLNet_ResolveHost(&ip, network_opponent_host, network_opponent_port);
|
|
|
|
SDLNet_UDP_Bind(socket, 0, &ip);
|
|
|
|
Uint16 episodes = 0, episodes_local = 0;
|
|
assert(EPISODE_MAX <= 16);
|
|
for (int i = EPISODE_MAX - 1; i >= 0; i--)
|
|
{
|
|
episodes <<= 1;
|
|
episodes |= (episodeAvail[i] != 0);
|
|
}
|
|
episodes_local = episodes;
|
|
|
|
assert(NET_PACKET_SIZE - 12 >= 20 + 1);
|
|
if (strlen(network_player_name) > 20)
|
|
network_player_name[20] = '\0';
|
|
|
|
connect_reset:
|
|
network_prepare(PACKET_CONNECT);
|
|
SDLNet_Write16(NET_VERSION, &packet_out_temp->data[4]);
|
|
SDLNet_Write16(network_delay, &packet_out_temp->data[6]);
|
|
SDLNet_Write16(episodes_local, &packet_out_temp->data[8]);
|
|
SDLNet_Write16(thisPlayerNum, &packet_out_temp->data[10]);
|
|
strcpy((char *)&packet_out_temp->data[12], network_player_name);
|
|
network_send(12 + strlen(network_player_name) + 1); // PACKET_CONNECT
|
|
|
|
// until opponent sends connect packet
|
|
while (true)
|
|
{
|
|
push_joysticks_as_keyboard();
|
|
service_SDL_events(false);
|
|
|
|
if (newkey && lastkey_sym == SDLK_ESCAPE)
|
|
network_tyrian_halt(0, false);
|
|
|
|
// never timeout
|
|
last_in_tick = SDL_GetTicks();
|
|
|
|
if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_CONNECT)
|
|
break;
|
|
|
|
network_update();
|
|
network_check();
|
|
|
|
uSDL_Delay(16);
|
|
}
|
|
|
|
connect_again:
|
|
if (SDLNet_Read16(&packet_in[0]->data[4]) != NET_VERSION)
|
|
{
|
|
fprintf(stderr, "error: network version did not match opponent's\n");
|
|
network_tyrian_halt(4, true);
|
|
}
|
|
if (SDLNet_Read16(&packet_in[0]->data[6]) != network_delay)
|
|
{
|
|
fprintf(stderr, "error: network delay did not match opponent's\n");
|
|
network_tyrian_halt(5, true);
|
|
}
|
|
if (SDLNet_Read16(&packet_in[0]->data[10]) == thisPlayerNum)
|
|
{
|
|
fprintf(stderr, "error: player number conflicts with opponent's\n");
|
|
network_tyrian_halt(6, true);
|
|
}
|
|
|
|
episodes = SDLNet_Read16(&packet_in[0]->data[8]);
|
|
for (int i = 0; i < EPISODE_MAX; i++) {
|
|
episodeAvail[i] &= (episodes & 1);
|
|
episodes >>= 1;
|
|
}
|
|
|
|
network_opponent_name = malloc(packet_in[0]->len - 12 + 1);
|
|
strcpy(network_opponent_name, (char *)&packet_in[0]->data[12]);
|
|
|
|
network_update();
|
|
|
|
// until opponent has acknowledged
|
|
while (!network_is_sync())
|
|
{
|
|
service_SDL_events(false);
|
|
|
|
// got a duplicate packet; process it again (but why?)
|
|
if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_CONNECT)
|
|
goto connect_again;
|
|
|
|
network_check();
|
|
|
|
// maybe opponent didn't get our packet
|
|
if (SDL_GetTicks() - last_out_tick > NET_RETRY)
|
|
goto connect_reset;
|
|
|
|
uSDL_Delay(16);
|
|
}
|
|
|
|
// send another packet since sometimes the network syncs without both connect packets exchanged
|
|
// there should be a better way to handle this
|
|
network_prepare(PACKET_CONNECT);
|
|
SDLNet_Write16(NET_VERSION, &packet_out_temp->data[4]);
|
|
SDLNet_Write16(network_delay, &packet_out_temp->data[6]);
|
|
SDLNet_Write16(episodes_local, &packet_out_temp->data[8]);
|
|
SDLNet_Write16(thisPlayerNum, &packet_out_temp->data[10]);
|
|
strcpy((char *)&packet_out_temp->data[12], network_player_name);
|
|
network_send(12 + strlen(network_player_name) + 1); // PACKET_CONNECT
|
|
|
|
connected = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// something has gone wrong :(
|
|
void network_tyrian_halt( unsigned int err, bool attempt_sync )
|
|
{
|
|
const char *err_msg[] = {
|
|
"Quitting...",
|
|
"Other player quit the game.",
|
|
"Network connection was lost.",
|
|
"Network connection failed.",
|
|
"Network version mismatch.",
|
|
"Network delay mismatch.",
|
|
"Network player number conflict.",
|
|
};
|
|
|
|
quit = true;
|
|
|
|
if (err >= COUNTOF(err_msg))
|
|
err = 0;
|
|
|
|
fade_black(10);
|
|
|
|
VGAScreen = VGAScreenSeg;
|
|
|
|
JE_loadPic(VGAScreen, 2, false);
|
|
JE_dString(VGAScreen, JE_fontCenter(err_msg[err], SMALL_FONT_SHAPES), 140, err_msg[err], SMALL_FONT_SHAPES);
|
|
|
|
JE_showVGA();
|
|
fade_palette(colors, 10, 0, 255);
|
|
|
|
if (attempt_sync)
|
|
{
|
|
while (!network_is_sync() && network_is_alive())
|
|
{
|
|
service_SDL_events(false);
|
|
|
|
network_check();
|
|
uSDL_Delay(16);
|
|
}
|
|
}
|
|
|
|
if (err)
|
|
{
|
|
while (!JE_anyButton())
|
|
uSDL_Delay(16);
|
|
}
|
|
|
|
fade_black(10);
|
|
|
|
SDLNet_Quit();
|
|
|
|
JE_tyrianHalt(5);
|
|
}
|
|
|
|
int network_init( void )
|
|
{
|
|
printf("Initializing network...\n");
|
|
|
|
if (network_delay * 2 > NET_PACKET_QUEUE - 2)
|
|
{
|
|
fprintf(stderr, "error: network delay would overflow packet queue\n");
|
|
return -4;
|
|
}
|
|
|
|
if (SDLNet_Init() == -1)
|
|
{
|
|
fprintf(stderr, "error: SDLNet_Init: %s\n", SDLNet_GetError());
|
|
return -1;
|
|
}
|
|
|
|
socket = SDLNet_UDP_Open(network_player_port);
|
|
if (!socket)
|
|
{
|
|
fprintf(stderr, "error: SDLNet_UDP_Open: %s\n", SDLNet_GetError());
|
|
return -2;
|
|
}
|
|
|
|
packet_temp = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
packet_out_temp = SDLNet_AllocPacket(NET_PACKET_SIZE);
|
|
|
|
if (!packet_temp || !packet_out_temp)
|
|
{
|
|
printf("SDLNet_AllocPacket: %s\n", SDLNet_GetError());
|
|
return -3;
|
|
}
|
|
|
|
net_initialized = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
void JE_clearSpecialRequests( void )
|
|
{
|
|
pauseRequest = false;
|
|
inGameMenuRequest = false;
|
|
skipLevelRequest = false;
|
|
helpRequest = false;
|
|
nortShipRequest = false;
|
|
}
|
|
|