5607 lines
157 KiB
C
5607 lines
157 KiB
C
/*
|
|
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
|
|
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
|
|
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
|
|
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
|
|
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
|
|
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
|
|
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
|
|
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
|
|
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
|
|
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* Routines for managing IPX-protocol network play.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <conf.h>
|
|
#endif
|
|
|
|
#define PATCH12
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "pstypes.h"
|
|
#include "window.h"
|
|
#include "strutil.h"
|
|
#include "args.h"
|
|
#include "timer.h"
|
|
#include "newmenu.h"
|
|
#include "key.h"
|
|
#include "gauges.h"
|
|
#include "object.h"
|
|
#include "error.h"
|
|
#include "laser.h"
|
|
#include "gamesave.h"
|
|
#include "gamemine.h"
|
|
#include "player.h"
|
|
#include "gameseq.h"
|
|
#include "fireball.h"
|
|
#include "net_ipx.h"
|
|
#include "game.h"
|
|
#include "multi.h"
|
|
#include "endlevel.h"
|
|
#include "palette.h"
|
|
#include "cntrlcen.h"
|
|
#include "powerup.h"
|
|
#include "menu.h"
|
|
#include "sounds.h"
|
|
#include "text.h"
|
|
#include "kmatrix.h"
|
|
#include "newdemo.h"
|
|
#include "multibot.h"
|
|
#include "wall.h"
|
|
#include "bm.h"
|
|
#include "effects.h"
|
|
#include "physics.h"
|
|
#include "switch.h"
|
|
#include "automap.h"
|
|
#include "byteswap.h"
|
|
#include "kconfig.h"
|
|
#include "playsave.h"
|
|
#include "cfile.h"
|
|
#include "gamefont.h"
|
|
#include "rbaudio.h"
|
|
|
|
// Prototypes
|
|
extern void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[]);
|
|
extern void multi_send_kill_goal_counts();
|
|
void net_ipx_process_naked_pdata (char *,int);
|
|
void net_ipx_flush();
|
|
void net_ipx_listen();
|
|
void net_ipx_update_netgame();
|
|
void net_ipx_check_for_old_version(char pnum);
|
|
void net_ipx_send_objects();
|
|
void net_ipx_send_rejoin_sync(int player_num);
|
|
void net_ipx_send_game_info(IPX_sequence_packet *their);
|
|
void net_ipx_send_endlevel_short_sub(int from_player_num, int to_player);
|
|
void net_ipx_read_sync_packet(netgame_info * sp, int rsinit);
|
|
int net_ipx_wait_for_playerinfo();
|
|
void net_ipx_process_pdata(char *data);
|
|
void net_ipx_read_object_packet(ubyte *data );
|
|
void net_ipx_read_endlevel_packet(ubyte *data );
|
|
void net_ipx_read_endlevel_short_packet(ubyte *data );
|
|
void net_ipx_process_names_return(ubyte *data);
|
|
void net_ipx_send_player_names(IPX_sequence_packet *their);
|
|
void net_ipx_more_game_options();
|
|
int net_ipx_wait_for_all_info(int choice);
|
|
void net_ipx_do_big_wait(int choice);
|
|
void net_ipx_send_extras();
|
|
void net_ipx_read_pdata_packet(IPX_frame_info *pd);
|
|
void net_ipx_read_pdata_short_packet(IPX_short_frame_info *pd);
|
|
void ClipRank(ubyte *rank);
|
|
void net_ipx_do_refuse_stuff(IPX_sequence_packet *their);
|
|
int net_ipx_get_new_player_num(IPX_sequence_packet *their);
|
|
int net_ipx_show_game_stats(int choice);
|
|
extern void multi_send_stolen_items();
|
|
int net_ipx_wait_for_snyc();
|
|
extern void multi_send_wall_status (int,ubyte,ubyte,ubyte);
|
|
extern void multi_send_wall_status_specific (int,int,ubyte,ubyte,ubyte);
|
|
|
|
// Variables
|
|
int num_active_ipx_games = 0;
|
|
int IPX_active=0;
|
|
int num_active_ipx_changed = 0;
|
|
int IPX_allow_socket_changes = 1;
|
|
int NetSecurityFlag=NETSECURITY_OFF;
|
|
int NetSecurityNum=0;
|
|
IPX_sequence_packet IPX_sync_player; // Who is rejoining now?
|
|
int IPX_TotalMissedPackets=0,IPX_TotalPacketsGot=0;
|
|
IPX_frame_info MySyncPack,UrgentSyncPack;
|
|
ubyte MySyncPackInitialized = 0; // Set to 1 if the MySyncPack is zeroed.
|
|
IPX_sequence_packet IPX_Seq;
|
|
char WantPlayersInfo=0;
|
|
char WaitingForPlayerInfo=0;
|
|
int IPX_Socket=0;
|
|
extern obj_position Player_init[MAX_PLAYERS];
|
|
extern int force_cockpit_redraw;
|
|
extern ubyte Version_major,Version_minor;
|
|
extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
|
|
extern char PowerupsInMine[MAX_POWERUP_TYPES];
|
|
extern int Final_boss_is_dead;
|
|
typedef struct IPX_endlevel_info {
|
|
ubyte type;
|
|
ubyte player_num;
|
|
sbyte connected;
|
|
ubyte seconds_left;
|
|
short kill_matrix[MAX_PLAYERS][MAX_PLAYERS];
|
|
short kills;
|
|
short killed;
|
|
} IPX_endlevel_info;
|
|
|
|
typedef struct IPX_endlevel_info_short {
|
|
ubyte type;
|
|
ubyte player_num;
|
|
sbyte connected;
|
|
ubyte seconds_left;
|
|
} IPX_endlevel_info_short;
|
|
|
|
#define SEQUENCE_PACKET_SIZE (sizeof(IPX_sequence_packet)-sizeof(netplayer_info)+sizeof(IPX_netplayer_info))
|
|
#define FRAME_INFO_SIZE sizeof(IPX_frame_info)
|
|
#define IPX_SHORT_INFO_SIZE sizeof(IPX_short_frame_info)
|
|
|
|
netgame_info Active_ipx_games[IPX_MAX_NETGAMES];
|
|
netgame_info *TempPlayersInfo,TempPlayersBase;
|
|
int NamesInfoSecurity=-1;
|
|
netgame_info TempNetInfo;
|
|
|
|
/* General IPX functions - START */
|
|
ubyte broadcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
ubyte null_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
ubyte ipxdrv_installed=0;
|
|
u_int32_t ipx_network = 0;
|
|
ubyte MyAddress[10];
|
|
int ipx_packetnum = 0; /* Sequence number */
|
|
|
|
/* User defined routing stuff */
|
|
typedef struct user_address {
|
|
ubyte network[4];
|
|
ubyte node[6];
|
|
ubyte address[6];
|
|
} user_address;
|
|
|
|
socket_t socket_data;
|
|
#define MAX_USERS 64
|
|
int Ipx_num_users = 0;
|
|
user_address Ipx_users[MAX_USERS];
|
|
#define MAX_NETWORKS 64
|
|
int Ipx_num_networks = 0;
|
|
uint Ipx_networks[MAX_NETWORKS];
|
|
|
|
int ipxdrv_general_packet_ready(int fd)
|
|
{
|
|
fd_set set;
|
|
struct timeval tv;
|
|
|
|
FD_ZERO(&set);
|
|
FD_SET(fd, &set);
|
|
tv.tv_sec = tv.tv_usec = 0;
|
|
if (select(fd + 1, &set, NULL, NULL, &tv) > 0)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
struct net_driver *driver = NULL;
|
|
|
|
ubyte * ipxdrv_get_my_server_address()
|
|
{
|
|
return (ubyte *)&ipx_network;
|
|
}
|
|
|
|
ubyte * ipxdrv_get_my_local_address()
|
|
{
|
|
return (ubyte *)(MyAddress + 4);
|
|
}
|
|
|
|
void ipxdrv_close()
|
|
{
|
|
if (ipxdrv_installed)
|
|
{
|
|
#ifdef _WIN32
|
|
WSACleanup();
|
|
#endif
|
|
driver->close_socket(&socket_data);
|
|
}
|
|
|
|
ipxdrv_installed = 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------
|
|
// Initializes all driver internals.
|
|
// If socket_number==0, then opens next available socket.
|
|
// Returns: 0 if successful.
|
|
// -1 if socket already open.
|
|
// -2 if socket table full.
|
|
// -3 if driver not installed.
|
|
// -4 if couldn't allocate low dos memory
|
|
// -5 if error with getting internetwork address
|
|
int ipxdrv_init( int socket_number )
|
|
{
|
|
#ifdef _WIN32
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
#endif
|
|
|
|
if (!driver)
|
|
return -1;
|
|
|
|
#ifdef _WIN32
|
|
wVersionRequested = MAKEWORD(2, 0);
|
|
if (WSAStartup( wVersionRequested, &wsaData))
|
|
{
|
|
return -1;
|
|
}
|
|
#endif
|
|
memset(MyAddress,0,10);
|
|
|
|
if (GameArg.MplIpxNetwork)
|
|
{
|
|
unsigned long n = strtol(GameArg.MplIpxNetwork, NULL, 16);
|
|
MyAddress[0] = n >> 24; MyAddress[1] = (n >> 16) & 255;
|
|
MyAddress[2] = (n >> 8) & 255; MyAddress[3] = n & 255;
|
|
con_printf(CON_DEBUG,"IPX: Using network %08x\n", (unsigned int)n);
|
|
}
|
|
|
|
if (driver->open_socket(&socket_data, socket_number))
|
|
{
|
|
return -3;
|
|
}
|
|
|
|
memcpy(&ipx_network, MyAddress, 4);
|
|
Ipx_num_networks = 0;
|
|
memcpy( &Ipx_networks[Ipx_num_networks++], &ipx_network, 4 );
|
|
|
|
ipxdrv_installed = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ipxdrv_set(int arg)
|
|
{
|
|
int ipxdrv_err;
|
|
|
|
ipxdrv_close();
|
|
|
|
con_printf(CON_VERBOSE, "\n%s ", TXT_INITIALIZING_NETWORK);
|
|
|
|
switch (arg)
|
|
{
|
|
#ifndef __APPLE__
|
|
case NETPROTO_IPX:
|
|
driver = &ipxdrv_ipx;
|
|
break;
|
|
#endif
|
|
#ifdef __LINUX__
|
|
case NETPROTO_KALINIX:
|
|
driver = &ipxdrv_kali;
|
|
break;
|
|
#endif
|
|
default:
|
|
driver = NULL;
|
|
break;
|
|
}
|
|
|
|
if ((ipxdrv_err=ipxdrv_init(IPX_DEFAULT_SOCKET))==0)
|
|
{
|
|
con_printf(CON_VERBOSE, "%s %d.\n", TXT_IPX_CHANNEL, IPX_DEFAULT_SOCKET );
|
|
IPX_active = 1;
|
|
}
|
|
else
|
|
{
|
|
switch (ipxdrv_err)
|
|
{
|
|
case 3:
|
|
con_printf(CON_VERBOSE, "%s\n", TXT_NO_NETWORK);
|
|
break;
|
|
case -2:
|
|
con_printf(CON_VERBOSE, "%s 0x%x.\n", TXT_SOCKET_ERROR, IPX_DEFAULT_SOCKET);
|
|
break;
|
|
case -4:
|
|
con_printf(CON_VERBOSE, "%s\n", TXT_MEMORY_IPX );
|
|
break;
|
|
default:
|
|
con_printf(CON_VERBOSE, "%s %d", TXT_ERROR_IPX, ipxdrv_err );
|
|
break;
|
|
}
|
|
|
|
con_printf(CON_VERBOSE, "%s\n",TXT_NETWORK_DISABLED);
|
|
IPX_active = 0; // Assume no network
|
|
}
|
|
|
|
return ipxdrv_installed?0:-1;
|
|
}
|
|
|
|
int ipxdrv_get_packet_data( ubyte * data )
|
|
{
|
|
struct recv_data rd;
|
|
char *buf;
|
|
int size;
|
|
|
|
if (driver->usepacketnum)
|
|
buf=alloca(MAX_IPX_DATA);
|
|
else
|
|
buf=(char *)data;
|
|
|
|
memset(rd.src_network,1,4);
|
|
|
|
while (driver->packet_ready(&socket_data))
|
|
{
|
|
if ((size = driver->receive_packet(&socket_data, buf, MAX_IPX_DATA, &rd)) > 4)
|
|
{
|
|
if (!memcmp(rd.src_network, MyAddress, 10))
|
|
{
|
|
continue; /* don't get own pkts */
|
|
}
|
|
|
|
if (driver->usepacketnum)
|
|
{
|
|
memcpy(data, buf + 4, size - 4);
|
|
return size-4;
|
|
}
|
|
else
|
|
{
|
|
return size;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ipxdrv_send_packet_data( ubyte * data, int datasize, ubyte *network, ubyte *address, ubyte *immediate_address )
|
|
{
|
|
IPXPacket_t ipx_header;
|
|
|
|
if (!ipxdrv_installed)
|
|
return;
|
|
|
|
memcpy(ipx_header.Destination.Network, network, 4);
|
|
memcpy(ipx_header.Destination.Node, immediate_address, 6);
|
|
ipx_header.PacketType = 4; /* Packet Exchange */
|
|
|
|
if (driver->usepacketnum)
|
|
{
|
|
ubyte buf[MAX_IPX_DATA];
|
|
memcpy(buf, &ipx_packetnum, 4);
|
|
ipx_packetnum++;
|
|
memcpy(buf + 4, data, datasize);
|
|
driver->send_packet(&socket_data, &ipx_header, buf, datasize + 4);
|
|
}
|
|
else
|
|
driver->send_packet(&socket_data, &ipx_header, data, datasize);//we can save 4 bytes
|
|
}
|
|
|
|
void ipxdrv_get_local_target( ubyte * server, ubyte * node, ubyte * local_target )
|
|
{
|
|
memcpy( local_target, node, 6 );
|
|
}
|
|
|
|
void ipxdrv_send_broadcast_packet_data( ubyte * data, int datasize )
|
|
{
|
|
int i, j;
|
|
ubyte local_address[6];
|
|
|
|
if (!ipxdrv_installed)
|
|
return;
|
|
|
|
// Set to all networks besides mine
|
|
for (i=0; i<Ipx_num_networks; i++ )
|
|
{
|
|
if ( memcmp( &Ipx_networks[i], &ipx_network, 4 ) )
|
|
{
|
|
ipxdrv_get_local_target( (ubyte *)&Ipx_networks[i], broadcast_addr, local_address );
|
|
ipxdrv_send_packet_data( data, datasize, (ubyte *)&Ipx_networks[i], broadcast_addr, local_address );
|
|
}
|
|
else
|
|
{
|
|
ipxdrv_send_packet_data( data, datasize, (ubyte *)&Ipx_networks[i], broadcast_addr, broadcast_addr );
|
|
}
|
|
}
|
|
|
|
// Send directly to all users not on my network or in the network list.
|
|
for (i=0; i<Ipx_num_users; i++ )
|
|
{
|
|
if ( memcmp( Ipx_users[i].network, &ipx_network, 4 ) )
|
|
{
|
|
for (j=0; j<Ipx_num_networks; j++ )
|
|
{
|
|
if (!memcmp( Ipx_users[i].network, &Ipx_networks[j], 4 ))
|
|
goto SkipUser;
|
|
}
|
|
|
|
ipxdrv_send_packet_data( data, datasize, Ipx_users[i].network, Ipx_users[i].node, Ipx_users[i].address );
|
|
SkipUser:
|
|
j = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sends a non-localized packet... needs 4 byte server, 6 byte address
|
|
void ipxdrv_send_internetwork_packet_data( ubyte * data, int datasize, ubyte * server, ubyte *address )
|
|
{
|
|
ubyte local_address[6];
|
|
|
|
if (!ipxdrv_installed)
|
|
return;
|
|
|
|
#ifdef WORDS_NEED_ALIGNMENT
|
|
int zero = 0;
|
|
if (memcmp(server, &zero, 4))
|
|
#else // WORDS_NEED_ALIGNMENT
|
|
if ((*(uint *)server) != 0)
|
|
#endif // WORDS_NEED_ALIGNMENT
|
|
{
|
|
ipxdrv_get_local_target( server, address, local_address );
|
|
ipxdrv_send_packet_data( data, datasize, server, address, local_address );
|
|
}
|
|
else
|
|
{
|
|
// Old method, no server info.
|
|
ipxdrv_send_packet_data( data, datasize, server, address, address );
|
|
}
|
|
}
|
|
|
|
int ipxdrv_change_default_socket( ushort socket_number )
|
|
{
|
|
if ( !ipxdrv_installed )
|
|
return -3;
|
|
|
|
driver->close_socket(&socket_data);
|
|
|
|
if (driver->open_socket(&socket_data, socket_number))
|
|
{
|
|
return -3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Return type of net_driver
|
|
int ipxdrv_type(void)
|
|
{
|
|
return driver->type;
|
|
}
|
|
/* General IPX functions - END */
|
|
|
|
void net_ipx_send_sequence_packet(IPX_sequence_packet seq, ubyte *server, ubyte *node, ubyte *net_address)
|
|
{
|
|
int loc, tmpi;
|
|
short tmps;
|
|
ubyte out_buffer[MAX_DATA_SIZE];
|
|
|
|
loc = 0;
|
|
memset(out_buffer, 0, sizeof(out_buffer));
|
|
out_buffer[0] = seq.type; loc++;
|
|
tmpi = INTEL_INT(seq.Security);
|
|
memcpy(&(out_buffer[loc]), &tmpi, 4); loc += 4; loc += 3;
|
|
memcpy(&out_buffer[loc], seq.player.callsign, CALLSIGN_LEN+1); loc += CALLSIGN_LEN+1;
|
|
memcpy(&out_buffer[loc], seq.player.protocol.ipx.server, 4); loc += 4;
|
|
memcpy(&out_buffer[loc], seq.player.protocol.ipx.node, 6); loc += 6;
|
|
out_buffer[loc] = seq.player.version_major; loc++;
|
|
out_buffer[loc] = seq.player.version_minor; loc++;
|
|
out_buffer[loc] = seq.player.protocol.ipx.computer_type; loc++;
|
|
out_buffer[loc] = seq.player.connected; loc++;
|
|
tmps = INTEL_SHORT(seq.player.protocol.ipx.socket);
|
|
memcpy(&(out_buffer[loc]), &tmps, 2); loc += 2;
|
|
out_buffer[loc] = seq.player.rank; loc++;
|
|
|
|
if (net_address != NULL)
|
|
ipxdrv_send_packet_data(out_buffer, loc, server, node, net_address);
|
|
else if ((server == NULL) && (node == NULL))
|
|
ipxdrv_send_broadcast_packet_data(out_buffer, loc);
|
|
else
|
|
ipxdrv_send_internetwork_packet_data(out_buffer, loc, server, node);
|
|
}
|
|
|
|
void net_ipx_receive_sequence_packet(ubyte *data, IPX_sequence_packet *seq)
|
|
{
|
|
int loc = 0;
|
|
|
|
seq->type = data[0]; loc++;
|
|
memcpy(&(seq->Security), &(data[loc]), 4); loc += 4; loc += 3; // +3 for pad byte
|
|
seq->Security = INTEL_INT(seq->Security);
|
|
memcpy(seq->player.callsign, &(data[loc]), CALLSIGN_LEN+1); loc += CALLSIGN_LEN+1;
|
|
memcpy(&(seq->player.protocol.ipx.server), &(data[loc]), 4); loc += 4;
|
|
memcpy(&(seq->player.protocol.ipx.node), &(data[loc]), 6); loc += 6;
|
|
seq->player.version_major = data[loc]; loc++;
|
|
seq->player.version_minor = data[loc]; loc++;
|
|
memcpy(&(seq->player.protocol.ipx.computer_type), &(data[loc]), 1); loc++; // memcpy to avoid compile time warning about enum
|
|
seq->player.connected = data[loc]; loc++;
|
|
memcpy(&(seq->player.protocol.ipx.socket), &(data[loc]), 2); loc += 2;
|
|
memcpy (&(seq->player.rank),&(data[loc]),1); loc++;
|
|
}
|
|
|
|
void send_netgame_packet(ubyte *server, ubyte *node, ubyte *net_address, int lite_flag)
|
|
{
|
|
if (lite_flag)
|
|
{
|
|
IPX_lite_info netpkt;
|
|
|
|
netpkt.type = Netgame.protocol.ipx.Game_pkt_type;
|
|
netpkt.Security = Netgame.protocol.ipx.Game_Security;
|
|
memcpy(&netpkt.game_name, &Netgame.game_name, sizeof(char)*(NETGAME_NAME_LEN+1));
|
|
memcpy(&netpkt.mission_title, &Netgame.mission_title, sizeof(char)*(MISSION_NAME_LEN+1));
|
|
memcpy(&netpkt.mission_name, &Netgame.mission_name, sizeof(char)*9);
|
|
netpkt.levelnum = Netgame.levelnum;
|
|
netpkt.gamemode = Netgame.gamemode;
|
|
netpkt.RefusePlayers = Netgame.RefusePlayers;
|
|
netpkt.difficulty = Netgame.difficulty;
|
|
netpkt.game_status = Netgame.game_status;
|
|
netpkt.numplayers = Netgame.numplayers;
|
|
netpkt.max_numplayers = Netgame.max_numplayers;
|
|
netpkt.numconnected = Netgame.numconnected;
|
|
netpkt.game_flags = Netgame.game_flags;
|
|
netpkt.protocol_version = Netgame.protocol.ipx.protocol_version;
|
|
netpkt.version_major = Netgame.version_major;
|
|
netpkt.version_minor = Netgame.version_minor;
|
|
netpkt.team_vector = Netgame.team_vector;
|
|
|
|
if (net_address != NULL)
|
|
ipxdrv_send_packet_data((ubyte *)&netpkt, sizeof(IPX_lite_info), server, node, net_address);
|
|
else if ((server == NULL) && (node == NULL))
|
|
ipxdrv_send_broadcast_packet_data((ubyte *)&netpkt, sizeof(IPX_lite_info));
|
|
else
|
|
ipxdrv_send_internetwork_packet_data((ubyte *)&netpkt, sizeof(IPX_lite_info), server, node);
|
|
}
|
|
else
|
|
{
|
|
IPX_netgame_info netpkt;
|
|
int i, j;
|
|
|
|
netpkt.type = Netgame.protocol.ipx.Game_pkt_type;
|
|
netpkt.Security = Netgame.protocol.ipx.Game_Security;
|
|
memcpy(&netpkt.game_name, &Netgame.game_name, sizeof(char)*(NETGAME_NAME_LEN+1));
|
|
memcpy(&netpkt.mission_title, &Netgame.mission_title, sizeof(char)*(MISSION_NAME_LEN+1));
|
|
memcpy(&netpkt.mission_name, &Netgame.mission_name, sizeof(char)*9);
|
|
netpkt.levelnum = Netgame.levelnum;
|
|
netpkt.gamemode = Netgame.gamemode;
|
|
netpkt.RefusePlayers = Netgame.RefusePlayers;
|
|
netpkt.difficulty = Netgame.difficulty;
|
|
netpkt.game_status = Netgame.game_status;
|
|
netpkt.numplayers = Netgame.numplayers;
|
|
netpkt.max_numplayers = Netgame.max_numplayers;
|
|
netpkt.numconnected = Netgame.numconnected;
|
|
netpkt.game_flags = Netgame.game_flags;
|
|
netpkt.protocol_version = Netgame.protocol.ipx.protocol_version;
|
|
netpkt.version_major = Netgame.version_major;
|
|
netpkt.version_minor = Netgame.version_minor;
|
|
netpkt.team_vector = Netgame.team_vector;
|
|
netpkt.DoMegas = (Netgame.AllowedItems & NETFLAG_DOMEGA);
|
|
netpkt.DoSmarts = (Netgame.AllowedItems & NETFLAG_DOSMART);
|
|
netpkt.DoFusions = (Netgame.AllowedItems & NETFLAG_DOFUSION);
|
|
netpkt.DoHelix = (Netgame.AllowedItems & NETFLAG_DOHELIX);
|
|
netpkt.DoPhoenix = (Netgame.AllowedItems & NETFLAG_DOPHOENIX);
|
|
netpkt.DoAfterburner = (Netgame.AllowedItems & NETFLAG_DOAFTERBURNER);
|
|
netpkt.DoInvulnerability = (Netgame.AllowedItems & NETFLAG_DOINVUL);
|
|
netpkt.DoCloak = (Netgame.AllowedItems & NETFLAG_DOCLOAK);
|
|
netpkt.DoGauss = (Netgame.AllowedItems & NETFLAG_DOGAUSS);
|
|
netpkt.DoVulcan = (Netgame.AllowedItems & NETFLAG_DOVULCAN);
|
|
netpkt.DoPlasma = (Netgame.AllowedItems & NETFLAG_DOPLASMA);
|
|
netpkt.DoOmega = (Netgame.AllowedItems & NETFLAG_DOOMEGA);
|
|
netpkt.DoSuperLaser = (Netgame.AllowedItems & NETFLAG_DOSUPERLASER);
|
|
netpkt.DoProximity = (Netgame.AllowedItems & NETFLAG_DOPROXIM);
|
|
netpkt.DoSpread = (Netgame.AllowedItems & NETFLAG_DOSPREAD);
|
|
netpkt.DoSmartMine = (Netgame.AllowedItems & NETFLAG_DOSMARTMINE);
|
|
netpkt.DoFlash = (Netgame.AllowedItems & NETFLAG_DOFLASH);
|
|
netpkt.DoGuided = (Netgame.AllowedItems & NETFLAG_DOGUIDED);
|
|
netpkt.DoEarthShaker = (Netgame.AllowedItems & NETFLAG_DOSHAKER);
|
|
netpkt.DoMercury = (Netgame.AllowedItems & NETFLAG_DOMERCURY);
|
|
netpkt.Allow_marker_view = Netgame.Allow_marker_view;
|
|
netpkt.AlwaysLighting = Netgame.AlwaysLighting;
|
|
netpkt.DoAmmoRack = (Netgame.AllowedItems & NETFLAG_DOAMMORACK);
|
|
netpkt.DoConverter = (Netgame.AllowedItems & NETFLAG_DOCONVERTER);
|
|
netpkt.DoHeadlight = (Netgame.AllowedItems & NETFLAG_DOHEADLIGHT);
|
|
netpkt.DoHoming = (Netgame.AllowedItems & NETFLAG_DOHOMING);
|
|
netpkt.DoLaserUpgrade = (Netgame.AllowedItems & NETFLAG_DOLASER);
|
|
netpkt.DoQuadLasers = (Netgame.AllowedItems & NETFLAG_DOQUAD);
|
|
netpkt.ShowAllNames = 1; // Was ShowAllNames - always allow, don't patronize me. :P
|
|
netpkt.BrightPlayers = Netgame.BrightPlayers;
|
|
netpkt.invul = Netgame.InvulAppear;
|
|
memcpy(&netpkt.team_name, &Netgame.team_name, 2*(CALLSIGN_LEN+1));
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
netpkt.locations[i] = Netgame.locations[i];
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
for (j = 0; j < MAX_PLAYERS; j++)
|
|
netpkt.kills[i][j] = Netgame.kills[i][j];
|
|
netpkt.segments_checksum = Netgame.segments_checksum;
|
|
netpkt.team_kills[0] = Netgame.team_kills[0];
|
|
netpkt.team_kills[1] = Netgame.team_kills[1];
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
netpkt.killed[i] = Netgame.killed[i];
|
|
netpkt.player_kills[i] = Netgame.player_kills[i];
|
|
}
|
|
netpkt.KillGoal = Netgame.KillGoal;
|
|
netpkt.PlayTimeAllowed = Netgame.PlayTimeAllowed;
|
|
netpkt.level_time = Netgame.level_time;
|
|
netpkt.control_invul_time = Netgame.control_invul_time;
|
|
netpkt.monitor_vector = Netgame.monitor_vector;
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
netpkt.player_score[i] = Netgame.player_score[i];
|
|
netpkt.player_flags[i] = Netgame.player_flags[i];
|
|
}
|
|
netpkt.PacketsPerSec = Netgame.PacketsPerSec;
|
|
netpkt.ShortPackets = Netgame.protocol.ipx.ShortPackets;
|
|
|
|
if (net_address != NULL)
|
|
ipxdrv_send_packet_data((ubyte *)&netpkt, sizeof(IPX_netgame_info), server, node, net_address);
|
|
else if ((server == NULL) && (node == NULL))
|
|
ipxdrv_send_broadcast_packet_data((ubyte *)&netpkt, sizeof(IPX_netgame_info));
|
|
else
|
|
ipxdrv_send_internetwork_packet_data((ubyte *)&netpkt, sizeof(IPX_netgame_info), server, node);
|
|
}
|
|
}
|
|
|
|
void receive_netgame_packet(ubyte *data, netgame_info *netgame, int lite_flag)
|
|
{
|
|
if (lite_flag)
|
|
{
|
|
IPX_lite_info netpkt;
|
|
|
|
memcpy(&netpkt, data, sizeof(IPX_lite_info));
|
|
|
|
netgame->protocol.ipx.Game_pkt_type = netpkt.type;
|
|
netgame->protocol.ipx.Game_Security = netpkt.Security;
|
|
memcpy(netgame->game_name, &netpkt.game_name, sizeof(char)*(NETGAME_NAME_LEN+1));
|
|
memcpy(netgame->mission_title, &netpkt.mission_title, sizeof(char)*(MISSION_NAME_LEN+1));
|
|
memcpy(netgame->mission_name, &netpkt.mission_name, sizeof(char)*9);
|
|
netgame->levelnum = netpkt.levelnum;
|
|
netgame->gamemode = netpkt.gamemode;
|
|
netgame->RefusePlayers = netpkt.RefusePlayers;
|
|
netgame->difficulty = netpkt.difficulty;
|
|
netgame->game_status = netpkt.game_status;
|
|
netgame->numplayers = netpkt.numplayers;
|
|
netgame->max_numplayers = netpkt.max_numplayers;
|
|
netgame->numconnected = netpkt.numconnected;
|
|
netgame->game_flags = netpkt.game_flags;
|
|
netgame->protocol.ipx.protocol_version = netpkt.protocol_version;
|
|
netgame->version_major = netpkt.version_major;
|
|
netgame->version_minor = netpkt.version_minor;
|
|
netgame->team_vector = netpkt.team_vector;
|
|
}
|
|
else
|
|
{
|
|
IPX_netgame_info netpkt;
|
|
int i, j;
|
|
short showallnamesdummy;
|
|
|
|
memcpy(&netpkt, data, sizeof(IPX_netgame_info));
|
|
|
|
netgame->protocol.ipx.Game_pkt_type = netpkt.type;
|
|
netgame->protocol.ipx.Game_Security = netpkt.Security;
|
|
memcpy(netgame->game_name, &netpkt.game_name, sizeof(char)*(NETGAME_NAME_LEN+1));
|
|
memcpy(netgame->mission_title, &netpkt.mission_title, sizeof(char)*(MISSION_NAME_LEN+1));
|
|
memcpy(netgame->mission_name, &netpkt.mission_name, sizeof(char)*9);
|
|
netgame->levelnum = netpkt.levelnum;
|
|
netgame->gamemode = netpkt.gamemode;
|
|
netgame->RefusePlayers = netpkt.RefusePlayers;
|
|
netgame->difficulty = netpkt.difficulty;
|
|
netgame->game_status = netpkt.game_status;
|
|
netgame->numplayers = netpkt.numplayers;
|
|
netgame->max_numplayers = netpkt.max_numplayers;
|
|
netgame->numconnected = netpkt.numconnected;
|
|
netgame->game_flags = netpkt.game_flags;
|
|
netgame->protocol.ipx.protocol_version = netpkt.protocol_version;
|
|
netgame->version_major = netpkt.version_major;
|
|
netgame->version_minor = netpkt.version_minor;
|
|
netgame->team_vector = netpkt.team_vector;
|
|
netgame->AllowedItems = 0;
|
|
if (netpkt.DoMegas) netgame->AllowedItems |= NETFLAG_DOMEGA;
|
|
if (netpkt.DoSmarts) netgame->AllowedItems |= NETFLAG_DOSMART;
|
|
if (netpkt.DoFusions) netgame->AllowedItems |= NETFLAG_DOFUSION;
|
|
if (netpkt.DoHelix) netgame->AllowedItems |= NETFLAG_DOHELIX;
|
|
if (netpkt.DoPhoenix) netgame->AllowedItems |= NETFLAG_DOPHOENIX;
|
|
if (netpkt.DoAfterburner) netgame->AllowedItems |= NETFLAG_DOAFTERBURNER;
|
|
if (netpkt.DoInvulnerability) netgame->AllowedItems |= NETFLAG_DOINVUL;
|
|
if (netpkt.DoCloak) netgame->AllowedItems |= NETFLAG_DOCLOAK;
|
|
if (netpkt.DoGauss) netgame->AllowedItems |= NETFLAG_DOGAUSS;
|
|
if (netpkt.DoVulcan) netgame->AllowedItems |= NETFLAG_DOVULCAN;
|
|
if (netpkt.DoPlasma) netgame->AllowedItems |= NETFLAG_DOPLASMA;
|
|
if (netpkt.DoOmega) netgame->AllowedItems |= NETFLAG_DOOMEGA;
|
|
if (netpkt.DoSuperLaser) netgame->AllowedItems |= NETFLAG_DOSUPERLASER;
|
|
if (netpkt.DoProximity) netgame->AllowedItems |= NETFLAG_DOPROXIM;
|
|
if (netpkt.DoSpread) netgame->AllowedItems |= NETFLAG_DOSPREAD;
|
|
if (netpkt.DoSmartMine) netgame->AllowedItems |= NETFLAG_DOSMARTMINE;
|
|
if (netpkt.DoFlash) netgame->AllowedItems |= NETFLAG_DOFLASH;
|
|
if (netpkt.DoGuided) netgame->AllowedItems |= NETFLAG_DOGUIDED;
|
|
if (netpkt.DoEarthShaker) netgame->AllowedItems |= NETFLAG_DOSHAKER;
|
|
if (netpkt.DoMercury) netgame->AllowedItems |= NETFLAG_DOMERCURY;
|
|
if (netpkt.DoAmmoRack) netgame->AllowedItems |= NETFLAG_DOAMMORACK;
|
|
if (netpkt.DoConverter) netgame->AllowedItems |= NETFLAG_DOCONVERTER;
|
|
if (netpkt.DoHeadlight) netgame->AllowedItems |= NETFLAG_DOHEADLIGHT;
|
|
if (netpkt.DoHoming) netgame->AllowedItems |= NETFLAG_DOHOMING;
|
|
if (netpkt.DoLaserUpgrade) netgame->AllowedItems |= NETFLAG_DOLASER;
|
|
if (netpkt.DoQuadLasers) netgame->AllowedItems |= NETFLAG_DOQUAD;
|
|
netgame->Allow_marker_view = netpkt.Allow_marker_view;
|
|
netgame->AlwaysLighting = netpkt.AlwaysLighting;
|
|
showallnamesdummy = netpkt.ShowAllNames;// we ignore that, don't patronize me. :P
|
|
netgame->BrightPlayers = netpkt.BrightPlayers;
|
|
netgame->InvulAppear = netpkt.invul;
|
|
memcpy(netgame->team_name, &netpkt.team_name, 2*(CALLSIGN_LEN+1));
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
netgame->locations[i] = netpkt.locations[i];
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
for (j = 0; j < MAX_PLAYERS; j++)
|
|
netgame->kills[i][j] = netpkt.kills[i][j];
|
|
netgame->segments_checksum = netpkt.segments_checksum;
|
|
netgame->team_kills[0] = netpkt.team_kills[0];
|
|
netgame->team_kills[1] = netpkt.team_kills[1];
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
netgame->killed[i] = netpkt.killed[i];
|
|
netgame->player_kills[i] = netpkt.player_kills[i];
|
|
}
|
|
netgame->KillGoal = netpkt.KillGoal;
|
|
netgame->PlayTimeAllowed = netpkt.PlayTimeAllowed;
|
|
netgame->level_time = netpkt.level_time;
|
|
netgame->control_invul_time = netpkt.control_invul_time;
|
|
netgame->monitor_vector = netpkt.monitor_vector;
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
netgame->player_score[i] = netpkt.player_score[i];
|
|
netgame->player_flags[i] = netpkt.player_flags[i];
|
|
}
|
|
netgame->PacketsPerSec = netpkt.PacketsPerSec;
|
|
netgame->protocol.ipx.ShortPackets = netpkt.ShortPackets;
|
|
}
|
|
}
|
|
|
|
void send_netplayers_packet(ubyte *server, ubyte *node)
|
|
{
|
|
IPX_AllNetPlayers_info netplrs;
|
|
int i;
|
|
|
|
netplrs.type = Netgame.protocol.ipx.Player_pkt_type;
|
|
netplrs.Security = Netgame.protocol.ipx.Player_Security;
|
|
for (i = 0; i < MAX_PLAYERS+4; i++)
|
|
{
|
|
memcpy(&netplrs.players[i].callsign, &Netgame.players[i].callsign, CALLSIGN_LEN+1);
|
|
memcpy(&netplrs.players[i].network.ipx.server, &Netgame.players[i].protocol.ipx.server, 4);
|
|
memcpy(&netplrs.players[i].network.ipx.node, &Netgame.players[i].protocol.ipx.node, 6);
|
|
netplrs.players[i].version_major = Netgame.players[i].version_major;
|
|
netplrs.players[i].version_minor = Netgame.players[i].version_minor;
|
|
netplrs.players[i].computer_type = Netgame.players[i].protocol.ipx.computer_type;
|
|
netplrs.players[i].connected = Netgame.players[i].connected;
|
|
netplrs.players[i].socket = Netgame.players[i].protocol.ipx.socket;
|
|
netplrs.players[i].rank = Netgame.players[i].rank;
|
|
}
|
|
|
|
if ((server == NULL) && (node == NULL))
|
|
ipxdrv_send_broadcast_packet_data((ubyte *)&netplrs, sizeof(IPX_AllNetPlayers_info));
|
|
else
|
|
ipxdrv_send_internetwork_packet_data((ubyte *)&netplrs, sizeof(IPX_AllNetPlayers_info), server, node);
|
|
}
|
|
|
|
void receive_netplayers_packet(ubyte *data, netgame_info *pinfo)
|
|
{
|
|
IPX_AllNetPlayers_info netplrs;
|
|
int i;
|
|
|
|
memcpy(&netplrs, data, sizeof(IPX_AllNetPlayers_info));
|
|
|
|
pinfo->protocol.ipx.Player_pkt_type = netplrs.type;
|
|
pinfo->protocol.ipx.Player_Security = netplrs.Security;
|
|
for (i = 0; i < MAX_PLAYERS+4; i++)
|
|
{
|
|
memcpy(pinfo->players[i].callsign, &netplrs.players[i].callsign, CALLSIGN_LEN+1);
|
|
memcpy(pinfo->players[i].protocol.ipx.server, &netplrs.players[i].network.ipx.server, 4);
|
|
memcpy(pinfo->players[i].protocol.ipx.node, &netplrs.players[i].network.ipx.node, 6);
|
|
pinfo->players[i].version_major = netplrs.players[i].version_major;
|
|
pinfo->players[i].version_minor = netplrs.players[i].version_minor;
|
|
pinfo->players[i].protocol.ipx.computer_type = netplrs.players[i].computer_type;
|
|
pinfo->players[i].connected = netplrs.players[i].connected;
|
|
pinfo->players[i].protocol.ipx.socket = netplrs.players[i].socket;
|
|
pinfo->players[i].rank = netplrs.players[i].rank;
|
|
}
|
|
}
|
|
|
|
void
|
|
net_ipx_init(void)
|
|
{
|
|
// So you want to play a netgame, eh? Let's a get a few things straight
|
|
int t;
|
|
int save_pnum = Player_num;
|
|
|
|
Final_boss_is_dead=0;
|
|
NamesInfoSecurity=-1;
|
|
|
|
for (t=0;t<MAX_POWERUP_TYPES;t++)
|
|
{
|
|
MaxPowerupsAllowed[t]=0;
|
|
PowerupsInMine[t]=0;
|
|
}
|
|
|
|
IPX_TotalMissedPackets=0; IPX_TotalPacketsGot=0;
|
|
|
|
memset(&Netgame, 0, sizeof(netgame_info));
|
|
memset(&IPX_Seq, 0, sizeof(IPX_sequence_packet));
|
|
IPX_Seq.type = PID_REQUEST;
|
|
memcpy(IPX_Seq.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
|
|
|
|
IPX_Seq.player.version_major=Version_major;
|
|
IPX_Seq.player.version_minor=Version_minor;
|
|
IPX_Seq.player.rank=GetMyNetRanking();
|
|
|
|
memcpy(IPX_Seq.player.protocol.ipx.node, ipxdrv_get_my_local_address(), 6);
|
|
memcpy(IPX_Seq.player.protocol.ipx.server, ipxdrv_get_my_server_address(), 4 );
|
|
IPX_Seq.player.protocol.ipx.computer_type = 1;
|
|
|
|
for (Player_num = 0; Player_num < MAX_NUM_NET_PLAYERS; Player_num++)
|
|
init_player_stats_game();
|
|
|
|
Player_num = save_pnum;
|
|
multi_new_game();
|
|
Network_new_game = 1;
|
|
Control_center_destroyed = 0;
|
|
net_ipx_flush();
|
|
|
|
Netgame.PacketsPerSec=10;
|
|
Netgame.protocol.ipx.ShortPackets=0;
|
|
}
|
|
|
|
int net_ipx_how_many_connected()
|
|
{
|
|
int num=0,i;
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
if (Players[i].connected)
|
|
num++;
|
|
return (num);
|
|
}
|
|
|
|
#define ENDLEVEL_SEND_INTERVAL (F1_0*2)
|
|
#define ENDLEVEL_IDLE_TIME (F1_0*20)
|
|
|
|
|
|
int net_ipx_kmatrix_poll1( newmenu *menu, d_event *event, void *userdata )
|
|
{
|
|
// Polling loop for End-of-level menu
|
|
|
|
static fix64 t1 = 0;
|
|
int i = 0;
|
|
int num_ready = 0;
|
|
int goto_secret = 0;
|
|
|
|
menu = menu;
|
|
userdata = userdata;
|
|
|
|
if (event->type != EVENT_WINDOW_DRAW)
|
|
return 0;
|
|
|
|
// Send our endlevel packet at regular intervals
|
|
|
|
if (timer_query() > t1+ENDLEVEL_SEND_INTERVAL)
|
|
{
|
|
net_ipx_send_endlevel_packet();
|
|
t1 = timer_query();
|
|
}
|
|
|
|
net_ipx_listen();
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if ((Players[i].connected != CONNECT_PLAYING) && (Players[i].connected != CONNECT_ESCAPE_TUNNEL) && (Players[i].connected != CONNECT_END_MENU))
|
|
num_ready++;
|
|
if (Players[i].connected == CONNECT_FOUND_SECRET)
|
|
goto_secret = 1;
|
|
}
|
|
|
|
if (num_ready == N_players) // All players have checked in or are disconnected
|
|
{
|
|
if (goto_secret)
|
|
return -3;
|
|
else
|
|
return -2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern fix64 StartAbortMenuTime;
|
|
|
|
int net_ipx_kmatrix_poll2( newmenu *menu, d_event *event, void *userdata )
|
|
{
|
|
// Polling loop for End-of-level menu
|
|
|
|
int num_ready=0, i, rval = 0;
|
|
|
|
menu = menu;
|
|
userdata = userdata;
|
|
|
|
if (event->type != EVENT_WINDOW_DRAW)
|
|
return 0;
|
|
|
|
if (timer_query() > (StartAbortMenuTime+(F1_0 * 8)))
|
|
rval = -2;
|
|
|
|
|
|
net_ipx_listen();
|
|
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
if ((Players[i].connected != CONNECT_PLAYING) && (Players[i].connected != CONNECT_ESCAPE_TUNNEL) && (Players[i].connected != CONNECT_END_MENU))
|
|
num_ready++;
|
|
|
|
if (num_ready == N_players) // All players have checked in or are disconnected
|
|
rval = -2;
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
int
|
|
net_ipx_endlevel(int *secret)
|
|
{
|
|
// Do whatever needs to be done between levels
|
|
|
|
int i;
|
|
|
|
*secret=0;
|
|
|
|
//net_ipx_flush();
|
|
|
|
Network_status = NETSTAT_ENDLEVEL; // We are between levels
|
|
|
|
net_ipx_listen();
|
|
|
|
net_ipx_send_endlevel_packet();
|
|
|
|
for (i=0; i<N_players; i++)
|
|
{
|
|
Netgame.players[i].LastPacketTime = timer_query();
|
|
}
|
|
|
|
net_ipx_send_endlevel_packet();
|
|
net_ipx_send_endlevel_packet();
|
|
MySyncPackInitialized = 0;
|
|
|
|
net_ipx_update_netgame();
|
|
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
net_ipx_can_join_netgame(netgame_info *game)
|
|
{
|
|
// Can this player rejoin a netgame in progress?
|
|
|
|
int i, num_players;
|
|
|
|
if (game->game_status == NETSTAT_STARTING)
|
|
return 1;
|
|
|
|
if (game->game_status != NETSTAT_PLAYING)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (game->version_major==0 && Version_major>0)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
if (game->version_major>0 && Version_major==0)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
// Game is in progress, figure out if this guy can re-join it
|
|
|
|
num_players = game->numplayers;
|
|
|
|
if (!(game->game_flags & NETGAME_FLAG_CLOSED)) {
|
|
// Look for player that is not connected
|
|
|
|
if (game->numconnected==game->max_numplayers)
|
|
return (2);
|
|
|
|
if (game->RefusePlayers)
|
|
return (3);
|
|
|
|
if (game->numplayers < game->max_numplayers)
|
|
return 1;
|
|
|
|
if (game->numconnected<num_players)
|
|
return 1;
|
|
}
|
|
|
|
// Search to see if we were already in this closed netgame in progress
|
|
|
|
for (i = 0; i < num_players; i++) {
|
|
if ( (!stricmp(Players[Player_num].callsign, game->players[i].callsign)) &&
|
|
(!memcmp(IPX_Seq.player.protocol.ipx.node, game->players[i].protocol.ipx.node, 6)) &&
|
|
(!memcmp(IPX_Seq.player.protocol.ipx.server, game->players[i].protocol.ipx.server, 4)) )
|
|
break;
|
|
}
|
|
|
|
if (i != num_players)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
net_ipx_disconnect_player(int playernum)
|
|
{
|
|
// A player has disconnected from the net game, take whatever steps are
|
|
// necessary
|
|
|
|
if (playernum == Player_num)
|
|
{
|
|
Int3(); // Weird, see Rob
|
|
return;
|
|
}
|
|
|
|
Players[playernum].connected = CONNECT_DISCONNECTED;
|
|
Netgame.players[playernum].connected = CONNECT_DISCONNECTED;
|
|
if (VerifyPlayerJoined==playernum)
|
|
VerifyPlayerJoined=-1;
|
|
|
|
// create_player_appearance_effect(&Objects[Players[playernum].objnum]);
|
|
multi_make_player_ghost(playernum);
|
|
|
|
if (Newdemo_state == ND_STATE_RECORDING)
|
|
newdemo_record_multi_disconnect(playernum);
|
|
|
|
multi_strip_robots(playernum);
|
|
}
|
|
|
|
void
|
|
net_ipx_new_player(IPX_sequence_packet *their)
|
|
{
|
|
int objnum;
|
|
int pnum;
|
|
uint server;
|
|
|
|
pnum = their->player.connected;
|
|
|
|
Assert(pnum >= 0);
|
|
Assert(pnum < MaxNumNetPlayers);
|
|
|
|
objnum = Players[pnum].objnum;
|
|
|
|
if (Newdemo_state == ND_STATE_RECORDING) {
|
|
int new_player;
|
|
|
|
if (pnum == N_players)
|
|
new_player = 1;
|
|
else
|
|
new_player = 0;
|
|
newdemo_record_multi_connect(pnum, new_player, their->player.callsign);
|
|
}
|
|
|
|
memcpy(Players[pnum].callsign, their->player.callsign, CALLSIGN_LEN+1);
|
|
memcpy(Netgame.players[pnum].callsign, their->player.callsign, CALLSIGN_LEN+1);
|
|
|
|
|
|
ClipRank (&their->player.rank);
|
|
Netgame.players[pnum].rank=their->player.rank;
|
|
Netgame.players[pnum].version_major=their->player.version_major;
|
|
Netgame.players[pnum].version_minor=their->player.version_minor;
|
|
net_ipx_check_for_old_version(pnum);
|
|
|
|
memcpy(&server, their->player.protocol.ipx.server, 4);
|
|
if ( server != 0 )
|
|
ipxdrv_get_local_target( their->player.protocol.ipx.server, their->player.protocol.ipx.node, Players[pnum].net_address );
|
|
else
|
|
memcpy(Players[pnum].net_address, their->player.protocol.ipx.node, 6);
|
|
|
|
memcpy(Netgame.players[pnum].protocol.ipx.node, their->player.protocol.ipx.node, 6);
|
|
memcpy(Netgame.players[pnum].protocol.ipx.server, their->player.protocol.ipx.server, 4);
|
|
|
|
Players[pnum].n_packets_got = 0;
|
|
Players[pnum].connected = CONNECT_PLAYING;
|
|
Players[pnum].net_kills_total = 0;
|
|
Players[pnum].net_killed_total = 0;
|
|
memset(kill_matrix[pnum], 0, MAX_PLAYERS*sizeof(short));
|
|
Players[pnum].score = 0;
|
|
Players[pnum].flags = 0;
|
|
Players[pnum].KillGoalCount=0;
|
|
|
|
if (pnum == N_players)
|
|
{
|
|
N_players++;
|
|
Netgame.numplayers = N_players;
|
|
}
|
|
|
|
digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
|
|
|
|
ClipRank (&their->player.rank);
|
|
|
|
if (PlayerCfg.NoRankings)
|
|
HUD_init_message(HM_MULTI, "'%s' %s\n",their->player.callsign, TXT_JOINING);
|
|
else
|
|
HUD_init_message(HM_MULTI, "%s'%s' %s\n",RankStrings[their->player.rank],their->player.callsign, TXT_JOINING);
|
|
|
|
multi_make_ghost_player(pnum);
|
|
|
|
multi_send_score();
|
|
multi_sort_kill_list();
|
|
}
|
|
|
|
void net_ipx_welcome_player(IPX_sequence_packet *their)
|
|
{
|
|
// Add a player to a game already in progress
|
|
ubyte local_address[6];
|
|
int player_num;
|
|
int i;
|
|
uint server;
|
|
|
|
WaitForRefuseAnswer=0;
|
|
|
|
if (HoardEquipped())
|
|
{
|
|
// If hoard game, and this guy isn't D2 Christmas (v1.2), dump him
|
|
if ((Game_mode & GM_HOARD) && ((their->player.version_minor & 0x0F)<2))
|
|
{
|
|
net_ipx_dump_player(their->player.protocol.ipx.server, their->player.protocol.ipx.node, DUMP_DORK);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Don't accept new players if we're ending this level. Its safe to
|
|
// ignore since they'll request again later
|
|
|
|
if ((Endlevel_sequence) || (Control_center_destroyed))
|
|
{
|
|
net_ipx_dump_player(their->player.protocol.ipx.server,their->player.protocol.ipx.node, DUMP_ENDLEVEL);
|
|
return;
|
|
}
|
|
|
|
if (Network_send_objects || Network_sending_extras)
|
|
{
|
|
// Ignore silently, we're already responding to someone and we can't
|
|
// do more than one person at a time. If we don't dump them they will
|
|
// re-request in a few seconds.
|
|
return;
|
|
}
|
|
|
|
if (their->player.connected != Current_level_num)
|
|
{
|
|
net_ipx_dump_player(their->player.protocol.ipx.server, their->player.protocol.ipx.node, DUMP_LEVEL);
|
|
return;
|
|
}
|
|
|
|
player_num = -1;
|
|
memset(&IPX_sync_player, 0, sizeof(IPX_sequence_packet));
|
|
Network_player_added = 0;
|
|
|
|
memcpy(&server, their->player.protocol.ipx.server, 4);
|
|
if ( server != 0 )
|
|
ipxdrv_get_local_target( their->player.protocol.ipx.server, their->player.protocol.ipx.node, local_address );
|
|
else
|
|
memcpy(local_address, their->player.protocol.ipx.node, 6);
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if ( (!stricmp(Players[i].callsign, their->player.callsign )) && (!memcmp(Players[i].net_address,local_address, 6)) )
|
|
{
|
|
player_num = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (player_num == -1)
|
|
{
|
|
// Player is new to this game
|
|
|
|
if ( !(Netgame.game_flags & NETGAME_FLAG_CLOSED) && (N_players < MaxNumNetPlayers))
|
|
{
|
|
// Add player in an open slot, game not full yet
|
|
|
|
player_num = N_players;
|
|
Network_player_added = 1;
|
|
}
|
|
else if (Netgame.game_flags & NETGAME_FLAG_CLOSED)
|
|
{
|
|
// Slots are open but game is closed
|
|
net_ipx_dump_player(their->player.protocol.ipx.server, their->player.protocol.ipx.node, DUMP_CLOSED);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Slots are full but game is open, see if anyone is
|
|
// disconnected and replace the oldest player with this new one
|
|
|
|
int oldest_player = -1;
|
|
fix64 oldest_time = timer_query();
|
|
int activeplayers = 0;
|
|
|
|
Assert(N_players == MaxNumNetPlayers);
|
|
|
|
for (i = 0; i < Netgame.numplayers; i++)
|
|
if (Netgame.players[i].connected)
|
|
activeplayers++;
|
|
|
|
if (activeplayers == Netgame.max_numplayers)
|
|
{
|
|
// Game is full.
|
|
net_ipx_dump_player(their->player.protocol.ipx.server, their->player.protocol.ipx.node, DUMP_FULL);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if ( (!Players[i].connected) && (Netgame.players[i].LastPacketTime < oldest_time))
|
|
{
|
|
oldest_time = Netgame.players[i].LastPacketTime;
|
|
oldest_player = i;
|
|
}
|
|
}
|
|
|
|
if (oldest_player == -1)
|
|
{
|
|
// Everyone is still connected
|
|
net_ipx_dump_player(their->player.protocol.ipx.server, their->player.protocol.ipx.node, DUMP_FULL);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Found a slot!
|
|
|
|
player_num = oldest_player;
|
|
Network_player_added = 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Player is reconnecting
|
|
|
|
if (Players[player_num].connected)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Newdemo_state == ND_STATE_RECORDING)
|
|
newdemo_record_multi_reconnect(player_num);
|
|
|
|
Network_player_added = 0;
|
|
|
|
digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
|
|
|
|
if (PlayerCfg.NoRankings)
|
|
HUD_init_message(HM_MULTI, "'%s' %s", Players[player_num].callsign, TXT_REJOIN);
|
|
else
|
|
HUD_init_message(HM_MULTI, "%s'%s' %s", RankStrings[Netgame.players[player_num].rank],Players[player_num].callsign, TXT_REJOIN);
|
|
}
|
|
|
|
Players[player_num].KillGoalCount=0;
|
|
|
|
// Send updated Objects data to the new/returning player
|
|
|
|
|
|
IPX_sync_player = *their;
|
|
IPX_sync_player.player.connected = player_num;
|
|
Network_send_objects = 1;
|
|
Network_send_objnum = -1;
|
|
|
|
net_ipx_send_objects();
|
|
}
|
|
|
|
int net_ipx_objnum_is_past(int objnum)
|
|
{
|
|
// determine whether or not a given object number has already been sent
|
|
// to a re-joining player.
|
|
|
|
int player_num = IPX_sync_player.player.connected;
|
|
int obj_mode = !((object_owner[objnum] == -1) || (object_owner[objnum] == player_num));
|
|
|
|
if (!Network_send_objects)
|
|
return 0; // We're not sending objects to a new player
|
|
|
|
if (obj_mode > Network_send_object_mode)
|
|
return 0;
|
|
else if (obj_mode < Network_send_object_mode)
|
|
return 1;
|
|
else if (objnum < Network_send_objnum)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
#define IPX_OBJ_PACKETS_PER_FRAME 1
|
|
extern void multi_send_active_door(char);
|
|
extern void multi_send_door_open_specific(int,int,int,ubyte);
|
|
|
|
|
|
void net_ipx_send_door_updates(int pnum)
|
|
{
|
|
// Send door status when new player joins
|
|
|
|
int i;
|
|
|
|
pnum=pnum;
|
|
|
|
// Assert (pnum>-1 && pnum<N_players);
|
|
|
|
for (i = 0; i < Num_walls; i++)
|
|
{
|
|
if ((Walls[i].type == WALL_DOOR) && ((Walls[i].state == WALL_DOOR_OPENING) || (Walls[i].state == WALL_DOOR_WAITING) || (Walls[i].state == WALL_DOOR_OPEN)))
|
|
multi_send_door_open_specific(pnum,Walls[i].segnum, Walls[i].sidenum,Walls[i].flags);
|
|
else if ((Walls[i].type == WALL_BLASTABLE) && (Walls[i].flags & WALL_BLASTED))
|
|
multi_send_door_open_specific(pnum,Walls[i].segnum, Walls[i].sidenum,Walls[i].flags);
|
|
else if ((Walls[i].type == WALL_BLASTABLE) && (Walls[i].hps != WALL_HPS))
|
|
multi_send_hostage_door_status(i);
|
|
else
|
|
multi_send_wall_status_specific(pnum,i,Walls[i].type,Walls[i].flags,Walls[i].state);
|
|
}
|
|
}
|
|
|
|
extern vms_vector MarkerPoint[];
|
|
void net_ipx_send_markers()
|
|
{
|
|
// send marker positions/text to new player
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if (MarkerObject[(i*2)]!=-1)
|
|
multi_send_drop_marker (i,MarkerPoint[(i*2)],0,MarkerMessage[i*2]);
|
|
if (MarkerObject[(i*2)+1]!=-1)
|
|
multi_send_drop_marker (i,MarkerPoint[(i*2)+1],1,MarkerMessage[(i*2)+1]);
|
|
}
|
|
}
|
|
|
|
void net_ipx_process_monitor_vector(int vector)
|
|
{
|
|
int i, j;
|
|
int count = 0;
|
|
segment *seg;
|
|
|
|
for (i=0; i <= Highest_segment_index; i++)
|
|
{
|
|
int tm, ec, bm;
|
|
seg = &Segments[i];
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
if ( ((tm = seg->sides[j].tmap_num2) != 0) &&
|
|
((ec = TmapInfo[tm&0x3fff].eclip_num) != -1) &&
|
|
((bm = Effects[ec].dest_bm_num) != -1) )
|
|
{
|
|
if (vector & (1 << count))
|
|
{
|
|
seg->sides[j].tmap_num2 = bm | (tm&0xc000);
|
|
}
|
|
count++;
|
|
Assert(count < 32);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int net_ipx_create_monitor_vector(void)
|
|
{
|
|
int i, j, k;
|
|
int num_blown_bitmaps = 0;
|
|
int monitor_num = 0;
|
|
#define NUM_BLOWN_BITMAPS 20
|
|
int blown_bitmaps[NUM_BLOWN_BITMAPS];
|
|
int vector = 0;
|
|
segment *seg;
|
|
|
|
for (i=0; i < Num_effects; i++)
|
|
{
|
|
if (Effects[i].dest_bm_num > 0) {
|
|
for (j = 0; j < num_blown_bitmaps; j++)
|
|
if (blown_bitmaps[j] == Effects[i].dest_bm_num)
|
|
break;
|
|
if (j == num_blown_bitmaps) {
|
|
blown_bitmaps[num_blown_bitmaps++] = Effects[i].dest_bm_num;
|
|
Assert(num_blown_bitmaps < NUM_BLOWN_BITMAPS);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=0; i <= Highest_segment_index; i++)
|
|
{
|
|
int tm, ec;
|
|
seg = &Segments[i];
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
if ((tm = seg->sides[j].tmap_num2) != 0)
|
|
{
|
|
if ( ((ec = TmapInfo[tm&0x3fff].eclip_num) != -1) &&
|
|
(Effects[ec].dest_bm_num != -1) )
|
|
{
|
|
monitor_num++;
|
|
Assert(monitor_num < 32);
|
|
}
|
|
else
|
|
{
|
|
for (k = 0; k < num_blown_bitmaps; k++)
|
|
{
|
|
if ((tm&0x3fff) == blown_bitmaps[k])
|
|
{
|
|
vector |= (1 << monitor_num);
|
|
monitor_num++;
|
|
Assert(monitor_num < 32);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(vector);
|
|
}
|
|
|
|
void net_ipx_stop_resync(IPX_sequence_packet *their)
|
|
{
|
|
if ( (!memcmp(IPX_sync_player.player.protocol.ipx.node, their->player.protocol.ipx.node, 6)) &&
|
|
(!memcmp(IPX_sync_player.player.protocol.ipx.server, their->player.protocol.ipx.server, 4)) &&
|
|
(!stricmp(IPX_sync_player.player.callsign, their->player.callsign)) )
|
|
{
|
|
Network_send_objects = 0;
|
|
Network_sending_extras=0;
|
|
Network_rejoined=0;
|
|
Player_joining_extras=-1;
|
|
Network_send_objnum = -1;
|
|
}
|
|
}
|
|
|
|
ubyte object_buffer[MAX_DATA_SIZE];
|
|
|
|
void net_ipx_send_objects(void)
|
|
{
|
|
short remote_objnum;
|
|
sbyte owner;
|
|
int loc, i, h;
|
|
|
|
static int obj_count = 0;
|
|
static int frame_num = 0;
|
|
|
|
int obj_count_frame = 0;
|
|
int player_num = IPX_sync_player.player.connected;
|
|
static fix64 last_send_time = 0;
|
|
|
|
if (last_send_time + (F1_0/50) > timer_query())
|
|
return;
|
|
last_send_time = timer_query();
|
|
|
|
// Send clear objects array trigger and send player num
|
|
|
|
Assert(Network_send_objects != 0);
|
|
Assert(player_num >= 0);
|
|
Assert(player_num < MaxNumNetPlayers);
|
|
|
|
if (Endlevel_sequence || Control_center_destroyed)
|
|
{
|
|
// Endlevel started before we finished sending the goods, we'll
|
|
// have to stop and try again after the level.
|
|
|
|
net_ipx_dump_player(IPX_sync_player.player.protocol.ipx.server,IPX_sync_player.player.protocol.ipx.node, DUMP_ENDLEVEL);
|
|
Network_send_objects = 0;
|
|
return;
|
|
}
|
|
|
|
for (h = 0; h < IPX_OBJ_PACKETS_PER_FRAME; h++) // Do more than 1 per frame, try to speed it up without over-stressing the receiver.
|
|
{
|
|
obj_count_frame = 0;
|
|
memset(object_buffer, 0, MAX_DATA_SIZE);
|
|
object_buffer[0] = PID_OBJECT_DATA;
|
|
loc = 3;
|
|
|
|
if (Network_send_objnum == -1)
|
|
{
|
|
obj_count = 0;
|
|
Network_send_object_mode = 0;
|
|
*(short *)(object_buffer+loc) = INTEL_SHORT(-1); loc += 2;
|
|
object_buffer[loc] = player_num; loc += 1;
|
|
/* Placeholder for remote_objnum, not used here */ loc += 2;
|
|
Network_send_objnum = 0;
|
|
obj_count_frame = 1;
|
|
frame_num = 0;
|
|
}
|
|
|
|
for (i = Network_send_objnum; i <= Highest_object_index; i++)
|
|
{
|
|
if ((Objects[i].type != OBJ_POWERUP) && (Objects[i].type != OBJ_PLAYER) &&
|
|
(Objects[i].type != OBJ_CNTRLCEN) && (Objects[i].type != OBJ_GHOST) &&
|
|
(Objects[i].type != OBJ_ROBOT) && (Objects[i].type != OBJ_HOSTAGE) &&
|
|
!(Objects[i].type==OBJ_WEAPON && Objects[i].id==PMINE_ID))
|
|
continue;
|
|
if ((Network_send_object_mode == 0) && ((object_owner[i] != -1) && (object_owner[i] != player_num)))
|
|
continue;
|
|
if ((Network_send_object_mode == 1) && ((object_owner[i] == -1) || (object_owner[i] == player_num)))
|
|
continue;
|
|
|
|
if ( loc + sizeof(object_rw) + 5 > MAX_DATA_SIZE-1 )
|
|
break; // Not enough room for another object
|
|
|
|
obj_count_frame++;
|
|
obj_count++;
|
|
|
|
remote_objnum = objnum_local_to_remote((short)i, &owner);
|
|
Assert(owner == object_owner[i]);
|
|
|
|
*(short *)(object_buffer+loc) = INTEL_SHORT((short)i); loc += 2;
|
|
object_buffer[loc] = owner; loc += 1;
|
|
*(short *)(object_buffer+loc) = INTEL_SHORT(remote_objnum); loc += 2;
|
|
// use object_rw to send objects for now. if object sometime contains some day contains something useful the client should know about, we should use it. but by now it's also easier to use object_rw because then we also do not need fix64 timer values.
|
|
multi_object_to_object_rw(&Objects[i], (object_rw *)&object_buffer[loc]);
|
|
#ifdef WORDS_BIGENDIAN
|
|
object_rw_swap((object_rw *)&object_buffer[loc], 1);
|
|
#endif
|
|
loc += sizeof(object_rw);
|
|
}
|
|
|
|
if (obj_count_frame) // Send any objects we've buffered
|
|
{
|
|
frame_num++;
|
|
|
|
Network_send_objnum = i;
|
|
object_buffer[1] = obj_count_frame; object_buffer[2] = frame_num;
|
|
|
|
Assert(loc <= MAX_DATA_SIZE);
|
|
|
|
ipxdrv_send_internetwork_packet_data( object_buffer, loc, IPX_sync_player.player.protocol.ipx.server, IPX_sync_player.player.protocol.ipx.node );
|
|
}
|
|
|
|
if (i > Highest_object_index)
|
|
{
|
|
if (Network_send_object_mode == 0)
|
|
{
|
|
Network_send_objnum = 0;
|
|
Network_send_object_mode = 1; // go to next mode
|
|
}
|
|
else
|
|
{
|
|
Assert(Network_send_object_mode == 1);
|
|
|
|
frame_num++;
|
|
// Send count so other side can make sure he got them all
|
|
object_buffer[0] = PID_OBJECT_DATA;
|
|
object_buffer[1] = 1;
|
|
object_buffer[2] = frame_num;
|
|
*(short *)(object_buffer+3) = INTEL_SHORT(-2);
|
|
*(short *)(object_buffer+6) = INTEL_SHORT(obj_count);
|
|
ipxdrv_send_internetwork_packet_data(object_buffer, 8, IPX_sync_player.player.protocol.ipx.server, IPX_sync_player.player.protocol.ipx.node);
|
|
|
|
// Send sync packet which tells the player who he is and to start!
|
|
net_ipx_send_rejoin_sync(player_num);
|
|
VerifyPlayerJoined=player_num;
|
|
|
|
// Turn off send object mode
|
|
Network_send_objnum = -1;
|
|
Network_send_objects = 0;
|
|
obj_count = 0;
|
|
|
|
//if (!multi_i_am_master ())
|
|
// Int3(); // Bad!! Get Jason. Someone goofy is trying to get ahold of the game!
|
|
|
|
Network_sending_extras=8; // start to send extras
|
|
Player_joining_extras=player_num;
|
|
|
|
return;
|
|
} // mode == 1;
|
|
} // i > Highest_object_index
|
|
} // For PACKETS_PER_FRAME
|
|
}
|
|
|
|
void net_ipx_send_rejoin_sync(int player_num)
|
|
{
|
|
int i, j;
|
|
|
|
Players[player_num].connected = CONNECT_PLAYING; // connect the new guy
|
|
Netgame.players[player_num].LastPacketTime = timer_query();
|
|
|
|
if (Endlevel_sequence || Control_center_destroyed)
|
|
{
|
|
// Endlevel started before we finished sending the goods, we'll
|
|
// have to stop and try again after the level.
|
|
|
|
net_ipx_dump_player(IPX_sync_player.player.protocol.ipx.server,IPX_sync_player.player.protocol.ipx.node, DUMP_ENDLEVEL);
|
|
|
|
Network_send_objects = 0;
|
|
Network_sending_extras=0;
|
|
return;
|
|
}
|
|
|
|
if (Network_player_added)
|
|
{
|
|
IPX_sync_player.type = PID_ADDPLAYER;
|
|
IPX_sync_player.player.connected = player_num;
|
|
net_ipx_new_player(&IPX_sync_player);
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if ((i != player_num) && (i != Player_num) && (Players[i].connected))
|
|
net_ipx_send_sequence_packet( IPX_sync_player, Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node, Players[i].net_address);
|
|
}
|
|
}
|
|
|
|
// Send sync packet to the new guy
|
|
|
|
net_ipx_update_netgame();
|
|
|
|
// Fill in the kill list
|
|
for (j=0; j<MAX_PLAYERS; j++)
|
|
{
|
|
for (i=0; i<MAX_PLAYERS;i++)
|
|
Netgame.kills[j][i] = kill_matrix[j][i];
|
|
Netgame.killed[j] = Players[j].net_killed_total;
|
|
Netgame.player_kills[j] = Players[j].net_kills_total;
|
|
Netgame.player_score[j] = Players[j].score;
|
|
}
|
|
|
|
Netgame.level_time = Players[Player_num].time_level;
|
|
Netgame.monitor_vector = net_ipx_create_monitor_vector();
|
|
|
|
send_netgame_packet(IPX_sync_player.player.protocol.ipx.server, IPX_sync_player.player.protocol.ipx.node, NULL, 0);
|
|
send_netplayers_packet(IPX_sync_player.player.protocol.ipx.server, IPX_sync_player.player.protocol.ipx.node);
|
|
|
|
return;
|
|
}
|
|
|
|
void resend_sync_due_to_packet_loss_for_allender ()
|
|
{
|
|
int i,j;
|
|
|
|
net_ipx_update_netgame();
|
|
|
|
// Fill in the kill list
|
|
for (j=0; j<MAX_PLAYERS; j++)
|
|
{
|
|
for (i=0; i<MAX_PLAYERS;i++)
|
|
Netgame.kills[j][i] = kill_matrix[j][i];
|
|
Netgame.killed[j] = Players[j].net_killed_total;
|
|
Netgame.player_kills[j] = Players[j].net_kills_total;
|
|
Netgame.player_score[j] = Players[j].score;
|
|
}
|
|
|
|
Netgame.level_time = Players[Player_num].time_level;
|
|
Netgame.monitor_vector = net_ipx_create_monitor_vector();
|
|
|
|
send_netgame_packet(IPX_sync_player.player.protocol.ipx.server, IPX_sync_player.player.protocol.ipx.node, NULL, 0);
|
|
send_netplayers_packet(IPX_sync_player.player.protocol.ipx.server, IPX_sync_player.player.protocol.ipx.node);
|
|
}
|
|
|
|
char * net_ipx_get_player_name( int objnum )
|
|
{
|
|
if ( objnum < 0 ) return NULL;
|
|
if ( Objects[objnum].type != OBJ_PLAYER ) return NULL;
|
|
if ( Objects[objnum].id >= MAX_PLAYERS ) return NULL;
|
|
if ( Objects[objnum].id >= N_players ) return NULL;
|
|
|
|
return Players[Objects[objnum].id].callsign;
|
|
}
|
|
|
|
void net_ipx_add_player(IPX_sequence_packet *p)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<N_players; i++ ) {
|
|
if ( !memcmp( Netgame.players[i].protocol.ipx.node, p->player.protocol.ipx.node, 6) && !memcmp(Netgame.players[i].protocol.ipx.server, p->player.protocol.ipx.server, 4))
|
|
return; // already got them
|
|
}
|
|
|
|
memcpy( Netgame.players[N_players].protocol.ipx.node, p->player.protocol.ipx.node, 6 );
|
|
memcpy( Netgame.players[N_players].protocol.ipx.server, p->player.protocol.ipx.server, 4 );
|
|
|
|
ClipRank (&p->player.rank);
|
|
|
|
memcpy( Netgame.players[N_players].callsign, p->player.callsign, CALLSIGN_LEN+1 );
|
|
Netgame.players[N_players].version_major=p->player.version_major;
|
|
Netgame.players[N_players].version_minor=p->player.version_minor;
|
|
Netgame.players[N_players].rank=p->player.rank;
|
|
Netgame.players[N_players].connected = CONNECT_PLAYING;
|
|
net_ipx_check_for_old_version (N_players);
|
|
|
|
Players[N_players].KillGoalCount=0;
|
|
Players[N_players].connected = CONNECT_PLAYING;
|
|
Netgame.players[N_players].LastPacketTime = timer_query();
|
|
N_players++;
|
|
Netgame.numplayers = N_players;
|
|
|
|
// Broadcast updated info
|
|
|
|
net_ipx_send_game_info(NULL);
|
|
}
|
|
|
|
// One of the players decided not to join the game
|
|
|
|
void net_ipx_remove_player(IPX_sequence_packet *p)
|
|
{
|
|
int i,pn;
|
|
|
|
pn = -1;
|
|
for (i=0; i<N_players; i++ ) {
|
|
if (!memcmp(Netgame.players[i].protocol.ipx.node, p->player.protocol.ipx.node, 6) && !memcmp(Netgame.players[i].protocol.ipx.server, p->player.protocol.ipx.server, 4)) {
|
|
pn = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pn < 0 ) return;
|
|
|
|
for (i=pn; i<N_players-1; i++ ) {
|
|
memcpy( Netgame.players[i].protocol.ipx.node, Netgame.players[i+1].protocol.ipx.node, 6 );
|
|
memcpy( Netgame.players[i].protocol.ipx.server, Netgame.players[i+1].protocol.ipx.server, 4 );
|
|
memcpy( Netgame.players[i].callsign, Netgame.players[i+1].callsign, CALLSIGN_LEN+1 );
|
|
Netgame.players[i].version_major=Netgame.players[i+1].version_major;
|
|
Netgame.players[i].version_minor=Netgame.players[i+1].version_minor;
|
|
|
|
Netgame.players[i].rank=Netgame.players[i+1].rank;
|
|
ClipRank (&Netgame.players[i].rank);
|
|
net_ipx_check_for_old_version(i);
|
|
}
|
|
|
|
N_players--;
|
|
Netgame.numplayers = N_players;
|
|
|
|
// Broadcast new info
|
|
|
|
net_ipx_send_game_info(NULL);
|
|
|
|
}
|
|
|
|
void
|
|
net_ipx_dump_player(ubyte * server, ubyte *node, int why)
|
|
{
|
|
// Inform player that he was not chosen for the netgame
|
|
|
|
IPX_sequence_packet temp;
|
|
|
|
memset(&temp, 0, sizeof(IPX_sequence_packet));
|
|
temp.type = PID_DUMP;
|
|
memcpy(temp.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
|
|
temp.player.connected = why;
|
|
net_ipx_send_sequence_packet( temp, server, node, NULL);
|
|
}
|
|
|
|
void
|
|
net_ipx_send_game_list_request()
|
|
{
|
|
// Send a broadcast request for game info
|
|
|
|
IPX_sequence_packet me;
|
|
|
|
memset(&me, 0, sizeof(IPX_sequence_packet));
|
|
me.type = PID_GAME_LIST;
|
|
memcpy( me.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
|
|
|
|
memcpy( me.player.protocol.ipx.node, ipxdrv_get_my_local_address(), 6 );
|
|
memcpy( me.player.protocol.ipx.server, ipxdrv_get_my_server_address(), 4 );
|
|
|
|
net_ipx_send_sequence_packet( me, NULL, NULL, NULL);
|
|
}
|
|
|
|
void net_ipx_send_all_info_request(char type,int which_security)
|
|
{
|
|
// Send a broadcast request for game info
|
|
|
|
IPX_sequence_packet me;
|
|
|
|
memset(&me, 0, sizeof(IPX_sequence_packet));
|
|
me.Security=which_security;
|
|
me.type = type;
|
|
memcpy( me.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
|
|
|
|
memcpy( me.player.protocol.ipx.node, ipxdrv_get_my_local_address(), 6 );
|
|
memcpy( me.player.protocol.ipx.server, ipxdrv_get_my_server_address(), 4 );
|
|
net_ipx_send_sequence_packet( me, NULL, NULL, NULL);
|
|
}
|
|
|
|
|
|
void
|
|
net_ipx_update_netgame(void)
|
|
{
|
|
// Update the netgame struct with current game variables
|
|
|
|
int i, j;
|
|
|
|
Netgame.numconnected=0;
|
|
for (i=0;i<N_players;i++)
|
|
if (Players[i].connected)
|
|
Netgame.numconnected++;
|
|
|
|
// This is great: D2 1.0 and 1.1 ignore upper part of the game_flags field of
|
|
// the lite_info struct when you're sitting on the join netgame screen. We can
|
|
// "sneak" Hoard information into this field. This is better than sending
|
|
// another packet that could be lost in transit.
|
|
|
|
|
|
if (HoardEquipped())
|
|
{
|
|
if (Game_mode & GM_HOARD)
|
|
{
|
|
Netgame.game_flags |=NETGAME_FLAG_HOARD;
|
|
if (Game_mode & GM_TEAM)
|
|
Netgame.game_flags |=NETGAME_FLAG_TEAM_HOARD;
|
|
}
|
|
}
|
|
|
|
if (Network_status == NETSTAT_STARTING)
|
|
return;
|
|
|
|
Netgame.numplayers = N_players;
|
|
Netgame.game_status = Network_status;
|
|
Netgame.max_numplayers = MaxNumNetPlayers;
|
|
|
|
for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
|
|
{
|
|
Netgame.players[i].connected = Players[i].connected;
|
|
for(j = 0; j < MAX_NUM_NET_PLAYERS; j++)
|
|
Netgame.kills[i][j] = kill_matrix[i][j];
|
|
|
|
Netgame.killed[i] = Players[i].net_killed_total;
|
|
Netgame.player_kills[i] = Players[i].net_kills_total;
|
|
Netgame.player_score[i] = Players[i].score;
|
|
Netgame.player_flags[i] = (Players[i].flags & (PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY));
|
|
}
|
|
Netgame.team_kills[0] = team_kills[0];
|
|
Netgame.team_kills[1] = team_kills[1];
|
|
Netgame.levelnum = Current_level_num;
|
|
|
|
|
|
}
|
|
|
|
void
|
|
net_ipx_send_endlevel_sub(int player_num)
|
|
{
|
|
IPX_endlevel_info end;
|
|
int i;
|
|
#ifdef WORDS_BIGENDIAN
|
|
int j;
|
|
#endif
|
|
|
|
// Send an endlevel packet for a player
|
|
memset(&end,0,sizeof(IPX_endlevel_info));
|
|
end.type = PID_ENDLEVEL;
|
|
end.player_num = player_num;
|
|
end.connected = Players[player_num].connected;
|
|
end.kills = INTEL_SHORT(Players[player_num].net_kills_total);
|
|
end.killed = INTEL_SHORT(Players[player_num].net_killed_total);
|
|
memcpy(end.kill_matrix, kill_matrix[player_num], MAX_PLAYERS*sizeof(short));
|
|
#ifdef WORDS_BIGENDIAN
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
for (j = 0; j < MAX_PLAYERS; j++)
|
|
end.kill_matrix[i][j] = INTEL_SHORT(end.kill_matrix[i][j]);
|
|
#endif
|
|
|
|
if (Players[player_num].connected == CONNECT_PLAYING) // Still playing
|
|
{
|
|
Assert(Control_center_destroyed);
|
|
end.seconds_left = Countdown_seconds_left;
|
|
}
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if ((i != Player_num) && (i!=player_num) && (Players[i].connected)) {
|
|
if (Players[i].connected==CONNECT_PLAYING) {
|
|
net_ipx_send_endlevel_short_sub(player_num,i);
|
|
} else {
|
|
ipxdrv_send_packet_data((ubyte *)&end, sizeof(IPX_endlevel_info), Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node,Players[i].net_address);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Send an updated endlevel status to other hosts */
|
|
void
|
|
net_ipx_send_endlevel_packet(void)
|
|
{
|
|
net_ipx_send_endlevel_sub(Player_num);
|
|
}
|
|
|
|
|
|
/* Send an endlevel packet for a player */
|
|
void
|
|
net_ipx_send_endlevel_short_sub(int from_player_num,int to_player)
|
|
{
|
|
IPX_endlevel_info_short end;
|
|
|
|
end.type = PID_ENDLEVEL_SHORT;
|
|
end.player_num = from_player_num;
|
|
end.connected = Players[from_player_num].connected;
|
|
end.seconds_left = Countdown_seconds_left;
|
|
|
|
|
|
if (Players[from_player_num].connected == CONNECT_PLAYING) // Still playing
|
|
{
|
|
Assert(Control_center_destroyed);
|
|
}
|
|
|
|
if ((to_player != Player_num) && (to_player!=from_player_num) && (Players[to_player].connected))
|
|
{
|
|
ipxdrv_send_packet_data((ubyte *)&end, sizeof(IPX_endlevel_info_short), Netgame.players[to_player].protocol.ipx.server, Netgame.players[to_player].protocol.ipx.node,Players[to_player].net_address);
|
|
}
|
|
}
|
|
|
|
extern fix ThisLevelTime;
|
|
|
|
void
|
|
net_ipx_send_game_info(IPX_sequence_packet *their)
|
|
{
|
|
// Send game info to someone who requested it
|
|
|
|
char old_type, old_status;
|
|
fix timevar;
|
|
int i;
|
|
|
|
net_ipx_update_netgame(); // Update the values in the netgame struct
|
|
|
|
old_type = Netgame.protocol.ipx.Game_pkt_type;
|
|
old_status = Netgame.game_status;
|
|
|
|
Netgame.protocol.ipx.Game_pkt_type = PID_GAME_INFO;
|
|
Netgame.protocol.ipx.Player_pkt_type = PID_PLAYERSINFO;
|
|
|
|
Netgame.protocol.ipx.Player_Security=Netgame.protocol.ipx.Game_Security;
|
|
Netgame.version_major=Version_major;
|
|
Netgame.version_minor=Version_minor;
|
|
|
|
if (Endlevel_sequence || Control_center_destroyed)
|
|
Netgame.game_status = NETSTAT_ENDLEVEL;
|
|
|
|
if (Netgame.PlayTimeAllowed)
|
|
{
|
|
timevar=i2f (Netgame.PlayTimeAllowed*5*60);
|
|
i=f2i(timevar-ThisLevelTime);
|
|
if (i<30)
|
|
Netgame.game_status=NETSTAT_ENDLEVEL;
|
|
}
|
|
|
|
if (!their) {
|
|
send_netgame_packet(NULL, NULL, NULL, 0);
|
|
send_netplayers_packet(NULL, NULL);
|
|
} else {
|
|
send_netgame_packet(their->player.protocol.ipx.server, their->player.protocol.ipx.node, NULL, 0);
|
|
send_netplayers_packet(their->player.protocol.ipx.server, their->player.protocol.ipx.node);
|
|
}
|
|
|
|
Netgame.protocol.ipx.Game_pkt_type = old_type;
|
|
Netgame.game_status = old_status;
|
|
}
|
|
|
|
void net_ipx_send_lite_info(IPX_sequence_packet *their)
|
|
{
|
|
// Send game info to someone who requested it
|
|
|
|
char old_type, old_status,oldstatus;
|
|
|
|
net_ipx_update_netgame(); // Update the values in the netgame struct
|
|
|
|
old_type = Netgame.protocol.ipx.Game_pkt_type;
|
|
old_status = Netgame.game_status;
|
|
|
|
Netgame.protocol.ipx.Game_pkt_type = PID_LITE_INFO;
|
|
|
|
if (Endlevel_sequence || Control_center_destroyed)
|
|
Netgame.game_status = NETSTAT_ENDLEVEL;
|
|
|
|
// If hoard mode, make this game look closed even if it isn't
|
|
if (HoardEquipped())
|
|
{
|
|
if (Game_mode & GM_HOARD)
|
|
{
|
|
oldstatus=Netgame.game_status;
|
|
Netgame.game_status=NETSTAT_ENDLEVEL;
|
|
Netgame.gamemode=NETGAME_CAPTURE_FLAG;
|
|
if (oldstatus==NETSTAT_ENDLEVEL)
|
|
Netgame.game_flags|=NETGAME_FLAG_REALLY_ENDLEVEL;
|
|
if (oldstatus==NETSTAT_STARTING)
|
|
Netgame.game_flags|=NETGAME_FLAG_REALLY_FORMING;
|
|
}
|
|
}
|
|
|
|
if (!their) {
|
|
send_netgame_packet(NULL, NULL, NULL, 1);
|
|
} else {
|
|
send_netgame_packet(their->player.protocol.ipx.server, their->player.protocol.ipx.node, NULL, 1);
|
|
}
|
|
|
|
// Restore the pre-hoard mode
|
|
if (HoardEquipped())
|
|
{
|
|
if (Game_mode & GM_HOARD)
|
|
{
|
|
if (!(Game_mode & GM_TEAM))
|
|
Netgame.gamemode=NETGAME_HOARD;
|
|
else
|
|
Netgame.gamemode=NETGAME_TEAM_HOARD;
|
|
Netgame.game_flags&=~NETGAME_FLAG_REALLY_ENDLEVEL;
|
|
Netgame.game_flags&=~NETGAME_FLAG_REALLY_FORMING;
|
|
Netgame.game_flags&=~NETGAME_FLAG_TEAM_HOARD;
|
|
}
|
|
}
|
|
|
|
Netgame.protocol.ipx.Game_pkt_type = old_type;
|
|
Netgame.game_status = old_status;
|
|
|
|
}
|
|
|
|
/* Send game info to all players in this game */
|
|
void net_ipx_send_netgame_update()
|
|
{
|
|
char old_type, old_status;
|
|
int i;
|
|
|
|
net_ipx_update_netgame(); // Update the values in the netgame struct
|
|
|
|
old_type = Netgame.protocol.ipx.Game_pkt_type;
|
|
old_status = Netgame.game_status;
|
|
|
|
Netgame.protocol.ipx.Game_pkt_type = PID_GAME_UPDATE;
|
|
|
|
if (Endlevel_sequence || Control_center_destroyed)
|
|
Netgame.game_status = NETSTAT_ENDLEVEL;
|
|
|
|
for (i=0; i<N_players; i++ ) {
|
|
if ( (Players[i].connected) && (i!=Player_num ) ) {
|
|
send_netgame_packet(Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node, Players[i].net_address, 1);
|
|
}
|
|
}
|
|
|
|
Netgame.protocol.ipx.Game_pkt_type = old_type;
|
|
Netgame.game_status = old_status;
|
|
}
|
|
|
|
int net_ipx_send_request(void)
|
|
{
|
|
// Send a request to join a game 'Netgame'. Returns 0 if we can join this
|
|
// game, non-zero if there is some problem.
|
|
int i;
|
|
|
|
if (Netgame.numplayers < 1)
|
|
return 1;
|
|
|
|
for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
|
|
if (Netgame.players[i].connected)
|
|
break;
|
|
|
|
Assert(i < MAX_NUM_NET_PLAYERS);
|
|
|
|
IPX_Seq.type = PID_REQUEST;
|
|
IPX_Seq.player.connected = Current_level_num;
|
|
|
|
net_ipx_send_sequence_packet(IPX_Seq, Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node, NULL);
|
|
|
|
return i;
|
|
}
|
|
|
|
int SecurityCheck=0;
|
|
|
|
void net_ipx_process_gameinfo(ubyte *data)
|
|
{
|
|
int i, j;
|
|
netgame_info *new = (netgame_info *)data;
|
|
netgame_info tmp_info;
|
|
|
|
receive_netgame_packet(data, &tmp_info, 0); // get correctly aligned structure
|
|
new = &tmp_info;
|
|
|
|
WaitingForPlayerInfo=0;
|
|
|
|
if (new->protocol.ipx.Game_Security !=TempPlayersInfo->protocol.ipx.Game_Security)
|
|
{
|
|
Int3(); // Get Jason
|
|
return; // If this first half doesn't go with the second half
|
|
}
|
|
|
|
num_active_ipx_changed = 1;
|
|
|
|
Assert (TempPlayersInfo!=NULL);
|
|
|
|
for (i = 0; i < num_active_ipx_games; i++)
|
|
{
|
|
if (!stricmp(Active_ipx_games[i].game_name, new->game_name) && Active_ipx_games[i].protocol.ipx.Game_Security==new->protocol.ipx.Game_Security)
|
|
break;
|
|
}
|
|
|
|
if (i == IPX_MAX_NETGAMES)
|
|
{
|
|
return;
|
|
}
|
|
|
|
memcpy(&Active_ipx_games[i], (ubyte *)new, sizeof(netgame_info));
|
|
memcpy (&Active_ipx_games[i].players,TempPlayersInfo->players,sizeof(netplayer_info)*(MAX_PLAYERS+4));
|
|
|
|
if (SecurityCheck)
|
|
if (Active_ipx_games[i].protocol.ipx.Game_Security==SecurityCheck)
|
|
SecurityCheck=-1;
|
|
|
|
if (i == num_active_ipx_games)
|
|
num_active_ipx_games++;
|
|
|
|
if (Active_ipx_games[i].numplayers == 0)
|
|
{
|
|
// Delete this game
|
|
for (j = i; j < num_active_ipx_games-1; j++)
|
|
{
|
|
memcpy(&Active_ipx_games[j], &Active_ipx_games[j+1], sizeof(netgame_info));
|
|
}
|
|
num_active_ipx_games--;
|
|
SecurityCheck=0;
|
|
}
|
|
}
|
|
|
|
void net_ipx_process_lite_info(ubyte *data)
|
|
{
|
|
int i, j;
|
|
netgame_info *new = (netgame_info *)data;
|
|
netgame_info tmp_info;
|
|
|
|
receive_netgame_packet(data, (netgame_info *)&tmp_info, 1);
|
|
new = &tmp_info;
|
|
|
|
num_active_ipx_changed = 1;
|
|
|
|
for (i = 0; i < num_active_ipx_games; i++)
|
|
{
|
|
if ((!stricmp(Active_ipx_games[i].game_name, new->game_name)) && Active_ipx_games[i].protocol.ipx.Game_Security==new->protocol.ipx.Game_Security)
|
|
break;
|
|
}
|
|
|
|
if (i == IPX_MAX_NETGAMES)
|
|
{
|
|
return;
|
|
}
|
|
|
|
memcpy(&Active_ipx_games[i], (ubyte *)new, sizeof(netgame_info));
|
|
|
|
// See if this is really a Hoard game
|
|
// If so, adjust all the data accordingly
|
|
if (HoardEquipped())
|
|
{
|
|
if (Active_ipx_games[i].game_flags & NETGAME_FLAG_HOARD)
|
|
{
|
|
Active_ipx_games[i].gamemode=NETGAME_HOARD;
|
|
Active_ipx_games[i].game_status=NETSTAT_PLAYING;
|
|
|
|
if (Active_ipx_games[i].game_flags & NETGAME_FLAG_TEAM_HOARD)
|
|
Active_ipx_games[i].gamemode=NETGAME_TEAM_HOARD;
|
|
if (Active_ipx_games[i].game_flags & NETGAME_FLAG_REALLY_ENDLEVEL)
|
|
Active_ipx_games[i].game_status=NETSTAT_ENDLEVEL;
|
|
if (Active_ipx_games[i].game_flags & NETGAME_FLAG_REALLY_FORMING)
|
|
Active_ipx_games[i].game_status=NETSTAT_STARTING;
|
|
}
|
|
}
|
|
|
|
if (i == num_active_ipx_games)
|
|
num_active_ipx_games++;
|
|
|
|
if (Active_ipx_games[i].numplayers == 0)
|
|
{
|
|
// Delete this game
|
|
for (j = i; j < num_active_ipx_games-1; j++)
|
|
{
|
|
memcpy(&Active_ipx_games[j], &Active_ipx_games[j+1], sizeof(netgame_info));
|
|
}
|
|
num_active_ipx_games--;
|
|
}
|
|
}
|
|
|
|
void net_ipx_process_dump(IPX_sequence_packet *their)
|
|
{
|
|
// Our request for join was denied. Tell the user why.
|
|
|
|
int i;
|
|
|
|
if (their->player.connected==DUMP_KICKED)
|
|
{
|
|
for (i=0;i<N_players;i++)
|
|
{
|
|
if (!stricmp (their->player.callsign,Players[i].callsign))
|
|
{
|
|
if (i==multi_who_is_master())
|
|
{
|
|
if (Network_status==NETSTAT_PLAYING)
|
|
multi_leave_game();
|
|
window_set_visible(Game_wind, 0);
|
|
nm_messagebox(NULL, 1, TXT_OK, "%s has kicked you out!",their->player.callsign);
|
|
window_set_visible(Game_wind, 1);
|
|
multi_quit_game = 1;
|
|
game_leave_menus();
|
|
multi_reset_stuff();
|
|
}
|
|
else
|
|
{
|
|
HUD_init_message(HM_MULTI, "%s attempted to kick you out.",their->player.callsign);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nm_messagebox(NULL, 1, TXT_OK, NET_DUMP_STRINGS(their->player.connected));
|
|
Network_status = NETSTAT_MENU;
|
|
}
|
|
}
|
|
void net_ipx_process_request(IPX_sequence_packet *their)
|
|
{
|
|
// Player is ready to receieve a sync packet
|
|
int i;
|
|
|
|
for (i = 0; i < N_players; i++) {
|
|
if (!memcmp(their->player.protocol.ipx.server, Netgame.players[i].protocol.ipx.server, 4) && !memcmp(their->player.protocol.ipx.node, Netgame.players[i].protocol.ipx.node, 6) && (!stricmp(their->player.callsign, Netgame.players[i].callsign))) {
|
|
Players[i].connected = CONNECT_PLAYING;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void multi_reset_object_texture (object *);
|
|
|
|
void net_ipx_process_packet(ubyte *data, int length )
|
|
{
|
|
IPX_sequence_packet *their = (IPX_sequence_packet *)data;
|
|
IPX_sequence_packet tmp_packet;
|
|
|
|
memset(&tmp_packet, 0, sizeof(IPX_sequence_packet));
|
|
|
|
net_ipx_receive_sequence_packet(data, &tmp_packet);
|
|
their = &tmp_packet; // reassign their to point to correctly alinged structure
|
|
|
|
length = length;
|
|
|
|
switch( data[0] )
|
|
{
|
|
|
|
case PID_GAME_INFO: // Jason L. says we can safely ignore this type.
|
|
break;
|
|
|
|
case PID_PLAYERSINFO:
|
|
if (Network_status==NETSTAT_WAITING)
|
|
{
|
|
receive_netplayers_packet(data, &TempPlayersBase);
|
|
|
|
if (TempPlayersBase.protocol.ipx.Player_Security!=Netgame.protocol.ipx.Game_Security)
|
|
{
|
|
break;
|
|
}
|
|
|
|
TempPlayersInfo=&TempPlayersBase;
|
|
WaitingForPlayerInfo=0;
|
|
NetSecurityNum=TempPlayersInfo->protocol.ipx.Player_Security;
|
|
NetSecurityFlag=NETSECURITY_WAIT_FOR_SYNC;
|
|
}
|
|
|
|
break;
|
|
|
|
case PID_LITE_INFO:
|
|
if (Network_status == NETSTAT_BROWSING)
|
|
net_ipx_process_lite_info (data);
|
|
|
|
break;
|
|
|
|
case PID_GAME_LIST:
|
|
// Someone wants a list of games
|
|
if (length != SEQUENCE_PACKET_SIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((Network_status == NETSTAT_PLAYING) || (Network_status == NETSTAT_STARTING) || (Network_status == NETSTAT_ENDLEVEL))
|
|
if (multi_i_am_master())
|
|
net_ipx_send_lite_info(their);
|
|
|
|
break;
|
|
|
|
case PID_SEND_ALL_GAMEINFO:
|
|
if (length != SEQUENCE_PACKET_SIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((Network_status == NETSTAT_PLAYING) || (Network_status == NETSTAT_STARTING) || (Network_status == NETSTAT_ENDLEVEL))
|
|
if (multi_i_am_master() && their->Security==Netgame.protocol.ipx.Game_Security)
|
|
net_ipx_send_game_info(their);
|
|
break;
|
|
|
|
case PID_ADDPLAYER:
|
|
if (length != SEQUENCE_PACKET_SIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
net_ipx_new_player(their);
|
|
|
|
break;
|
|
case PID_REQUEST:
|
|
if (length != SEQUENCE_PACKET_SIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Network_status == NETSTAT_STARTING)
|
|
{
|
|
// Someone wants to join our game!
|
|
net_ipx_add_player(their);
|
|
}
|
|
else if (Network_status == NETSTAT_WAITING)
|
|
{
|
|
// Someone is ready to recieve a sync packet
|
|
net_ipx_process_request(their);
|
|
}
|
|
else if (Network_status == NETSTAT_PLAYING)
|
|
{
|
|
// Someone wants to join a game in progress!
|
|
if (Netgame.RefusePlayers)
|
|
net_ipx_do_refuse_stuff (their);
|
|
else
|
|
net_ipx_welcome_player(their);
|
|
}
|
|
|
|
break;
|
|
|
|
case PID_DUMP:
|
|
|
|
if (length != SEQUENCE_PACKET_SIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Network_status == NETSTAT_WAITING || Network_status==NETSTAT_PLAYING )
|
|
net_ipx_process_dump(their);
|
|
|
|
break;
|
|
case PID_QUIT_JOINING:
|
|
|
|
if (length != SEQUENCE_PACKET_SIZE)
|
|
{
|
|
return;
|
|
}
|
|
if (Network_status == NETSTAT_STARTING)
|
|
net_ipx_remove_player( their );
|
|
else if ((Network_status == NETSTAT_PLAYING) && (Network_send_objects))
|
|
net_ipx_stop_resync( their );
|
|
|
|
break;
|
|
|
|
case PID_SYNC:
|
|
|
|
if (Network_status == NETSTAT_WAITING)
|
|
{
|
|
receive_netgame_packet(data, &TempNetInfo, 0);
|
|
|
|
if (TempNetInfo.protocol.ipx.Game_Security!=Netgame.protocol.ipx.Game_Security)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (NetSecurityFlag==NETSECURITY_WAIT_FOR_SYNC)
|
|
{
|
|
if (TempNetInfo.protocol.ipx.Game_Security==TempPlayersInfo->protocol.ipx.Player_Security)
|
|
{
|
|
net_ipx_read_sync_packet (&TempNetInfo,0);
|
|
NetSecurityFlag=0;
|
|
NetSecurityNum=0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetSecurityFlag=NETSECURITY_WAIT_FOR_PLAYERS;
|
|
NetSecurityNum=TempNetInfo.protocol.ipx.Game_Security;
|
|
|
|
if ( net_ipx_wait_for_playerinfo())
|
|
net_ipx_read_sync_packet((netgame_info *)data,0);
|
|
|
|
NetSecurityFlag=0;
|
|
NetSecurityNum=0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PID_PDATA:
|
|
if ((Game_mode&GM_NETWORK) && ((Network_status == NETSTAT_PLAYING)||(Network_status == NETSTAT_ENDLEVEL) || Network_status==NETSTAT_WAITING)) {
|
|
net_ipx_process_pdata((char *)data);
|
|
}
|
|
break;
|
|
|
|
case PID_NAKED_PDATA:
|
|
if ((Game_mode&GM_NETWORK) && ((Network_status == NETSTAT_PLAYING)||(Network_status == NETSTAT_ENDLEVEL) || Network_status==NETSTAT_WAITING))
|
|
net_ipx_process_naked_pdata((char *)data,length);
|
|
break;
|
|
|
|
case PID_OBJECT_DATA:
|
|
if (Network_status == NETSTAT_WAITING)
|
|
net_ipx_read_object_packet(data);
|
|
break;
|
|
|
|
case PID_ENDLEVEL:
|
|
if ((Network_status == NETSTAT_ENDLEVEL) || (Network_status == NETSTAT_PLAYING))
|
|
net_ipx_read_endlevel_packet(data);
|
|
break;
|
|
|
|
case PID_ENDLEVEL_SHORT:
|
|
if ((Network_status == NETSTAT_ENDLEVEL) || (Network_status == NETSTAT_PLAYING))
|
|
net_ipx_read_endlevel_short_packet(data);
|
|
break;
|
|
|
|
case PID_GAME_UPDATE:
|
|
if (Network_status==NETSTAT_PLAYING)
|
|
{
|
|
memcpy ((ubyte *)&TempNetInfo,&Netgame,sizeof(netgame_info));
|
|
receive_netgame_packet(data, &TempNetInfo, 1);
|
|
|
|
if (TempNetInfo.protocol.ipx.Game_Security==Netgame.protocol.ipx.Game_Security)
|
|
memcpy (&Netgame,(ubyte *)&TempNetInfo,sizeof(netgame_info));
|
|
}
|
|
|
|
if (Game_mode & GM_TEAM)
|
|
{
|
|
int i;
|
|
|
|
for (i=0;i<N_players;i++)
|
|
if (Players[i].connected)
|
|
multi_reset_object_texture (&Objects[Players[i].objnum]);
|
|
reset_cockpit();
|
|
}
|
|
break;
|
|
|
|
case PID_NAMES_RETURN:
|
|
if (Network_status==NETSTAT_BROWSING && NamesInfoSecurity!=-1)
|
|
net_ipx_process_names_return ((ubyte *) data);
|
|
break;
|
|
|
|
case PID_GAME_PLAYERS:
|
|
// Someone wants a list of players in this game
|
|
|
|
if (length != SEQUENCE_PACKET_SIZE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((Network_status == NETSTAT_PLAYING) || (Network_status == NETSTAT_STARTING) || (Network_status == NETSTAT_ENDLEVEL))
|
|
if (multi_i_am_master() && their->Security==Netgame.protocol.ipx.Game_Security)
|
|
net_ipx_send_player_names(their);
|
|
break;
|
|
|
|
default:
|
|
Int3(); // Invalid network packet type, see ROB
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
void dump_segments()
|
|
{
|
|
PHYSFS_file *fp;
|
|
|
|
fp = PHYSFS_openWrite("test.dmp");
|
|
PHYSFS_write(fp, Segments, sizeof(segment), Highest_segment_index + 1);
|
|
PHYSFS_close(fp);
|
|
}
|
|
#endif
|
|
|
|
|
|
void
|
|
net_ipx_read_endlevel_packet( ubyte *data )
|
|
{
|
|
// Special packet for end of level syncing
|
|
int playernum;
|
|
IPX_endlevel_info *end = (IPX_endlevel_info *)data;
|
|
#ifdef WORDS_BIGENDIAN
|
|
int i, j;
|
|
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
for (j = 0; j < MAX_PLAYERS; j++)
|
|
end->kill_matrix[i][j] = INTEL_SHORT(end->kill_matrix[i][j]);
|
|
end->kills = INTEL_SHORT(end->kills);
|
|
end->killed = INTEL_SHORT(end->killed);
|
|
#endif
|
|
|
|
playernum = end->player_num;
|
|
|
|
Assert(playernum != Player_num);
|
|
|
|
if (playernum>=N_players)
|
|
{
|
|
Int3(); // weird, but it an happen in a coop restore game
|
|
return; // if it happens in a coop restore, don't worry about it
|
|
}
|
|
|
|
if ((Network_status == NETSTAT_PLAYING) && (end->connected != CONNECT_DISCONNECTED))
|
|
return; // Only accept disconnect packets if we're not out of the level yet
|
|
|
|
Players[playernum].connected = end->connected;
|
|
memcpy(&kill_matrix[playernum][0], end->kill_matrix, MAX_PLAYERS*sizeof(short));
|
|
Players[playernum].net_kills_total = end->kills;
|
|
Players[playernum].net_killed_total = end->killed;
|
|
|
|
if ((Players[playernum].connected == CONNECT_PLAYING) && (end->seconds_left < Countdown_seconds_left))
|
|
Countdown_seconds_left = end->seconds_left;
|
|
|
|
Netgame.players[playernum].LastPacketTime = timer_query();
|
|
}
|
|
|
|
void
|
|
net_ipx_read_endlevel_short_packet( ubyte *data )
|
|
{
|
|
// Special packet for end of level syncing
|
|
|
|
int playernum;
|
|
IPX_endlevel_info_short *end;
|
|
|
|
end = (IPX_endlevel_info_short *)data;
|
|
|
|
playernum = end->player_num;
|
|
|
|
Assert(playernum != Player_num);
|
|
|
|
if (playernum>=N_players)
|
|
{
|
|
Int3(); // weird, but it can happen in a coop restore game
|
|
return; // if it happens in a coop restore, don't worry about it
|
|
}
|
|
|
|
if ((Network_status == NETSTAT_PLAYING) && (end->connected != CONNECT_DISCONNECTED))
|
|
{
|
|
return; // Only accept disconnect packets if we're not out of the level yet
|
|
}
|
|
|
|
Players[playernum].connected = end->connected;
|
|
|
|
if ((Players[playernum].connected == CONNECT_PLAYING) && (end->seconds_left < Countdown_seconds_left))
|
|
Countdown_seconds_left = end->seconds_left;
|
|
|
|
Netgame.players[playernum].LastPacketTime = timer_query();
|
|
}
|
|
|
|
|
|
void
|
|
net_ipx_pack_objects(void)
|
|
{
|
|
// Switching modes, pack the object array
|
|
|
|
special_reset_objects();
|
|
}
|
|
|
|
int
|
|
net_ipx_verify_objects(int remote, int local)
|
|
{
|
|
int i, nplayers = 0;
|
|
|
|
if ((remote-local) > 10)
|
|
return(2);
|
|
|
|
for (i = 0; i <= Highest_object_index; i++)
|
|
{
|
|
if ((Objects[i].type == OBJ_PLAYER) || (Objects[i].type == OBJ_GHOST))
|
|
nplayers++;
|
|
}
|
|
|
|
if (MaxNumNetPlayers<=nplayers)
|
|
return(0);
|
|
|
|
return(1);
|
|
}
|
|
|
|
void
|
|
net_ipx_read_object_packet( ubyte *data )
|
|
{
|
|
// Object from another net player we need to sync with
|
|
|
|
short objnum, remote_objnum;
|
|
sbyte obj_owner;
|
|
int segnum, i;
|
|
object *obj;
|
|
|
|
static int my_pnum = 0;
|
|
static int mode = 0;
|
|
static int object_count = 0;
|
|
static int frame_num = 0;
|
|
int nobj = data[1];
|
|
int loc = 3;
|
|
int remote_frame_num = data[2];
|
|
|
|
frame_num++;
|
|
|
|
for (i = 0; i < nobj; i++)
|
|
{
|
|
objnum = INTEL_SHORT( *(short *)(data+loc) ); loc += 2;
|
|
obj_owner = data[loc]; loc += 1;
|
|
remote_objnum = INTEL_SHORT( *(short *)(data+loc) ); loc += 2;
|
|
|
|
if (objnum == -1)
|
|
{
|
|
// Clear object array
|
|
init_objects();
|
|
Network_rejoined = 1;
|
|
my_pnum = obj_owner;
|
|
change_playernum_to(my_pnum);
|
|
mode = 1;
|
|
object_count = 0;
|
|
frame_num = 1;
|
|
}
|
|
else if (objnum == -2)
|
|
{
|
|
// Special debug checksum marker for entire send
|
|
if (mode == 1)
|
|
{
|
|
net_ipx_pack_objects();
|
|
mode = 0;
|
|
}
|
|
if (remote_objnum != object_count) {
|
|
Int3();
|
|
}
|
|
if (net_ipx_verify_objects(remote_objnum, object_count))
|
|
{
|
|
// Failed to sync up
|
|
nm_messagebox(NULL, 1, TXT_OK, TXT_NET_SYNC_FAILED);
|
|
Network_status = NETSTAT_MENU;
|
|
return;
|
|
}
|
|
frame_num = 0;
|
|
}
|
|
else
|
|
{
|
|
if (frame_num != remote_frame_num)
|
|
Int3();
|
|
object_count++;
|
|
if ((obj_owner == my_pnum) || (obj_owner == -1))
|
|
{
|
|
if (mode != 1)
|
|
Int3(); // SEE ROB
|
|
objnum = remote_objnum;
|
|
//if (objnum > Highest_object_index)
|
|
//{
|
|
// Highest_object_index = objnum;
|
|
// num_objects = Highest_object_index+1;
|
|
//}
|
|
}
|
|
else {
|
|
if (mode == 1)
|
|
{
|
|
net_ipx_pack_objects();
|
|
mode = 0;
|
|
}
|
|
objnum = obj_allocate();
|
|
}
|
|
if (objnum != -1) {
|
|
obj = &Objects[objnum];
|
|
if (obj->segnum != -1)
|
|
obj_unlink(objnum);
|
|
Assert(obj->segnum == -1);
|
|
Assert(objnum < MAX_OBJECTS);
|
|
#ifdef WORDS_BIGENDIAN
|
|
object_rw_swap((object_rw *)&data[loc], 1);
|
|
#endif
|
|
multi_object_rw_to_object((object_rw *)&data[loc], obj);
|
|
loc += sizeof(object_rw);
|
|
segnum = obj->segnum;
|
|
obj->next = obj->prev = obj->segnum = -1;
|
|
obj->attached_obj = -1;
|
|
if (segnum > -1)
|
|
obj_link(obj-Objects,segnum);
|
|
if (obj_owner == my_pnum)
|
|
map_objnum_local_to_local(objnum);
|
|
else if (obj_owner != -1)
|
|
map_objnum_local_to_remote(objnum, remote_objnum, obj_owner);
|
|
else
|
|
object_owner[objnum] = -1;
|
|
}
|
|
} // For a standard onbject
|
|
} // For each object in packet
|
|
}
|
|
|
|
/* Polling loop waiting for sync packet to start game
|
|
* after having sent request
|
|
*/
|
|
int net_ipx_sync_poll( newmenu *menu, d_event *event, void *userdata )
|
|
{
|
|
static fix64 t1 = 0;
|
|
int rval = 0;
|
|
|
|
menu = menu;
|
|
userdata = userdata;
|
|
|
|
if (event->type != EVENT_WINDOW_DRAW)
|
|
return 0;
|
|
|
|
net_ipx_listen();
|
|
|
|
if (Network_status != NETSTAT_WAITING) // Status changed to playing, exit the menu
|
|
rval = -2;
|
|
|
|
if (Network_status != NETSTAT_MENU && !Network_rejoined && (timer_query() > t1+F1_0*2))
|
|
{
|
|
int i;
|
|
|
|
// Poll time expired, re-send request
|
|
|
|
t1 = timer_query();
|
|
|
|
i = net_ipx_send_request();
|
|
if (i < 0)
|
|
rval = -2;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
int net_ipx_start_poll( newmenu *menu, d_event *event, void *userdata )
|
|
{
|
|
newmenu_item *menus = newmenu_get_items(menu);
|
|
int nitems = newmenu_get_nitems(menu);
|
|
int i,n,nm;
|
|
|
|
if (event->type != EVENT_WINDOW_DRAW)
|
|
return 0;
|
|
|
|
userdata = userdata;
|
|
|
|
Assert(Network_status == NETSTAT_STARTING);
|
|
|
|
if (!menus[0].value) {
|
|
menus[0].value = 1;
|
|
}
|
|
|
|
for (i=1; i<nitems; i++ ) {
|
|
if ( (i>= N_players) && (menus[i].value) ) {
|
|
menus[i].value = 0;
|
|
}
|
|
}
|
|
|
|
nm = 0;
|
|
for (i=0; i<nitems; i++ ) {
|
|
if ( menus[i].value ) {
|
|
nm++;
|
|
if ( nm > N_players ) {
|
|
menus[i].value = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( nm > MaxNumNetPlayers ) {
|
|
nm_messagebox( TXT_ERROR, 1, TXT_OK, "%s %d %s", TXT_SORRY_ONLY, MaxNumNetPlayers, TXT_NETPLAYERS_IN );
|
|
// Turn off the last player highlighted
|
|
for (i = N_players; i > 0; i--)
|
|
if (menus[i].value == 1)
|
|
{
|
|
menus[i].value = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//added/killed by Victor Rachels to eventually add msging
|
|
//since nitems should not be changing, anyway
|
|
// if (nitems > MAX_PLAYERS ) return;
|
|
//end this section kill - VR
|
|
|
|
n = Netgame.numplayers;
|
|
net_ipx_listen();
|
|
|
|
if (n < Netgame.numplayers )
|
|
{
|
|
digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
|
|
|
|
if (PlayerCfg.NoRankings)
|
|
sprintf( menus[N_players-1].text, "%d. %-20s", N_players,Netgame.players[N_players-1].callsign );
|
|
else
|
|
sprintf( menus[N_players-1].text, "%d. %s%-20s", N_players, RankStrings[Netgame.players[N_players-1].rank],Netgame.players[N_players-1].callsign );
|
|
|
|
if (N_players <= MaxNumNetPlayers)
|
|
{
|
|
menus[N_players-1].value = 1;
|
|
}
|
|
}
|
|
else if ( n > Netgame.numplayers )
|
|
{
|
|
// One got removed...
|
|
|
|
digi_play_sample(SOUND_HUD_KILL,F1_0);
|
|
|
|
for (i=0; i<N_players; i++ )
|
|
{
|
|
if (PlayerCfg.NoRankings)
|
|
sprintf( menus[i].text, "%d. %-20s", i+1, Netgame.players[i].callsign );
|
|
else
|
|
sprintf( menus[i].text, "%d. %s%-20s", i+1, RankStrings[Netgame.players[i].rank],Netgame.players[i].callsign );
|
|
if (i < MaxNumNetPlayers)
|
|
menus[i].value = 1;
|
|
else
|
|
menus[i].value = 0;
|
|
}
|
|
for (i=N_players; i<n; i++ )
|
|
{
|
|
sprintf( menus[i].text, "%d. ", i+1 ); // Clear out the deleted entries...
|
|
menus[i].value = 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int opt_cinvul, opt_show_on_map;
|
|
static int opt_setpower,opt_playtime,opt_killgoal,opt_marker_view,opt_light;
|
|
static int opt_difficulty,opt_packets, opt_bright,opt_start_invul, opt_short_packets, opt_socket;
|
|
|
|
typedef struct param_opt
|
|
{
|
|
int start_game, name, level, mode, moreopts;
|
|
int closed, refuse, maxnet, coop, team_anarchy, team_hoard, capture;
|
|
} param_opt;
|
|
|
|
int net_ipx_start_game(void);
|
|
|
|
int net_ipx_game_param_handler( newmenu *menu, d_event *event, param_opt *opt )
|
|
{
|
|
newmenu_item *menus = newmenu_get_items(menu);
|
|
int citem = newmenu_get_citem(menu);
|
|
static int oldmaxnet=0;
|
|
|
|
switch (event->type)
|
|
{
|
|
case EVENT_NEWMENU_CHANGED:
|
|
if (((HoardEquipped() && (citem == opt->team_hoard)) || ((citem == opt->team_anarchy) || (citem == opt->capture))) && !menus[opt->closed].value && !menus[opt->refuse].value)
|
|
{
|
|
menus[opt->refuse].value = 1;
|
|
menus[opt->refuse-1].value = 0;
|
|
menus[opt->refuse-2].value = 0;
|
|
}
|
|
|
|
if (menus[opt->coop].value)
|
|
{
|
|
oldmaxnet=1;
|
|
|
|
if (menus[opt->maxnet].value>2)
|
|
{
|
|
menus[opt->maxnet].value=2;
|
|
}
|
|
|
|
if (menus[opt->maxnet].max_value>2)
|
|
{
|
|
menus[opt->maxnet].max_value=2;
|
|
}
|
|
sprintf( menus[opt->maxnet].text, "Maximum players: %d", menus[opt->maxnet].value+2 );
|
|
Netgame.max_numplayers = MaxNumNetPlayers = menus[opt->maxnet].value+2;
|
|
|
|
if (!(Netgame.game_flags & NETGAME_FLAG_SHOW_MAP))
|
|
Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
|
|
|
|
if (Netgame.PlayTimeAllowed || Netgame.KillGoal)
|
|
{
|
|
Netgame.PlayTimeAllowed=0;
|
|
Netgame.KillGoal=0;
|
|
}
|
|
|
|
}
|
|
else // if !Coop game
|
|
{
|
|
if (oldmaxnet)
|
|
{
|
|
oldmaxnet=0;
|
|
menus[opt->maxnet].value=6;
|
|
menus[opt->maxnet].max_value=6;
|
|
sprintf( menus[opt->maxnet].text, "Maximum players: %d", menus[opt->maxnet].value+2 );
|
|
Netgame.max_numplayers = MaxNumNetPlayers = menus[opt->maxnet].value+2;
|
|
}
|
|
}
|
|
|
|
if (citem == opt->level)
|
|
{
|
|
char *slevel = menus[opt->level].text;
|
|
Netgame.levelnum = atoi(slevel);
|
|
}
|
|
|
|
if (citem == opt->refuse)
|
|
Netgame.RefusePlayers=menus[opt->refuse].value;
|
|
|
|
if (citem == opt->maxnet)
|
|
{
|
|
sprintf( menus[opt->maxnet].text, "Maximum players: %d", menus[opt->maxnet].value+2 );
|
|
MaxNumNetPlayers = menus[opt->maxnet].value+2;
|
|
Netgame.max_numplayers=MaxNumNetPlayers;
|
|
}
|
|
|
|
if ((citem >= opt->mode) && (citem <= opt->team_hoard))
|
|
{
|
|
if ( menus[opt->mode].value )
|
|
Netgame.gamemode = NETGAME_ANARCHY;
|
|
|
|
else if (menus[opt->mode+1].value) {
|
|
Netgame.gamemode = NETGAME_TEAM_ANARCHY;
|
|
}
|
|
else if (ANARCHY_ONLY_MISSION) {
|
|
nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
|
|
menus[opt->mode+2].value = 0;
|
|
menus[opt->mode+3].value = 0;
|
|
menus[opt->mode].value = 1;
|
|
return 0;
|
|
}
|
|
else if ( menus[opt->mode+2].value )
|
|
Netgame.gamemode = NETGAME_ROBOT_ANARCHY;
|
|
else if ( menus[opt->mode+3].value )
|
|
Netgame.gamemode = NETGAME_COOPERATIVE;
|
|
else if (menus[opt->capture].value)
|
|
Netgame.gamemode = NETGAME_CAPTURE_FLAG;
|
|
else if (HoardEquipped() && menus[opt->capture+1].value)
|
|
Netgame.gamemode = NETGAME_HOARD;
|
|
else if (HoardEquipped() && menus[opt->capture+2].value)
|
|
Netgame.gamemode = NETGAME_TEAM_HOARD;
|
|
else Int3(); // Invalid mode -- see Rob
|
|
}
|
|
|
|
if (citem == opt->closed)
|
|
{
|
|
if (menus[opt->closed].value)
|
|
Netgame.game_flags |= NETGAME_FLAG_CLOSED;
|
|
else
|
|
Netgame.game_flags &= ~NETGAME_FLAG_CLOSED;
|
|
}
|
|
break;
|
|
|
|
case EVENT_NEWMENU_SELECTED:
|
|
if ((Netgame.levelnum < 1) || (Netgame.levelnum > Last_level))
|
|
{
|
|
char *slevel = menus[opt->level].text;
|
|
nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE );
|
|
sprintf(slevel, "1");
|
|
return 1;
|
|
}
|
|
|
|
if (citem==opt->moreopts)
|
|
{
|
|
if ( menus[opt->mode+3].value )
|
|
Game_mode=GM_MULTI_COOP;
|
|
net_ipx_more_game_options();
|
|
Game_mode=0;
|
|
return 1;
|
|
}
|
|
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < num_active_ipx_games; j++)
|
|
if (!stricmp(Active_ipx_games[j].game_name, Netgame.game_name))
|
|
{
|
|
nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DUPLICATE_NAME);
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
|
|
if (citem==opt->start_game)
|
|
return !net_ipx_start_game();
|
|
return 1;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_ipx_setup_game()
|
|
{
|
|
int i;
|
|
int optnum;
|
|
param_opt opt;
|
|
newmenu_item m[21];
|
|
char slevel[5];
|
|
char level_text[32];
|
|
char srmaxnet[50];
|
|
|
|
net_ipx_init();
|
|
|
|
change_playernum_to(0);
|
|
|
|
NamesInfoSecurity=-1;
|
|
|
|
for (i=0;i<MAX_PLAYERS;i++)
|
|
if (i!=Player_num)
|
|
Players[i].callsign[0]=0;
|
|
|
|
MaxNumNetPlayers = MAX_NUM_NET_PLAYERS;
|
|
Netgame.KillGoal=0;
|
|
Netgame.PlayTimeAllowed=0;
|
|
Netgame.Allow_marker_view=1;
|
|
Netgame.difficulty=PlayerCfg.DefaultDifficulty;
|
|
Netgame.max_numplayers=MaxNumNetPlayers;
|
|
sprintf( Netgame.game_name, "%s%s", Players[Player_num].callsign, TXT_S_GAME );
|
|
Netgame.BrightPlayers = Netgame.InvulAppear = 1;
|
|
Netgame.AllowedItems = 0;
|
|
Netgame.AllowedItems |= NETFLAG_DOPOWERUP;
|
|
|
|
strcpy(Netgame.mission_name, Current_mission_filename);
|
|
strcpy(Netgame.mission_title, Current_mission_longname);
|
|
|
|
sprintf( slevel, "1" ); Netgame.levelnum = 1;
|
|
|
|
optnum = 0;
|
|
opt.start_game=optnum;
|
|
m[optnum].type = NM_TYPE_MENU; m[optnum].text = "Start Game"; optnum++;
|
|
m[optnum].type = NM_TYPE_TEXT; m[optnum].text = TXT_DESCRIPTION; optnum++;
|
|
|
|
opt.name = optnum;
|
|
m[optnum].type = NM_TYPE_INPUT; m[optnum].text = Netgame.game_name; m[optnum].text_len = NETGAME_NAME_LEN; optnum++;
|
|
|
|
sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level);
|
|
|
|
Assert(strlen(level_text) < 32);
|
|
|
|
m[optnum].type = NM_TYPE_TEXT; m[optnum].text = level_text; optnum++;
|
|
|
|
opt.level = optnum;
|
|
m[optnum].type = NM_TYPE_INPUT; m[optnum].text = slevel; m[optnum].text_len=4; optnum++;
|
|
m[optnum].type = NM_TYPE_TEXT; m[optnum].text = TXT_OPTIONS; optnum++;
|
|
|
|
opt.mode = optnum;
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = TXT_ANARCHY; m[optnum].value=(Netgame.gamemode == NETGAME_ANARCHY); m[optnum].group=0; optnum++;
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = TXT_TEAM_ANARCHY; m[optnum].value=(Netgame.gamemode == NETGAME_TEAM_ANARCHY); m[optnum].group=0; opt.team_anarchy=optnum; optnum++;
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = TXT_ANARCHY_W_ROBOTS; m[optnum].value=(Netgame.gamemode == NETGAME_ROBOT_ANARCHY); m[optnum].group=0; optnum++;
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = TXT_COOPERATIVE; m[optnum].value=(Netgame.gamemode == NETGAME_COOPERATIVE); m[optnum].group=0; opt.coop=optnum; optnum++;
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = "Capture the flag"; m[optnum].value=(Netgame.gamemode == NETGAME_CAPTURE_FLAG); m[optnum].group=0; opt.capture=optnum; optnum++;
|
|
|
|
if (HoardEquipped())
|
|
{
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = "Hoard"; m[optnum].value=(Netgame.gamemode & NETGAME_HOARD); m[optnum].group=0; optnum++;
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = "Team Hoard"; m[optnum].value=(Netgame.gamemode & NETGAME_TEAM_HOARD); m[optnum].group=0; opt.team_hoard=optnum; optnum++;
|
|
}
|
|
|
|
m[optnum].type = NM_TYPE_TEXT; m[optnum].text = ""; optnum++;
|
|
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = "Open game"; m[optnum].group=1; m[optnum].value=(!Netgame.RefusePlayers && !Netgame.game_flags & NETGAME_FLAG_CLOSED); optnum++;
|
|
opt.closed = optnum;
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = TXT_CLOSED_GAME; m[optnum].group=1; m[optnum].value=Netgame.game_flags & NETGAME_FLAG_CLOSED; optnum++;
|
|
opt.refuse = optnum;
|
|
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = "Restricted Game "; m[optnum].group=1; m[optnum].value=Netgame.RefusePlayers; optnum++;
|
|
|
|
opt.maxnet = optnum;
|
|
sprintf( srmaxnet, "Maximum players: %d", MaxNumNetPlayers);
|
|
m[optnum].type = NM_TYPE_SLIDER; m[optnum].value=Netgame.max_numplayers-2; m[optnum].text= srmaxnet; m[optnum].min_value=0;
|
|
m[optnum].max_value=MaxNumNetPlayers-2; optnum++;
|
|
|
|
opt.moreopts=optnum;
|
|
m[optnum].type = NM_TYPE_MENU; m[optnum].text = "Advanced Options"; optnum++;
|
|
|
|
Assert(optnum <= 20);
|
|
|
|
i = newmenu_do1( NULL, TXT_NETGAME_SETUP, optnum, m, (int (*)( newmenu *, d_event *, void * ))net_ipx_game_param_handler, &opt, opt.start_game );
|
|
|
|
if (i < 0)
|
|
ipxdrv_close();
|
|
|
|
return i >= 0;
|
|
}
|
|
|
|
void
|
|
net_ipx_set_game_mode(int gamemode)
|
|
{
|
|
Show_kill_list = 1;
|
|
|
|
if ( gamemode == NETGAME_ANARCHY )
|
|
Game_mode = GM_NETWORK;
|
|
else if ( gamemode == NETGAME_ROBOT_ANARCHY )
|
|
Game_mode = GM_NETWORK | GM_MULTI_ROBOTS;
|
|
else if ( gamemode == NETGAME_COOPERATIVE )
|
|
Game_mode = GM_NETWORK | GM_MULTI_COOP | GM_MULTI_ROBOTS;
|
|
else if (gamemode == NETGAME_CAPTURE_FLAG)
|
|
{
|
|
Game_mode = GM_NETWORK | GM_TEAM | GM_CAPTURE;
|
|
Show_kill_list=3;
|
|
}
|
|
|
|
else if (HoardEquipped() && gamemode == NETGAME_HOARD)
|
|
Game_mode = GM_NETWORK | GM_HOARD;
|
|
else if (HoardEquipped() && gamemode == NETGAME_TEAM_HOARD)
|
|
{
|
|
Game_mode = GM_NETWORK | GM_HOARD | GM_TEAM;
|
|
Show_kill_list=3;
|
|
}
|
|
else if ( gamemode == NETGAME_TEAM_ANARCHY )
|
|
{
|
|
Game_mode = GM_NETWORK | GM_TEAM;
|
|
Show_kill_list = 3;
|
|
}
|
|
else
|
|
Int3();
|
|
}
|
|
|
|
int
|
|
net_ipx_find_game(void)
|
|
{
|
|
// Find out whether or not there is space left on this socket
|
|
|
|
fix64 t1;
|
|
|
|
Network_status = NETSTAT_BROWSING;
|
|
|
|
num_active_ipx_games = 0;
|
|
|
|
show_boxed_message(TXT_WAIT, 0);
|
|
|
|
net_ipx_send_game_list_request();
|
|
t1 = timer_query() + F1_0*3;
|
|
|
|
while (timer_query() < t1) // Wait 3 seconds for replies
|
|
{
|
|
timer_update();
|
|
net_ipx_listen();
|
|
}
|
|
|
|
if (num_active_ipx_games < IPX_MAX_NETGAMES)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void net_ipx_read_sync_packet( netgame_info * sp, int rsinit)
|
|
{
|
|
int i, j;
|
|
char temp_callsign[CALLSIGN_LEN+1];
|
|
netgame_info tmp_info;
|
|
uint server;
|
|
|
|
if (sp != &Netgame)
|
|
{
|
|
receive_netgame_packet((ubyte *)sp, &tmp_info, 0);
|
|
sp = &tmp_info;
|
|
}
|
|
|
|
if (rsinit)
|
|
TempPlayersInfo=&Netgame;
|
|
|
|
// This function is now called by all people entering the netgame.
|
|
|
|
if (sp != &Netgame)
|
|
{
|
|
memcpy( &Netgame, sp, sizeof(netgame_info) );
|
|
memcpy (&Netgame.players,TempPlayersInfo->players,sizeof(netplayer_info)*(MAX_PLAYERS+4));
|
|
}
|
|
|
|
N_players = sp->numplayers;
|
|
Difficulty_level = sp->difficulty;
|
|
Network_status = sp->game_status;
|
|
|
|
if (Netgame.segments_checksum != my_segments_checksum)
|
|
{
|
|
Network_status = NETSTAT_MENU;
|
|
nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NETLEVEL_NMATCH);
|
|
#ifdef RELEASE
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
// Discover my player number
|
|
|
|
memcpy(temp_callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
|
|
|
|
Player_num = -1;
|
|
|
|
for (i=0; i<MAX_NUM_NET_PLAYERS; i++ ) {
|
|
Players[i].net_kills_total = 0;
|
|
}
|
|
|
|
for (i=0; i<N_players; i++ ) {
|
|
if ( (!memcmp( TempPlayersInfo->players[i].protocol.ipx.node, IPX_Seq.player.protocol.ipx.node, 6 )) && (!stricmp( TempPlayersInfo->players[i].callsign, temp_callsign)) ) {
|
|
if (Player_num!=-1) {
|
|
Int3(); // Hey, we've found ourselves twice
|
|
Network_status = NETSTAT_MENU;
|
|
return;
|
|
}
|
|
change_playernum_to(i);
|
|
}
|
|
memcpy( Players[i].callsign, TempPlayersInfo->players[i].callsign, CALLSIGN_LEN+1 );
|
|
|
|
#ifdef WORDS_NEED_ALIGNMENT
|
|
memcpy(&server, sp->players[i].protocol.ipx.server, 4);
|
|
if (server != 0)
|
|
ipxdrv_get_local_target((ubyte *)&server, TempPlayersInfo->players[i].protocol.ipx.node, Players[i].net_address);
|
|
#else // WORDS_NEED_ALIGNMENT
|
|
memcpy(&server, sp->players[i].protocol.ipx.server, 4);
|
|
if ( server != 0 )
|
|
ipxdrv_get_local_target(TempPlayersInfo->players[i].protocol.ipx.server, TempPlayersInfo->players[i].protocol.ipx.node, Players[i].net_address);
|
|
#endif // WORDS_NEED_ALIGNMENT
|
|
else
|
|
memcpy( Players[i].net_address, TempPlayersInfo->players[i].protocol.ipx.node, 6 );
|
|
|
|
Players[i].n_packets_got=0; // How many packets we got from them
|
|
Players[i].n_packets_sent=0; // How many packets we sent to them
|
|
Players[i].connected = TempPlayersInfo->players[i].connected;
|
|
Players[i].net_kills_total = sp->player_kills[i];
|
|
Players[i].net_killed_total = sp->killed[i];
|
|
if ((Network_rejoined) || (i != Player_num))
|
|
Players[i].score = sp->player_score[i];
|
|
for (j = 0; j < MAX_NUM_NET_PLAYERS; j++)
|
|
{
|
|
kill_matrix[i][j] = sp->kills[i][j];
|
|
}
|
|
}
|
|
|
|
if ( Player_num < 0 ) {
|
|
Network_status = NETSTAT_MENU;
|
|
return;
|
|
}
|
|
|
|
if (Network_rejoined)
|
|
for (i=0; i<N_players;i++)
|
|
Players[i].net_killed_total = sp->killed[i];
|
|
|
|
if (Network_rejoined) {
|
|
net_ipx_process_monitor_vector(sp->monitor_vector);
|
|
Players[Player_num].time_level = sp->level_time;
|
|
}
|
|
|
|
team_kills[0] = sp->team_kills[0];
|
|
team_kills[1] = sp->team_kills[1];
|
|
|
|
Players[Player_num].connected = CONNECT_PLAYING;
|
|
Netgame.players[Player_num].connected = CONNECT_PLAYING;
|
|
Netgame.players[Player_num].rank=GetMyNetRanking();
|
|
|
|
if (!Network_rejoined)
|
|
for (i=0; i<NumNetPlayerPositions; i++) {
|
|
Objects[Players[i].objnum].pos = Player_init[Netgame.locations[i]].pos;
|
|
Objects[Players[i].objnum].orient = Player_init[Netgame.locations[i]].orient;
|
|
obj_relink(Players[i].objnum,Player_init[Netgame.locations[i]].segnum);
|
|
}
|
|
|
|
Objects[Players[Player_num].objnum].type = OBJ_PLAYER;
|
|
|
|
Network_status = NETSTAT_PLAYING;
|
|
multi_sort_kill_list();
|
|
|
|
}
|
|
|
|
void
|
|
net_ipx_send_sync(void)
|
|
{
|
|
int i, j, np;
|
|
|
|
// Randomize their starting locations...
|
|
|
|
d_srand( (fix)timer_query() );
|
|
for (i=0; i<NumNetPlayerPositions; i++ )
|
|
{
|
|
if (Players[i].connected)
|
|
Players[i].connected = CONNECT_PLAYING; // Get rid of endlevel connect statuses
|
|
|
|
if (Game_mode & GM_MULTI_COOP)
|
|
Netgame.locations[i] = i;
|
|
else {
|
|
do
|
|
{
|
|
np = d_rand() % NumNetPlayerPositions;
|
|
for (j=0; j<i; j++ )
|
|
{
|
|
if (Netgame.locations[j]==np)
|
|
{
|
|
np =-1;
|
|
break;
|
|
}
|
|
}
|
|
} while (np<0);
|
|
// np is a location that is not used anywhere else..
|
|
Netgame.locations[i]=np;
|
|
}
|
|
}
|
|
|
|
// Push current data into the sync packet
|
|
|
|
net_ipx_update_netgame();
|
|
Netgame.game_status = NETSTAT_PLAYING;
|
|
Netgame.protocol.ipx.Game_pkt_type = PID_SYNC;
|
|
Netgame.segments_checksum = my_segments_checksum;
|
|
|
|
for (i=0; i<N_players; i++ ) {
|
|
if ((!Players[i].connected) || (i == Player_num))
|
|
continue;
|
|
|
|
// Send several times, extras will be ignored
|
|
send_netgame_packet(Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node, NULL, 0);
|
|
send_netplayers_packet(Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node);
|
|
}
|
|
|
|
net_ipx_read_sync_packet(&Netgame,1); // Read it myself, as if I had sent it
|
|
}
|
|
|
|
int
|
|
net_ipx_select_teams(void)
|
|
{
|
|
newmenu_item m[MAX_PLAYERS+4];
|
|
int choice, opt, opt_team_b;
|
|
ubyte team_vector = 0;
|
|
char team_names[2][CALLSIGN_LEN+1];
|
|
int i;
|
|
int pnums[MAX_PLAYERS+2];
|
|
|
|
// One-time initialization
|
|
|
|
for (i = N_players/2; i < N_players; i++) // Put first half of players on team A
|
|
{
|
|
team_vector |= (1 << i);
|
|
}
|
|
|
|
sprintf(team_names[0], "%s", TXT_BLUE);
|
|
sprintf(team_names[1], "%s", TXT_RED);
|
|
|
|
// Here comes da menu
|
|
menu:
|
|
m[0].type = NM_TYPE_INPUT; m[0].text = team_names[0]; m[0].text_len = CALLSIGN_LEN;
|
|
|
|
opt = 1;
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if (!(team_vector & (1 << i)))
|
|
{
|
|
m[opt].type = NM_TYPE_MENU; m[opt].text = Netgame.players[i].callsign; pnums[opt] = i; opt++;
|
|
}
|
|
}
|
|
opt_team_b = opt;
|
|
m[opt].type = NM_TYPE_INPUT; m[opt].text = team_names[1]; m[opt].text_len = CALLSIGN_LEN; opt++;
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if (team_vector & (1 << i))
|
|
{
|
|
m[opt].type = NM_TYPE_MENU; m[opt].text = Netgame.players[i].callsign; pnums[opt] = i; opt++;
|
|
}
|
|
}
|
|
m[opt].type = NM_TYPE_TEXT; m[opt].text = ""; opt++;
|
|
m[opt].type = NM_TYPE_MENU; m[opt].text = TXT_ACCEPT; opt++;
|
|
|
|
Assert(opt <= MAX_PLAYERS+4);
|
|
|
|
choice = newmenu_do(NULL, TXT_TEAM_SELECTION, opt, m, NULL, NULL);
|
|
|
|
if (choice == opt-1)
|
|
{
|
|
if ((opt-2-opt_team_b < 2) || (opt_team_b == 1))
|
|
{
|
|
nm_messagebox(NULL, 1, TXT_OK, TXT_TEAM_MUST_ONE);
|
|
#ifdef RELEASE
|
|
goto menu;
|
|
#endif
|
|
}
|
|
|
|
Netgame.team_vector = team_vector;
|
|
strcpy(Netgame.team_name[0], team_names[0]);
|
|
strcpy(Netgame.team_name[1], team_names[1]);
|
|
return 1;
|
|
}
|
|
|
|
else if ((choice > 0) && (choice < opt_team_b)) {
|
|
team_vector |= (1 << pnums[choice]);
|
|
}
|
|
else if ((choice > opt_team_b) && (choice < opt-2)) {
|
|
team_vector &= ~(1 << pnums[choice]);
|
|
}
|
|
else if (choice == -1)
|
|
return 0;
|
|
goto menu;
|
|
}
|
|
|
|
int
|
|
net_ipx_select_players(void)
|
|
{
|
|
int i, j;
|
|
newmenu_item m[MAX_PLAYERS+4];
|
|
char text[MAX_PLAYERS+4][45];
|
|
char title[50];
|
|
int save_nplayers; //how may people would like to join
|
|
|
|
net_ipx_add_player( &IPX_Seq );
|
|
|
|
for (i=0; i< MAX_PLAYERS+4; i++ ) {
|
|
sprintf( text[i], "%d. %-20s", i+1, "" );
|
|
m[i].type = NM_TYPE_CHECK; m[i].text = text[i]; m[i].value = 0;
|
|
}
|
|
|
|
m[0].value = 1; // Assume server will play...
|
|
|
|
if (PlayerCfg.NoRankings)
|
|
sprintf( text[0], "%d. %-20s", 1, Players[Player_num].callsign );
|
|
else
|
|
sprintf( text[0], "%d. %s%-20s", 1, RankStrings[Netgame.players[Player_num].rank],Players[Player_num].callsign );
|
|
|
|
sprintf( title, "%s %d %s", TXT_TEAM_SELECT, MaxNumNetPlayers, TXT_TEAM_PRESS_ENTER );
|
|
|
|
GetPlayersAgain:
|
|
j=newmenu_do1( NULL, title, MAX_PLAYERS+4, m, net_ipx_start_poll, NULL, 1 );
|
|
|
|
save_nplayers = N_players;
|
|
|
|
if (j<0)
|
|
{
|
|
// Aborted!
|
|
// Dump all players and go back to menu mode
|
|
|
|
abort:
|
|
for (i=1; i<save_nplayers; i++) {
|
|
net_ipx_dump_player(Netgame.players[i].protocol.ipx.server,Netgame.players[i].protocol.ipx.node, DUMP_ABORTED);
|
|
}
|
|
|
|
Netgame.numplayers = 0;
|
|
net_ipx_send_game_info(0); // Tell everyone we're bailing
|
|
|
|
Network_status = NETSTAT_MENU;
|
|
return(0);
|
|
}
|
|
// Count number of players chosen
|
|
|
|
N_players = 0;
|
|
for (i=0; i<save_nplayers; i++ )
|
|
{
|
|
if (m[i].value)
|
|
N_players++;
|
|
}
|
|
|
|
if ( N_players > Netgame.max_numplayers) {
|
|
nm_messagebox( TXT_ERROR, 1, TXT_OK, "%s %d %s", TXT_SORRY_ONLY, MaxNumNetPlayers, TXT_NETPLAYERS_IN );
|
|
N_players = save_nplayers;
|
|
goto GetPlayersAgain;
|
|
}
|
|
|
|
#ifdef RELEASE
|
|
if ( N_players < 2 ) {
|
|
nm_messagebox( TXT_ERROR, 1, TXT_OK, TXT_TEAM_ATLEAST_TWO );
|
|
N_players = save_nplayers;
|
|
goto GetPlayersAgain;
|
|
}
|
|
#endif
|
|
|
|
#ifdef RELEASE
|
|
if ( (Netgame.gamemode == NETGAME_TEAM_ANARCHY || Netgame.gamemode == NETGAME_CAPTURE_FLAG || Netgame.gamemode == NETGAME_TEAM_HOARD) && (N_players < 2) )
|
|
{
|
|
nm_messagebox(TXT_ERROR, 1, TXT_OK, "You must select at least two\nplayers to start a team game" );
|
|
N_players = save_nplayers;
|
|
goto GetPlayersAgain;
|
|
}
|
|
#endif
|
|
|
|
// Remove players that aren't marked.
|
|
N_players = 0;
|
|
for (i=0; i<save_nplayers; i++ )
|
|
{
|
|
if (m[i].value)
|
|
{
|
|
if (i > N_players)
|
|
{
|
|
memcpy(Netgame.players[N_players].protocol.ipx.node, Netgame.players[i].protocol.ipx.node, 6);
|
|
memcpy(Netgame.players[N_players].protocol.ipx.server, Netgame.players[i].protocol.ipx.server, 4);
|
|
memcpy(Netgame.players[N_players].callsign, Netgame.players[i].callsign, CALLSIGN_LEN+1);
|
|
Netgame.players[N_players].version_major=Netgame.players[i].version_major;
|
|
Netgame.players[N_players].version_minor=Netgame.players[i].version_minor;
|
|
Netgame.players[N_players].rank=Netgame.players[i].rank;
|
|
ClipRank (&Netgame.players[N_players].rank);
|
|
net_ipx_check_for_old_version(i);
|
|
}
|
|
Players[N_players].connected = CONNECT_PLAYING;
|
|
N_players++;
|
|
}
|
|
else
|
|
{
|
|
net_ipx_dump_player(Netgame.players[i].protocol.ipx.server,Netgame.players[i].protocol.ipx.node, DUMP_DORK);
|
|
}
|
|
}
|
|
|
|
for (i = N_players; i < MAX_NUM_NET_PLAYERS; i++) {
|
|
memset(Netgame.players[i].protocol.ipx.node, 0, 6);
|
|
memset(Netgame.players[i].protocol.ipx.server, 0, 4);
|
|
memset(Netgame.players[i].callsign, 0, CALLSIGN_LEN+1);
|
|
Netgame.players[i].version_major=0;
|
|
Netgame.players[i].version_minor=0;
|
|
Netgame.players[i].rank=0;
|
|
}
|
|
|
|
if (Netgame.gamemode == NETGAME_TEAM_ANARCHY ||
|
|
Netgame.gamemode == NETGAME_CAPTURE_FLAG ||
|
|
Netgame.gamemode == NETGAME_TEAM_HOARD)
|
|
if (!net_ipx_select_teams())
|
|
goto abort;
|
|
|
|
return(1);
|
|
}
|
|
|
|
int net_ipx_start_game(void)
|
|
{
|
|
Assert( FRAME_INFO_SIZE < MAX_DATA_SIZE );
|
|
if ( !IPX_active )
|
|
{
|
|
nm_messagebox(NULL, 1, TXT_OK, TXT_IPX_NOT_FOUND );
|
|
return 0;
|
|
}
|
|
|
|
if (net_ipx_find_game())
|
|
{
|
|
nm_messagebox(NULL, 1, TXT_OK, TXT_NET_FULL);
|
|
return 0;
|
|
}
|
|
|
|
if (IPX_Socket) {
|
|
ipxdrv_change_default_socket( IPX_DEFAULT_SOCKET + IPX_Socket );
|
|
}
|
|
|
|
if (is_D2_OEM)
|
|
IPX_Seq.player.version_minor|=0x10;
|
|
N_players = 0;
|
|
Netgame.game_status = NETSTAT_STARTING;
|
|
Netgame.numplayers = 0;
|
|
Netgame.protocol.ipx.protocol_version = MULTI_PROTO_VERSION;
|
|
|
|
Network_status = NETSTAT_STARTING;
|
|
|
|
net_ipx_set_game_mode(Netgame.gamemode);
|
|
|
|
d_srand( (fix)timer_query() );
|
|
Netgame.protocol.ipx.Game_Security=d_rand(); // For syncing Netgames with player packets
|
|
|
|
if(net_ipx_select_players())
|
|
{
|
|
StartNewLevel(Netgame.levelnum, 0);
|
|
}
|
|
else
|
|
{
|
|
Game_mode = GM_GAME_OVER;
|
|
return 0; // see if we want to tweak the game we setup
|
|
}
|
|
|
|
return 1; // don't keep params menu or mission listbox (may want to join a game next time)
|
|
}
|
|
|
|
void restart_net_searching(newmenu_item * m)
|
|
{
|
|
int i;
|
|
N_players = 0;
|
|
num_active_ipx_games = 0;
|
|
|
|
memset(Active_ipx_games, 0, sizeof(netgame_info)*IPX_MAX_NETGAMES);
|
|
|
|
for (i = 0; i < IPX_MAX_NETGAMES; i++)
|
|
{
|
|
sprintf(m[i+2].text, "%d. ",i+1);
|
|
}
|
|
|
|
NamesInfoSecurity=-1;
|
|
num_active_ipx_changed = 1;
|
|
}
|
|
|
|
void net_ipx_join_listen(newmenu *menu)
|
|
{
|
|
newmenu_item *menus = newmenu_get_items(menu);
|
|
static fix64 t1 = 0;
|
|
int i,join_status,temp;
|
|
|
|
// send a request for game info every 3 seconds
|
|
if (timer_query() > t1+F1_0*3) {
|
|
t1 = timer_query();
|
|
net_ipx_send_game_list_request();
|
|
}
|
|
|
|
temp=num_active_ipx_games;
|
|
|
|
net_ipx_listen();
|
|
|
|
if (!num_active_ipx_changed)
|
|
return;
|
|
|
|
if (temp!=num_active_ipx_games)
|
|
digi_play_sample (SOUND_HUD_MESSAGE,F1_0);
|
|
|
|
num_active_ipx_changed = 0;
|
|
|
|
// Copy the active games data into the menu options
|
|
for (i = 0; i < num_active_ipx_games; i++)
|
|
{
|
|
int game_status = Active_ipx_games[i].game_status;
|
|
int j,x, k,tx,ty,ta,nplayers = 0;
|
|
char levelname[8],MissName[25],GameName[25],thold[2];
|
|
thold[1]=0;
|
|
|
|
// These next two loops protect against menu skewing
|
|
// if missiontitle or gamename contain a tab
|
|
|
|
for (x=0,tx=0,k=0,j=0;j<15;j++)
|
|
{
|
|
if (Active_ipx_games[i].mission_title[j]=='\t')
|
|
continue;
|
|
thold[0]=Active_ipx_games[i].mission_title[j];
|
|
gr_get_string_size (thold,&tx,&ty,&ta);
|
|
|
|
if ((x+=tx)>=FSPACX(55))
|
|
{
|
|
MissName[k]=MissName[k+1]=MissName[k+2]='.';
|
|
k+=3;
|
|
break;
|
|
}
|
|
|
|
MissName[k++]=Active_ipx_games[i].mission_title[j];
|
|
}
|
|
MissName[k]=0;
|
|
|
|
for (x=0,tx=0,k=0,j=0;j<15;j++)
|
|
{
|
|
if (Active_ipx_games[i].game_name[j]=='\t')
|
|
continue;
|
|
thold[0]=Active_ipx_games[i].game_name[j];
|
|
gr_get_string_size (thold,&tx,&ty,&ta);
|
|
|
|
if ((x+=tx)>=FSPACX(55))
|
|
{
|
|
GameName[k]=GameName[k+1]=GameName[k+2]='.';
|
|
k+=3;
|
|
break;
|
|
}
|
|
GameName[k++]=Active_ipx_games[i].game_name[j];
|
|
}
|
|
GameName[k]=0;
|
|
|
|
|
|
nplayers=Active_ipx_games[i].numconnected;
|
|
|
|
if (Active_ipx_games[i].levelnum < 0)
|
|
sprintf(levelname, "S%d", -Active_ipx_games[i].levelnum);
|
|
else
|
|
sprintf(levelname, "%d", Active_ipx_games[i].levelnum);
|
|
|
|
if (game_status == NETSTAT_STARTING)
|
|
{
|
|
sprintf (menus[i+2].text,"%d.\t%s \t%s \t %d/%d \t%s \t %s \t%s",
|
|
i+1,GameName,GMNamesShrt[Active_ipx_games[i].gamemode],nplayers,
|
|
Active_ipx_games[i].max_numplayers,MissName,levelname,"Forming");
|
|
}
|
|
else if (game_status == NETSTAT_PLAYING)
|
|
{
|
|
join_status=net_ipx_can_join_netgame(&Active_ipx_games[i]);
|
|
|
|
if (join_status==1)
|
|
sprintf (menus[i+2].text,"%d.\t%s \t%s \t %d/%d \t%s \t %s \t%s",
|
|
i+1,GameName,GMNamesShrt[Active_ipx_games[i].gamemode],nplayers,
|
|
Active_ipx_games[i].max_numplayers,MissName,levelname,"Open");
|
|
else if (join_status==2)
|
|
sprintf (menus[i+2].text,"%d.\t%s \t%s \t %d/%d \t%s \t %s \t%s",
|
|
i+1,GameName,GMNamesShrt[Active_ipx_games[i].gamemode],nplayers,
|
|
Active_ipx_games[i].max_numplayers,MissName,levelname,"Full");
|
|
else if (join_status==3)
|
|
sprintf (menus[i+2].text,"%d.\t%s \t%s \t %d/%d \t%s \t %s \t%s",
|
|
i+1,GameName,GMNamesShrt[Active_ipx_games[i].gamemode],nplayers,
|
|
Active_ipx_games[i].max_numplayers,MissName,levelname,"Restrict");
|
|
else
|
|
sprintf (menus[i+2].text,"%d.\t%s \t%s \t %d/%d \t%s \t %s \t%s",
|
|
i+1,GameName,GMNamesShrt[Active_ipx_games[i].gamemode],nplayers,
|
|
Active_ipx_games[i].max_numplayers,MissName,levelname,"Closed");
|
|
|
|
}
|
|
else
|
|
sprintf (menus[i+2].text,"%d.\t%s \t%s \t %d/%d \t%s \t %s \t%s",
|
|
i+1,GameName,GMNamesShrt[Active_ipx_games[i].gamemode],nplayers,
|
|
Active_ipx_games[i].max_numplayers,MissName,levelname,"Between");
|
|
|
|
|
|
Assert(strlen(menus[i+2].text) < 100);
|
|
}
|
|
|
|
for (i = num_active_ipx_games; i < IPX_MAX_NETGAMES; i++)
|
|
{
|
|
sprintf(menus[i+2].text, "%d. ",i+1);
|
|
}
|
|
}
|
|
|
|
int net_ipx_do_join_game(int choice);
|
|
|
|
int net_ipx_join_poll( newmenu *menu, d_event *event, void *menu_text )
|
|
{
|
|
// Polling loop for Join Game menu
|
|
newmenu_item *menus = newmenu_get_items(menu);
|
|
int citem = newmenu_get_citem(menu);
|
|
int key = 0;
|
|
|
|
switch (event->type)
|
|
{
|
|
case EVENT_KEY_COMMAND:
|
|
key = event_key_get(event);
|
|
|
|
if ( IPX_allow_socket_changes ) {
|
|
int osocket;
|
|
int rval = 0;
|
|
|
|
osocket = IPX_Socket;
|
|
|
|
if ( key==KEY_PAGEDOWN ) { IPX_Socket--; rval = 1; }
|
|
if ( key==KEY_PAGEUP ) { IPX_Socket++; rval = 1; }
|
|
|
|
if (IPX_Socket>99)
|
|
IPX_Socket=99;
|
|
if (IPX_Socket<-99)
|
|
IPX_Socket=-99;
|
|
|
|
if ( IPX_Socket+IPX_DEFAULT_SOCKET > 0x8000 )
|
|
IPX_Socket = 0x8000 - IPX_DEFAULT_SOCKET;
|
|
|
|
if ( IPX_Socket+IPX_DEFAULT_SOCKET < 0 )
|
|
IPX_Socket = IPX_DEFAULT_SOCKET;
|
|
|
|
if (IPX_Socket != osocket ) {
|
|
sprintf( menus[0].text, "\t%s %+d (PgUp/PgDn to change)", TXT_CURRENT_IPX_SOCKET, IPX_Socket );
|
|
net_ipx_listen();
|
|
ipxdrv_change_default_socket( IPX_DEFAULT_SOCKET + IPX_Socket );
|
|
restart_net_searching(menus);
|
|
net_ipx_send_game_list_request();
|
|
return rval;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EVENT_IDLE:
|
|
net_ipx_join_listen(menu);
|
|
break;
|
|
|
|
case EVENT_NEWMENU_SELECTED:
|
|
citem-=2;
|
|
|
|
if (citem >=num_active_ipx_games)
|
|
{
|
|
nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_INVALID_CHOICE);
|
|
return 1;
|
|
}
|
|
|
|
if (net_ipx_show_game_stats(citem)==0)
|
|
return 1;
|
|
|
|
// Choice has been made and looks legit
|
|
if (net_ipx_do_join_game(citem)==0)
|
|
return 1;
|
|
|
|
// look ma, we're in a game!!!
|
|
break;
|
|
|
|
case EVENT_WINDOW_CLOSE:
|
|
d_free(menu_text);
|
|
d_free(menus);
|
|
if (!Game_wind)
|
|
{
|
|
ipxdrv_close();
|
|
Network_status = NETSTAT_MENU; // they cancelled
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
net_ipx_wait_for_sync(void)
|
|
{
|
|
char text[60];
|
|
newmenu_item m[2];
|
|
int i, choice=0;
|
|
|
|
Network_status = NETSTAT_WAITING;
|
|
|
|
m[0].type=NM_TYPE_TEXT; m[0].text = text;
|
|
m[1].type=NM_TYPE_TEXT; m[1].text = TXT_NET_LEAVE;
|
|
|
|
i = net_ipx_send_request();
|
|
|
|
if (i < 0)
|
|
return(-1);
|
|
|
|
sprintf( m[0].text, "%s\n'%s' %s", TXT_NET_WAITING, Netgame.players[i].callsign, TXT_NET_TO_ENTER );
|
|
|
|
while (choice > -1)
|
|
{
|
|
timer_update();
|
|
choice=newmenu_do( NULL, TXT_WAIT, 2, m, net_ipx_sync_poll, NULL );
|
|
}
|
|
|
|
if (Network_status != NETSTAT_PLAYING)
|
|
{
|
|
IPX_sequence_packet me;
|
|
|
|
memset(&me, 0, sizeof(IPX_sequence_packet));
|
|
me.type = PID_QUIT_JOINING;
|
|
memcpy( me.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
|
|
memcpy( me.player.protocol.ipx.node, ipxdrv_get_my_local_address(), 6 );
|
|
memcpy( me.player.protocol.ipx.server, ipxdrv_get_my_server_address(), 4 );
|
|
net_ipx_send_sequence_packet(me, Netgame.players[0].protocol.ipx.server, Netgame.players[0].protocol.ipx.node, NULL);
|
|
N_players = 0;
|
|
Game_mode = GM_GAME_OVER;
|
|
return(-1); // they cancelled
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int net_ipx_request_poll( newmenu *menu, d_event *event, void *userdata )
|
|
{
|
|
// Polling loop for waiting-for-requests menu
|
|
|
|
int i = 0;
|
|
int num_ready = 0;
|
|
|
|
if (event->type != EVENT_WINDOW_DRAW)
|
|
return 0;
|
|
|
|
menu = menu;
|
|
userdata = userdata;
|
|
|
|
net_ipx_listen();
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if ((Players[i].connected == CONNECT_PLAYING) || (Players[i].connected == CONNECT_DISCONNECTED))
|
|
num_ready++;
|
|
}
|
|
|
|
if (num_ready == N_players) // All players have checked in or are disconnected
|
|
{
|
|
return -2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_ipx_wait_for_requests(void)
|
|
{
|
|
// Wait for other players to load the level before we send the sync
|
|
int choice, i;
|
|
newmenu_item m[1];
|
|
|
|
Network_status = NETSTAT_WAITING;
|
|
|
|
m[0].type=NM_TYPE_TEXT; m[0].text = TXT_NET_LEAVE;
|
|
|
|
|
|
Network_status = NETSTAT_WAITING;
|
|
net_ipx_flush();
|
|
|
|
Players[Player_num].connected = CONNECT_PLAYING;
|
|
|
|
menu:
|
|
choice = newmenu_do(NULL, TXT_WAIT, 1, m, net_ipx_request_poll, NULL);
|
|
|
|
if (choice == -1)
|
|
{
|
|
// User aborted
|
|
choice = nm_messagebox(NULL, 3, TXT_YES, TXT_NO, TXT_START_NOWAIT, TXT_QUITTING_NOW);
|
|
if (choice == 2) {
|
|
N_players = 1;
|
|
return 0;
|
|
}
|
|
if (choice != 0)
|
|
goto menu;
|
|
|
|
// User confirmed abort
|
|
|
|
for (i=0; i < N_players; i++) {
|
|
if ((Players[i].connected != CONNECT_DISCONNECTED) && (i != Player_num)) {
|
|
net_ipx_dump_player(Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node, DUMP_ABORTED);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
else if (choice != -2)
|
|
goto menu;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Do required syncing after each level, before starting new one */
|
|
int
|
|
net_ipx_level_sync(void)
|
|
{
|
|
int result = 0;
|
|
|
|
MySyncPackInitialized = 0;
|
|
|
|
net_ipx_flush(); // Flush any old packets
|
|
|
|
if (N_players == 0)
|
|
result = net_ipx_wait_for_sync();
|
|
else if (multi_i_am_master())
|
|
{
|
|
result = net_ipx_wait_for_requests();
|
|
if (!result)
|
|
net_ipx_send_sync();
|
|
}
|
|
else
|
|
result = net_ipx_wait_for_sync();
|
|
|
|
multi_powcap_count_powerups_in_mine();
|
|
|
|
if (result)
|
|
{
|
|
Players[Player_num].connected = CONNECT_DISCONNECTED;
|
|
net_ipx_send_endlevel_packet();
|
|
if (Game_wind)
|
|
window_close(Game_wind);
|
|
show_menus();
|
|
ipxdrv_close();
|
|
return -1;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int net_ipx_do_join_game(int choice)
|
|
{
|
|
if (Active_ipx_games[choice].protocol.ipx.protocol_version != MULTI_PROTO_VERSION)
|
|
{
|
|
nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_VERSION_MISMATCH);
|
|
return 0;
|
|
}
|
|
|
|
if (Active_ipx_games[choice].game_status == NETSTAT_ENDLEVEL)
|
|
{
|
|
nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_NET_GAME_BETWEEN2);
|
|
return 0;
|
|
}
|
|
|
|
// Check for valid mission name
|
|
if (!load_mission_by_name(Active_ipx_games[choice].mission_name))
|
|
{
|
|
nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
|
|
return 0;
|
|
}
|
|
|
|
if (is_D2_OEM)
|
|
{
|
|
IPX_Seq.player.version_minor|=0x10;
|
|
if (Active_ipx_games[choice].levelnum>8)
|
|
{
|
|
nm_messagebox(NULL, 1, TXT_OK, "This OEM version only supports\nthe first 8 levels!");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (is_MAC_SHARE)
|
|
{
|
|
if (Active_ipx_games[choice].levelnum > 4)
|
|
{
|
|
nm_messagebox(NULL, 1, TXT_OK, "This SHAREWARE version only supports\nthe first 4 levels!");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!net_ipx_wait_for_all_info (0))
|
|
{
|
|
nm_messagebox (TXT_SORRY,1,TXT_OK,"There was a join error!");
|
|
Network_status = NETSTAT_BROWSING; // We are looking at a game menu
|
|
return 0;
|
|
}
|
|
|
|
Network_status = NETSTAT_BROWSING; // We are looking at a game menu
|
|
|
|
if (!net_ipx_can_join_netgame(&Active_ipx_games[choice]))
|
|
{
|
|
if (Active_ipx_games[0].numplayers == Active_ipx_games[0].max_numplayers)
|
|
nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_GAME_FULL);
|
|
else
|
|
nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_IN_PROGRESS);
|
|
return 0;
|
|
}
|
|
|
|
// Choice is valid, prepare to join in
|
|
memcpy (&Netgame, &Active_ipx_games[choice], sizeof(netgame_info));
|
|
memcpy (&Netgame.players,TempPlayersInfo->players,sizeof(netplayer_info)*(MAX_PLAYERS+4));
|
|
|
|
Difficulty_level = Netgame.difficulty;
|
|
MaxNumNetPlayers = Netgame.max_numplayers;
|
|
change_playernum_to(1);
|
|
|
|
net_ipx_set_game_mode(Netgame.gamemode);
|
|
|
|
StartNewLevel(Netgame.levelnum, 0);
|
|
|
|
return 1; // look ma, we're in a game!!!
|
|
}
|
|
|
|
|
|
int net_ipx_show_game_stats(int choice);
|
|
int net_ipx_do_join_game(int choice);
|
|
|
|
void net_ipx_join_game()
|
|
{
|
|
int i;
|
|
char *menu_text;
|
|
newmenu_item *m;
|
|
|
|
if ( !IPX_active )
|
|
{
|
|
nm_messagebox(NULL, 1, TXT_OK, TXT_IPX_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
MALLOC(m, newmenu_item, ((IPX_MAX_NETGAMES)*2)+1);
|
|
if (!m)
|
|
return;
|
|
MALLOC(menu_text, char, (((IPX_MAX_NETGAMES)*2)+1)*74);
|
|
if (!menu_text)
|
|
{
|
|
d_free(m);
|
|
return;
|
|
}
|
|
|
|
net_ipx_init();
|
|
|
|
N_players = 0;
|
|
|
|
Network_send_objects = 0;
|
|
Network_sending_extras=0;
|
|
Network_rejoined=0;
|
|
|
|
Network_status = NETSTAT_BROWSING; // We are looking at a game menu
|
|
|
|
net_ipx_flush();
|
|
net_ipx_listen(); // Throw out old info
|
|
|
|
net_ipx_send_game_list_request(); // broadcast a request for lists
|
|
|
|
num_active_ipx_games = 0;
|
|
|
|
memset(m, 0, sizeof(newmenu_item)*IPX_MAX_NETGAMES);
|
|
memset(Active_ipx_games, 0, sizeof(netgame_info)*IPX_MAX_NETGAMES);
|
|
|
|
gr_set_fontcolor(BM_XRGB(15,15,23),-1);
|
|
|
|
m[0].text = menu_text;
|
|
m[0].type = NM_TYPE_TEXT;
|
|
if (IPX_allow_socket_changes)
|
|
sprintf( m[0].text, "\tCurrent IPX Socket is default %+d (PgUp/PgDn to change)", IPX_Socket );
|
|
else
|
|
strcpy( m[0].text, "" ); //sprintf( m[0].text, "" );
|
|
|
|
m[1].text=menu_text + 74*1;
|
|
m[1].type=NM_TYPE_TEXT;
|
|
sprintf (m[1].text,"\tGAME \tMODE \t#PLYRS \tMISSION \tLEV \tSTATUS");
|
|
|
|
for (i = 0; i < IPX_MAX_NETGAMES; i++) {
|
|
m[i+2].text = menu_text + 74*(i+2);
|
|
m[i+2].type = NM_TYPE_MENU;
|
|
sprintf(m[i+2].text, "%d. ", i+1);
|
|
}
|
|
|
|
num_active_ipx_changed = 1;
|
|
newmenu_dotiny("NETGAMES", NULL,IPX_MAX_NETGAMES+2, m, 1, net_ipx_join_poll, menu_text);
|
|
}
|
|
|
|
fix64 StartWaitAllTime=0;
|
|
int WaitAllChoice=0;
|
|
#define ALL_INFO_REQUEST_INTERVAL F1_0*3
|
|
|
|
int net_ipx_wait_all_poll( newmenu *menu, d_event *event, void *userdata )
|
|
{
|
|
static fix64 t1=0;
|
|
|
|
menu=menu;
|
|
userdata=userdata;
|
|
|
|
if (event->type != EVENT_WINDOW_DRAW)
|
|
return 0;
|
|
|
|
if (timer_query() > t1+ALL_INFO_REQUEST_INTERVAL)
|
|
{
|
|
net_ipx_send_all_info_request(PID_SEND_ALL_GAMEINFO,SecurityCheck);
|
|
t1 = timer_query();
|
|
}
|
|
|
|
net_ipx_do_big_wait(WaitAllChoice);
|
|
|
|
if(SecurityCheck==-1)
|
|
return -2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_ipx_wait_for_all_info (int choice)
|
|
{
|
|
int pick;
|
|
|
|
newmenu_item m[2];
|
|
|
|
m[0].type=NM_TYPE_TEXT; m[0].text = "Press Escape to cancel";
|
|
|
|
WaitAllChoice=choice;
|
|
StartWaitAllTime=timer_query();
|
|
SecurityCheck=Active_ipx_games[choice].protocol.ipx.Game_Security;
|
|
NetSecurityFlag=0;
|
|
|
|
GetMenu:
|
|
pick=newmenu_do( NULL, "Connecting...", 1, m, net_ipx_wait_all_poll, NULL );
|
|
|
|
if (pick>-1 && SecurityCheck!=-1)
|
|
goto GetMenu;
|
|
|
|
if (SecurityCheck==-1)
|
|
{
|
|
SecurityCheck=0;
|
|
return (1);
|
|
}
|
|
SecurityCheck=0;
|
|
return (0);
|
|
}
|
|
|
|
void net_ipx_do_big_wait(int choice)
|
|
{
|
|
int size;
|
|
ubyte packet[MAX_DATA_SIZE],*data;
|
|
netgame_info *temp_info;
|
|
netgame_info info_struct;
|
|
|
|
size=ipxdrv_get_packet_data( packet );
|
|
|
|
if (size>0)
|
|
{
|
|
data = packet;
|
|
|
|
switch (data[0])
|
|
{
|
|
case PID_GAME_INFO:
|
|
receive_netgame_packet(data, &TempNetInfo, 0);
|
|
|
|
if (TempNetInfo.protocol.ipx.Game_Security !=SecurityCheck)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (NetSecurityFlag==NETSECURITY_WAIT_FOR_GAMEINFO)
|
|
{
|
|
if (TempPlayersInfo->protocol.ipx.Player_Security==TempNetInfo.protocol.ipx.Game_Security)
|
|
{
|
|
if (TempPlayersInfo->protocol.ipx.Player_Security==SecurityCheck)
|
|
{
|
|
memcpy (&Active_ipx_games[choice],(ubyte *)&TempNetInfo,sizeof(netgame_info));
|
|
memcpy (&Active_ipx_games[choice].players, TempPlayersInfo->players, sizeof(netplayer_info)*(MAX_PLAYERS+4));
|
|
SecurityCheck=-1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetSecurityFlag=NETSECURITY_WAIT_FOR_PLAYERS;
|
|
NetSecurityNum=TempNetInfo.protocol.ipx.Game_Security;
|
|
|
|
if (net_ipx_wait_for_playerinfo())
|
|
{
|
|
memcpy (&Active_ipx_games[choice],(ubyte *)&TempNetInfo,sizeof(netgame_info));
|
|
memcpy (&Active_ipx_games[choice].players, TempPlayersInfo->players, sizeof(netplayer_info)*(MAX_PLAYERS+4));
|
|
SecurityCheck=-1;
|
|
}
|
|
|
|
NetSecurityFlag=0;
|
|
NetSecurityNum=0;
|
|
}
|
|
break;
|
|
|
|
case PID_PLAYERSINFO:
|
|
receive_netplayers_packet(data, &info_struct);
|
|
temp_info = &info_struct;
|
|
|
|
if (temp_info->protocol.ipx.Player_Security!=SecurityCheck)
|
|
break; // If this isn't the guy we're looking for, move on
|
|
|
|
memcpy (&TempPlayersBase,(ubyte *)&temp_info,sizeof(netgame_info));
|
|
TempPlayersInfo=&TempPlayersBase;
|
|
WaitingForPlayerInfo=0;
|
|
NetSecurityNum=TempPlayersInfo->protocol.ipx.Player_Security;
|
|
NetSecurityFlag=NETSECURITY_WAIT_FOR_GAMEINFO;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void net_ipx_leave_game()
|
|
{
|
|
int nsave;
|
|
|
|
net_ipx_do_frame(1, 1);
|
|
|
|
if ((multi_i_am_master()))
|
|
{
|
|
while (Network_sending_extras>1 && Player_joining_extras!=-1)
|
|
net_ipx_send_extras();
|
|
|
|
Netgame.numplayers = 0;
|
|
nsave=N_players;
|
|
N_players=0;
|
|
net_ipx_send_game_info(NULL);
|
|
N_players=nsave;
|
|
|
|
}
|
|
|
|
Players[Player_num].connected = CONNECT_DISCONNECTED;
|
|
net_ipx_send_endlevel_packet();
|
|
change_playernum_to(0);
|
|
write_player_file();
|
|
|
|
net_ipx_flush();
|
|
ipxdrv_close();
|
|
}
|
|
|
|
void net_ipx_flush()
|
|
{
|
|
ubyte packet[MAX_DATA_SIZE];
|
|
|
|
if (!IPX_active) return;
|
|
|
|
while (ipxdrv_get_packet_data(packet) > 0) ;
|
|
}
|
|
|
|
void net_ipx_listen()
|
|
{
|
|
int size;
|
|
ubyte packet[MAX_DATA_SIZE];
|
|
int i,loopmax=999;
|
|
|
|
if (Network_status==NETSTAT_PLAYING && Netgame.protocol.ipx.ShortPackets && !Network_send_objects)
|
|
{
|
|
loopmax=N_players*Netgame.PacketsPerSec;
|
|
}
|
|
|
|
if (!IPX_active) return;
|
|
|
|
WaitingForPlayerInfo=1;
|
|
NetSecurityFlag=NETSECURITY_OFF;
|
|
|
|
i=1;
|
|
size = ipxdrv_get_packet_data( packet );
|
|
while ( size > 0) {
|
|
net_ipx_process_packet( packet, size );
|
|
if (++i>loopmax)
|
|
break;
|
|
size = ipxdrv_get_packet_data( packet );
|
|
}
|
|
}
|
|
|
|
int net_ipx_wait_for_playerinfo()
|
|
{
|
|
int size,retries=0;
|
|
ubyte packet[MAX_DATA_SIZE];
|
|
struct netgame_info *TempInfo;
|
|
fix64 basetime;
|
|
ubyte id;
|
|
netgame_info info_struct;
|
|
|
|
if (!IPX_active) return(0);
|
|
|
|
if (Network_status==NETSTAT_PLAYING)
|
|
{
|
|
Int3(); //MY GOD! Get Jason...this is the source of many problems
|
|
return (0);
|
|
}
|
|
|
|
basetime=timer_query();
|
|
|
|
while (WaitingForPlayerInfo && retries<50 && (timer_query()<(basetime+(F1_0*5))))
|
|
{
|
|
size = ipxdrv_get_packet_data( packet );
|
|
id = packet[0];
|
|
|
|
timer_update();
|
|
|
|
if (size>0 && id==PID_PLAYERSINFO)
|
|
{
|
|
receive_netplayers_packet(packet, &info_struct);
|
|
TempInfo = &info_struct;
|
|
retries++;
|
|
|
|
if (NetSecurityFlag==NETSECURITY_WAIT_FOR_PLAYERS)
|
|
{
|
|
if (NetSecurityNum==TempInfo->protocol.ipx.Player_Security)
|
|
{
|
|
memcpy (&TempPlayersBase,(ubyte *)TempInfo,sizeof(netgame_info));
|
|
TempPlayersInfo=&TempPlayersBase;
|
|
NetSecurityFlag=NETSECURITY_OFF;
|
|
NetSecurityNum=0;
|
|
WaitingForPlayerInfo=0;
|
|
return (1);
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
NetSecurityNum=TempInfo->protocol.ipx.Player_Security;
|
|
NetSecurityFlag=NETSECURITY_WAIT_FOR_GAMEINFO;
|
|
|
|
memcpy (&TempPlayersBase,(ubyte *)TempInfo,sizeof(netgame_info));
|
|
TempPlayersInfo=&TempPlayersBase;
|
|
WaitingForPlayerInfo=0;
|
|
return (1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
void net_ipx_send_data( ubyte * ptr, int len, int urgent )
|
|
{
|
|
char check;
|
|
|
|
if (Endlevel_sequence)
|
|
return;
|
|
|
|
if (!MySyncPackInitialized) {
|
|
MySyncPackInitialized = 1;
|
|
memset( &MySyncPack, 0, sizeof(IPX_frame_info) );
|
|
}
|
|
|
|
if (urgent)
|
|
PacketUrgent = 1;
|
|
|
|
if ((MySyncPack.data_size+len) > NET_XDATA_SIZE ) {
|
|
check = ptr[0];
|
|
net_ipx_do_frame(1, 0);
|
|
if (MySyncPack.data_size != 0) {
|
|
Int3();
|
|
}
|
|
Assert(check == ptr[0]);
|
|
}
|
|
|
|
Assert(MySyncPack.data_size+len <= NET_XDATA_SIZE);
|
|
|
|
memcpy( &MySyncPack.data[MySyncPack.data_size], ptr, len );
|
|
MySyncPack.data_size += len;
|
|
}
|
|
|
|
void net_ipx_timeout_player(int playernum)
|
|
{
|
|
// Remove a player from the game if we haven't heard from them in
|
|
// a long time.
|
|
int i, n = 0;
|
|
|
|
Assert(playernum < N_players);
|
|
Assert(playernum > -1);
|
|
|
|
net_ipx_disconnect_player(playernum);
|
|
create_player_appearance_effect(&Objects[Players[playernum].objnum]);
|
|
|
|
digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
|
|
|
|
HUD_init_message(HM_MULTI, "%s %s", Players[playernum].callsign, TXT_DISCONNECTING);
|
|
for (i = 0; i < N_players; i++)
|
|
if (Players[i].connected)
|
|
n++;
|
|
|
|
if (n == 1)
|
|
{
|
|
HUD_init_message(HM_MULTI, "You are the only person remaining in this netgame");
|
|
//nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
|
|
}
|
|
}
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
void squish_short_frame_info(IPX_short_frame_info old_info, ubyte *data)
|
|
{
|
|
int loc = 0;
|
|
int tmpi;
|
|
short tmps;
|
|
|
|
data[0] = old_info.type; loc++;
|
|
/* skip three for pad byte */ loc += 3;
|
|
tmpi = INTEL_INT(old_info.numpackets);
|
|
memcpy(&(data[loc]), &tmpi, 4); loc += 4;
|
|
|
|
memcpy(&(data[loc]), old_info.thepos.bytemat, 9); loc += 9;
|
|
tmps = INTEL_SHORT(old_info.thepos.xo);
|
|
memcpy(&(data[loc]), &tmps, 2); loc += 2;
|
|
tmps = INTEL_SHORT(old_info.thepos.yo);
|
|
memcpy(&(data[loc]), &tmps, 2); loc += 2;
|
|
tmps = INTEL_SHORT(old_info.thepos.zo);
|
|
memcpy(&(data[loc]), &tmps, 2); loc += 2;
|
|
tmps = INTEL_SHORT(old_info.thepos.segment);
|
|
memcpy(&(data[loc]), &tmps, 2); loc += 2;
|
|
tmps = INTEL_SHORT(old_info.thepos.velx);
|
|
memcpy(&(data[loc]), &tmps, 2); loc += 2;
|
|
tmps = INTEL_SHORT(old_info.thepos.vely);
|
|
memcpy(&(data[loc]), &tmps, 2); loc += 2;
|
|
tmps = INTEL_SHORT(old_info.thepos.velz);
|
|
memcpy(&(data[loc]), &tmps, 2); loc += 2;
|
|
|
|
tmps = INTEL_SHORT(old_info.data_size);
|
|
memcpy(&(data[loc]), &tmps, 2); loc += 2;
|
|
|
|
data[loc] = old_info.playernum; loc++;
|
|
data[loc] = old_info.obj_render_type; loc++;
|
|
data[loc] = old_info.level_num; loc++;
|
|
memcpy(&(data[loc]), old_info.data, old_info.data_size);
|
|
}
|
|
#endif
|
|
|
|
char NakedBuf[NET_XDATA_SIZE+4];
|
|
int NakedPacketLen=0;
|
|
int NakedPacketDestPlayer=-1;
|
|
|
|
void net_ipx_do_frame(int force, int listen)
|
|
{
|
|
int i;
|
|
IPX_short_frame_info ShortSyncPack;
|
|
static fix64 LastEndlevel=0;
|
|
static fix last_send_time = 0, last_timeout_check = 0;
|
|
|
|
if (!(Game_mode&GM_NETWORK)) return;
|
|
|
|
if ((Network_status != NETSTAT_PLAYING) || (Endlevel_sequence)) // Don't send postion during escape sequence...
|
|
goto listen;
|
|
|
|
if (NakedPacketLen)
|
|
{
|
|
Assert (NakedPacketDestPlayer>-1);
|
|
ipxdrv_send_packet_data( (ubyte *)NakedBuf, NakedPacketLen, Netgame.players[NakedPacketDestPlayer].protocol.ipx.server, Netgame.players[NakedPacketDestPlayer].protocol.ipx.node,Players[NakedPacketDestPlayer].net_address );
|
|
NakedPacketLen=0;
|
|
NakedPacketDestPlayer=-1;
|
|
}
|
|
|
|
if (WaitForRefuseAnswer && timer_query()>(RefuseTimeLimit+(F1_0*12)))
|
|
WaitForRefuseAnswer=0;
|
|
|
|
last_send_time += FrameTime;
|
|
last_timeout_check += FrameTime;
|
|
|
|
// Send out packet PacksPerSec times per second maximum... unless they fire, then send more often...
|
|
if ( (last_send_time>F1_0/Netgame.PacketsPerSec) || (Network_laser_fired) || force || PacketUrgent )
|
|
{
|
|
if ( Players[Player_num].connected ) {
|
|
int objnum = Players[Player_num].objnum;
|
|
|
|
if (listen) {
|
|
multi_send_robot_frame(0);
|
|
multi_send_fire(); // Do firing if needed..
|
|
}
|
|
|
|
last_send_time = 0;
|
|
|
|
if (Netgame.protocol.ipx.ShortPackets)
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
ubyte send_data[MAX_DATA_SIZE];
|
|
//int squished_size;
|
|
#endif
|
|
int i;
|
|
|
|
memset(&ShortSyncPack,0,sizeof(IPX_short_frame_info));
|
|
create_shortpos(&ShortSyncPack.thepos, Objects+objnum, 0);
|
|
ShortSyncPack.type = PID_PDATA;
|
|
ShortSyncPack.playernum = Player_num;
|
|
ShortSyncPack.obj_render_type = Objects[objnum].render_type;
|
|
ShortSyncPack.level_num = Current_level_num;
|
|
ShortSyncPack.data_size = MySyncPack.data_size;
|
|
memcpy (&ShortSyncPack.data[0],&MySyncPack.data[0],MySyncPack.data_size);
|
|
|
|
MySyncPack.numpackets = INTEL_INT(Players[0].n_packets_sent++);
|
|
ShortSyncPack.numpackets = MySyncPack.numpackets;
|
|
#ifndef WORDS_BIGENDIAN
|
|
// ipxdrvSendGamePacket((ubyte*)&ShortSyncPack, sizeof(IPX_short_frame_info) - MaxXDataSize + MySyncPack.data_size);
|
|
for(i=0; i<N_players; i++) {
|
|
if(Players[i].connected && (i != Player_num))
|
|
ipxdrv_send_packet_data((ubyte*)&ShortSyncPack, sizeof(IPX_short_frame_info) - NET_XDATA_SIZE + MySyncPack.data_size, Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node,Players[i].net_address);
|
|
}
|
|
#else
|
|
squish_short_frame_info(ShortSyncPack, send_data);
|
|
// ipxdrvSendGamePacket((ubyte*)send_data, IPX_SHORT_INFO_SIZE-MaxXDataSize+MySyncPack.data_size);
|
|
for(i=0; i<N_players; i++) {
|
|
if(Players[i].connected && (i != Player_num))
|
|
ipxdrv_send_packet_data((ubyte*)send_data, IPX_SHORT_INFO_SIZE-NET_XDATA_SIZE+MySyncPack.data_size, Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node,Players[i].net_address);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
else // If long packets
|
|
{
|
|
int send_data_size, i;
|
|
|
|
MySyncPack.numpackets = Players[0].n_packets_sent++;
|
|
MySyncPack.type = PID_PDATA;
|
|
MySyncPack.playernum = Player_num;
|
|
MySyncPack.obj_render_type = Objects[objnum].render_type;
|
|
MySyncPack.level_num = Current_level_num;
|
|
MySyncPack.obj_segnum = Objects[objnum].segnum;
|
|
MySyncPack.obj_pos = Objects[objnum].pos;
|
|
MySyncPack.obj_orient = Objects[objnum].orient;
|
|
MySyncPack.phys_velocity = Objects[objnum].mtype.phys_info.velocity;
|
|
MySyncPack.phys_rotvel = Objects[objnum].mtype.phys_info.rotvel;
|
|
|
|
send_data_size = MySyncPack.data_size; // do this so correct size data is sent
|
|
|
|
// ipxdrvSendGamePacket((ubyte*)&MySyncPack, sizeof(IPX_frame_info) - MaxXDataSize + send_data_size);
|
|
for(i=0; i<N_players; i++)
|
|
{
|
|
if(Players[i].connected && (i != Player_num))
|
|
{
|
|
ipxdrv_send_packet_data((ubyte *)&MySyncPack, sizeof(IPX_frame_info) - NET_XDATA_SIZE + (&MySyncPack)->data_size, Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node, Players[i].net_address);
|
|
}
|
|
}
|
|
}
|
|
|
|
PacketUrgent = 0;
|
|
MySyncPack.data_size = 0; // Start data over at 0 length.
|
|
|
|
if (Control_center_destroyed)
|
|
{
|
|
if (Player_is_dead)
|
|
Players[Player_num].connected=CONNECT_DIED_IN_MINE;
|
|
if (timer_query() > (LastEndlevel+(F1_0/2)))
|
|
{
|
|
net_ipx_send_endlevel_packet();
|
|
LastEndlevel=timer_query();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!listen)
|
|
return;
|
|
|
|
if ((last_timeout_check > F1_0) && !(Control_center_destroyed))
|
|
{
|
|
// Check for player timeouts
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if ((i != Player_num) && (Players[i].connected == CONNECT_PLAYING))
|
|
{
|
|
if ((Netgame.players[i].LastPacketTime == 0) || (Netgame.players[i].LastPacketTime > timer_query()))
|
|
{
|
|
Netgame.players[i].LastPacketTime = timer_query();
|
|
continue;
|
|
}
|
|
if ((timer_query() - Netgame.players[i].LastPacketTime) > IPX_TIMEOUT)
|
|
net_ipx_timeout_player(i);
|
|
}
|
|
if(Players[i].connected != CONNECT_PLAYING && Objects[Players[i].objnum].type != OBJ_GHOST)
|
|
{
|
|
HUD_init_message(HM_MULTI, "'%s' has left.", Players[i].callsign );
|
|
multi_make_player_ghost(i);
|
|
}
|
|
}
|
|
last_timeout_check = 0;
|
|
}
|
|
|
|
listen:
|
|
if (!listen)
|
|
{
|
|
MySyncPack.data_size = 0;
|
|
return;
|
|
}
|
|
net_ipx_listen();
|
|
|
|
if (VerifyPlayerJoined!=-1 && !(FrameCount & 63))
|
|
resend_sync_due_to_packet_loss_for_allender(); // This will resend to IPX_sync_player
|
|
|
|
if (Network_send_objects)
|
|
net_ipx_send_objects();
|
|
|
|
if (Network_sending_extras && VerifyPlayerJoined==-1)
|
|
{
|
|
net_ipx_send_extras();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void net_ipx_process_pdata (char *data)
|
|
{
|
|
Assert (Game_mode & GM_NETWORK);
|
|
|
|
if (Netgame.protocol.ipx.ShortPackets)
|
|
net_ipx_read_pdata_short_packet ((IPX_short_frame_info *)data);
|
|
else
|
|
net_ipx_read_pdata_packet ((IPX_frame_info *) data);
|
|
}
|
|
|
|
void net_ipx_read_pdata_packet(IPX_frame_info *pd)
|
|
{
|
|
int TheirPlayernum;
|
|
int TheirObjnum;
|
|
object * TheirObj = NULL;
|
|
|
|
TheirPlayernum = pd->playernum;
|
|
TheirObjnum = Players[pd->playernum].objnum;
|
|
|
|
if (TheirPlayernum < 0) {
|
|
Int3(); // This packet is bogus!!
|
|
return;
|
|
}
|
|
|
|
if (VerifyPlayerJoined!=-1 && TheirPlayernum==VerifyPlayerJoined)
|
|
{
|
|
// Hurray! Someone really really got in the game (I think).
|
|
VerifyPlayerJoined=-1;
|
|
}
|
|
|
|
if (!multi_quit_game && (TheirPlayernum >= N_players))
|
|
{
|
|
if (Network_status!=NETSTAT_WAITING)
|
|
{
|
|
Int3(); // We missed an important packet!
|
|
multi_consistency_error(0);
|
|
return;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (Endlevel_sequence || (Network_status == NETSTAT_ENDLEVEL) ) {
|
|
int old_Endlevel_sequence = Endlevel_sequence;
|
|
Endlevel_sequence = 1;
|
|
if ( pd->data_size>0 )
|
|
{
|
|
// pass pd->data to some parser function....
|
|
multi_process_bigdata( pd->data, pd->data_size );
|
|
}
|
|
Endlevel_sequence = old_Endlevel_sequence;
|
|
return;
|
|
}
|
|
|
|
if ((sbyte)pd->level_num != Current_level_num)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TheirObj = &Objects[TheirObjnum];
|
|
|
|
//------------- Keep track of missed packets -----------------
|
|
Players[TheirPlayernum].n_packets_got++;
|
|
IPX_TotalPacketsGot++;
|
|
Netgame.players[TheirPlayernum].LastPacketTime = timer_query();
|
|
|
|
if ( pd->numpackets != Players[TheirPlayernum].n_packets_got ) {
|
|
if ((pd->numpackets-Players[TheirPlayernum].n_packets_got)>0)
|
|
IPX_TotalMissedPackets += pd->numpackets-Players[TheirPlayernum].n_packets_got;
|
|
|
|
Players[TheirPlayernum].n_packets_got = pd->numpackets;
|
|
}
|
|
|
|
//------------ Read the player's ship's object info ----------------------
|
|
TheirObj->pos = pd->obj_pos;
|
|
TheirObj->orient = pd->obj_orient;
|
|
TheirObj->mtype.phys_info.velocity = pd->phys_velocity;
|
|
TheirObj->mtype.phys_info.rotvel = pd->phys_rotvel;
|
|
|
|
if ((TheirObj->render_type != pd->obj_render_type) && (pd->obj_render_type == RT_POLYOBJ))
|
|
multi_make_ghost_player(TheirPlayernum);
|
|
|
|
obj_relink(TheirObjnum,pd->obj_segnum);
|
|
|
|
if (TheirObj->movement_type == MT_PHYSICS)
|
|
set_thrust_from_velocity(TheirObj);
|
|
|
|
//------------ Welcome them back if reconnecting --------------
|
|
if (!Players[TheirPlayernum].connected) {
|
|
Players[TheirPlayernum].connected = CONNECT_PLAYING;
|
|
|
|
if (Newdemo_state == ND_STATE_RECORDING)
|
|
newdemo_record_multi_reconnect(TheirPlayernum);
|
|
|
|
multi_make_ghost_player(TheirPlayernum);
|
|
|
|
create_player_appearance_effect(&Objects[TheirObjnum]);
|
|
|
|
digi_play_sample( SOUND_HUD_MESSAGE, F1_0);
|
|
|
|
ClipRank (&Netgame.players[TheirPlayernum].rank);
|
|
|
|
if (PlayerCfg.NoRankings)
|
|
HUD_init_message(HM_MULTI, "'%s' %s", Players[TheirPlayernum].callsign, TXT_REJOIN );
|
|
else
|
|
HUD_init_message(HM_MULTI, "%s'%s' %s", RankStrings[Netgame.players[TheirPlayernum].rank],Players[TheirPlayernum].callsign, TXT_REJOIN );
|
|
|
|
|
|
multi_send_score();
|
|
}
|
|
|
|
//------------ Parse the extra data at the end ---------------
|
|
|
|
if ( pd->data_size>0 ) {
|
|
// pass pd->data to some parser function....
|
|
multi_process_bigdata( pd->data, pd->data_size );
|
|
}
|
|
|
|
}
|
|
|
|
void net_ipx_read_pdata_short_packet(IPX_short_frame_info *pd)
|
|
{
|
|
int TheirPlayernum;
|
|
int TheirObjnum;
|
|
object * TheirObj = NULL;
|
|
|
|
TheirPlayernum = pd->playernum;
|
|
TheirObjnum = Players[pd->playernum].objnum;
|
|
|
|
if (TheirPlayernum < 0) {
|
|
Int3(); // This packet is bogus!!
|
|
return;
|
|
}
|
|
if (!multi_quit_game && (TheirPlayernum >= N_players)) {
|
|
if (Network_status!=NETSTAT_WAITING)
|
|
{
|
|
Int3(); // We missed an important packet!
|
|
multi_consistency_error(0);
|
|
return;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (VerifyPlayerJoined!=-1 && TheirPlayernum==VerifyPlayerJoined)
|
|
{
|
|
// Hurray! Someone really really got in the game (I think).
|
|
VerifyPlayerJoined=-1;
|
|
}
|
|
|
|
if (Endlevel_sequence || (Network_status == NETSTAT_ENDLEVEL) ) {
|
|
int old_Endlevel_sequence = Endlevel_sequence;
|
|
Endlevel_sequence = 1;
|
|
if ( pd->data_size>0 ) {
|
|
// pass pd->data to some parser function....
|
|
multi_process_bigdata( pd->data, pd->data_size );
|
|
}
|
|
Endlevel_sequence = old_Endlevel_sequence;
|
|
return;
|
|
}
|
|
|
|
if ((sbyte)pd->level_num != Current_level_num)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TheirObj = &Objects[TheirObjnum];
|
|
|
|
//------------- Keep track of missed packets -----------------
|
|
Players[TheirPlayernum].n_packets_got++;
|
|
IPX_TotalPacketsGot++;
|
|
Netgame.players[TheirPlayernum].LastPacketTime = timer_query();
|
|
|
|
if ( pd->numpackets != Players[TheirPlayernum].n_packets_got ) {
|
|
if ((pd->numpackets-Players[TheirPlayernum].n_packets_got)>0)
|
|
IPX_TotalMissedPackets += pd->numpackets-Players[TheirPlayernum].n_packets_got;
|
|
|
|
Players[TheirPlayernum].n_packets_got = pd->numpackets;
|
|
}
|
|
|
|
//------------ Read the player's ship's object info ----------------------
|
|
|
|
extract_shortpos(TheirObj, &pd->thepos, 0);
|
|
|
|
if ((TheirObj->render_type != pd->obj_render_type) && (pd->obj_render_type == RT_POLYOBJ))
|
|
multi_make_ghost_player(TheirPlayernum);
|
|
|
|
if (TheirObj->movement_type == MT_PHYSICS)
|
|
set_thrust_from_velocity(TheirObj);
|
|
|
|
//------------ Welcome them back if reconnecting --------------
|
|
if (!Players[TheirPlayernum].connected) {
|
|
Players[TheirPlayernum].connected = CONNECT_PLAYING;
|
|
|
|
if (Newdemo_state == ND_STATE_RECORDING)
|
|
newdemo_record_multi_reconnect(TheirPlayernum);
|
|
|
|
multi_make_ghost_player(TheirPlayernum);
|
|
|
|
create_player_appearance_effect(&Objects[TheirObjnum]);
|
|
|
|
digi_play_sample( SOUND_HUD_MESSAGE, F1_0);
|
|
ClipRank (&Netgame.players[TheirPlayernum].rank);
|
|
|
|
if (PlayerCfg.NoRankings)
|
|
HUD_init_message(HM_MULTI, "'%s' %s", Players[TheirPlayernum].callsign, TXT_REJOIN );
|
|
else
|
|
HUD_init_message(HM_MULTI, "%s'%s' %s", RankStrings[Netgame.players[TheirPlayernum].rank],Players[TheirPlayernum].callsign, TXT_REJOIN );
|
|
|
|
|
|
multi_send_score();
|
|
}
|
|
|
|
//------------ Parse the extra data at the end ---------------
|
|
|
|
if ( pd->data_size>0 ) {
|
|
// pass pd->data to some parser function....
|
|
multi_process_bigdata( pd->data, pd->data_size );
|
|
}
|
|
|
|
}
|
|
|
|
void net_ipx_set_power (void)
|
|
{
|
|
/*
|
|
int opt=0,choice,opt_primary,opt_second,opt_power;
|
|
newmenu_item m[40];
|
|
|
|
opt_primary=opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Laser upgrade"; m[opt].value=Netgame.DoLaserUpgrade; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Super lasers"; m[opt].value=Netgame.DoSuperLaser; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Quad Lasers"; m[opt].value=Netgame.DoQuadLasers; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Vulcan cannon"; m[opt].value=Netgame.DoVulcan; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Spreadfire cannon"; m[opt].value=Netgame.DoSpread; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Plasma cannon"; m[opt].value=Netgame.DoPlasma; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Fusion cannon"; m[opt].value=Netgame.DoFusions; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Gauss cannon"; m[opt].value=Netgame.DoGauss; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Helix cannon"; m[opt].value=Netgame.DoHelix; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Phoenix cannon"; m[opt].value=Netgame.DoPhoenix; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Omega cannon"; m[opt].value=Netgame.DoOmega; opt++;
|
|
|
|
opt_second=opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Homing Missiles"; m[opt].value=Netgame.DoHoming; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Proximity Bombs"; m[opt].value=Netgame.DoProximity; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Smart Missiles"; m[opt].value=Netgame.DoSmarts; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Mega Missiles"; m[opt].value=Netgame.DoMegas; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Flash Missiles"; m[opt].value=Netgame.DoFlash; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Guided Missiles"; m[opt].value=Netgame.DoGuided; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Smart Mines"; m[opt].value=Netgame.DoSmartMine; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Mercury Missiles"; m[opt].value=Netgame.DoMercury; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "EarthShaker Missiles"; m[opt].value=Netgame.DoEarthShaker; opt++;
|
|
|
|
opt_power=opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Invulnerability"; m[opt].value=Netgame.DoInvulnerability; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Cloaking"; m[opt].value=Netgame.DoCloak; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Afterburners"; m[opt].value=Netgame.DoAfterburner; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Ammo rack"; m[opt].value=Netgame.DoAmmoRack; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Energy Converter"; m[opt].value=Netgame.DoConverter; opt++;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Headlight"; m[opt].value=Netgame.DoHeadlight; opt++;
|
|
|
|
choice = newmenu_do(NULL, "Objects to allow", opt, m, NULL);
|
|
|
|
Netgame.DoLaserUpgrade=m[opt_primary].value;
|
|
Netgame.DoSuperLaser=m[opt_primary+1].value;
|
|
Netgame.DoQuadLasers=m[opt_primary+2].value;
|
|
Netgame.DoVulcan=m[opt_primary+3].value;
|
|
Netgame.DoSpread=m[opt_primary+4].value;
|
|
Netgame.DoPlasma=m[opt_primary+5].value;
|
|
Netgame.DoFusions=m[opt_primary+6].value;
|
|
Netgame.DoGauss=m[opt_primary+7].value;
|
|
Netgame.DoHelix=m[opt_primary+8].value;
|
|
Netgame.DoPhoenix=m[opt_primary+9].value;
|
|
Netgame.DoOmega=m[opt_primary+10].value;
|
|
|
|
Netgame.DoHoming=m[opt_second].value;
|
|
Netgame.DoProximity=m[opt_second+1].value;
|
|
Netgame.DoSmarts=m[opt_second+2].value;
|
|
Netgame.DoMegas=m[opt_second+3].value;
|
|
Netgame.DoFlash=m[opt_second+4].value;
|
|
Netgame.DoGuided=m[opt_second+5].value;
|
|
Netgame.DoSmartMine=m[opt_second+6].value;
|
|
Netgame.DoMercury=m[opt_second+7].value;
|
|
Netgame.DoEarthShaker=m[opt_second+8].value;
|
|
|
|
Netgame.DoInvulnerability=m[opt_power].value;
|
|
Netgame.DoCloak=m[opt_power+1].value;
|
|
Netgame.DoAfterburner=m[opt_power+2].value;
|
|
Netgame.DoAmmoRack=m[opt_power+3].value;
|
|
Netgame.DoConverter=m[opt_power+4].value;
|
|
Netgame.DoHeadlight=m[opt_power+5].value;
|
|
*/
|
|
newmenu_item m[MULTI_ALLOW_POWERUP_MAX];
|
|
int i;
|
|
|
|
for (i = 0; i < MULTI_ALLOW_POWERUP_MAX; i++)
|
|
{
|
|
m[i].type = NM_TYPE_CHECK; m[i].text = multi_allow_powerup_text[i]; m[i].value = (Netgame.AllowedItems >> i) & 1;
|
|
}
|
|
|
|
newmenu_do1( NULL, "Objects to allow", MULTI_ALLOW_POWERUP_MAX, m, NULL, NULL, 0 );
|
|
|
|
Netgame.AllowedItems &= ~NETFLAG_DOPOWERUP;
|
|
for (i = 0; i < MULTI_ALLOW_POWERUP_MAX; i++)
|
|
if (m[i].value)
|
|
Netgame.AllowedItems |= (1 << i);
|
|
}
|
|
|
|
int net_ipx_more_options_handler( newmenu *menu, d_event *event, void *userdata );
|
|
|
|
void net_ipx_more_game_options ()
|
|
{
|
|
int opt=0,i;
|
|
char PlayText[80],KillText[80],srinvul[50],packstring[5],socket_string[5];
|
|
newmenu_item m[20];
|
|
|
|
sprintf (socket_string,"%d",IPX_Socket);
|
|
sprintf (packstring,"%d",Netgame.PacketsPerSec);
|
|
|
|
opt_difficulty = opt;
|
|
m[opt].type = NM_TYPE_SLIDER; m[opt].value=Netgame.difficulty; m[opt].text=TXT_DIFFICULTY; m[opt].min_value=0; m[opt].max_value=(NDL-1); opt++;
|
|
|
|
opt_cinvul = opt;
|
|
sprintf( srinvul, "%s: %d %s", TXT_REACTOR_LIFE, Netgame.control_invul_time/F1_0/60, TXT_MINUTES_ABBREV );
|
|
m[opt].type = NM_TYPE_SLIDER; m[opt].value=Netgame.control_invul_time/5/F1_0/60; m[opt].text= srinvul; m[opt].min_value=0; m[opt].max_value=10; opt++;
|
|
|
|
opt_playtime=opt;
|
|
sprintf( PlayText, "Max time: %d %s", Netgame.PlayTimeAllowed*5, TXT_MINUTES_ABBREV );
|
|
m[opt].type = NM_TYPE_SLIDER; m[opt].value=Netgame.PlayTimeAllowed; m[opt].text= PlayText; m[opt].min_value=0; m[opt].max_value=10; opt++;
|
|
|
|
opt_killgoal=opt;
|
|
sprintf( KillText, "Kill Goal: %d kills", Netgame.KillGoal*5);
|
|
m[opt].type = NM_TYPE_SLIDER; m[opt].value=Netgame.KillGoal; m[opt].text= KillText; m[opt].min_value=0; m[opt].max_value=10; opt++;
|
|
|
|
opt_start_invul=opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Invulnerable when reappearing"; m[opt].value=Netgame.InvulAppear; opt++;
|
|
|
|
opt_marker_view = opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Allow Marker camera views"; m[opt].value=Netgame.Allow_marker_view; opt++;
|
|
opt_light = opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Indestructible lights"; m[opt].value=Netgame.AlwaysLighting; opt++;
|
|
|
|
opt_bright = opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Bright player ships"; m[opt].value=Netgame.BrightPlayers; opt++;
|
|
|
|
opt_show_on_map=opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=(Netgame.game_flags & NETGAME_FLAG_SHOW_MAP); opt_show_on_map=opt; opt++;
|
|
|
|
opt_short_packets=opt;
|
|
m[opt].type = NM_TYPE_CHECK; m[opt].text = "Short packets"; m[opt].value=Netgame.protocol.ipx.ShortPackets; opt++;
|
|
|
|
opt_setpower = opt;
|
|
m[opt].type = NM_TYPE_MENU; m[opt].text = "Set Objects allowed..."; opt++;
|
|
|
|
m[opt].type = NM_TYPE_TEXT; m[opt].text = "Packets per second (2 - 20)"; opt++;
|
|
opt_packets=opt;
|
|
m[opt].type = NM_TYPE_INPUT; m[opt].text=packstring; m[opt].text_len=2; opt++;
|
|
|
|
m[opt].type = NM_TYPE_TEXT; m[opt].text = "Network socket"; opt++;
|
|
opt_socket = opt;
|
|
m[opt].type = NM_TYPE_INPUT; m[opt].text = socket_string; m[opt].text_len=4; opt++;
|
|
|
|
menu:
|
|
i = newmenu_do1( NULL, "Advanced netgame options", opt, m, net_ipx_more_options_handler, NULL, 0 );
|
|
|
|
Netgame.control_invul_time = m[opt_cinvul].value*5*F1_0*60;
|
|
|
|
if (i==opt_setpower)
|
|
{
|
|
net_ipx_set_power ();
|
|
goto menu;
|
|
}
|
|
|
|
Netgame.PacketsPerSec=atoi(packstring);
|
|
|
|
if (Netgame.PacketsPerSec>20)
|
|
{
|
|
Netgame.PacketsPerSec=20;
|
|
nm_messagebox(TXT_ERROR, 1, TXT_OK, "Packet value out of range\nSetting value to 20");
|
|
}
|
|
|
|
if (Netgame.PacketsPerSec<2)
|
|
{
|
|
nm_messagebox(TXT_ERROR, 1, TXT_OK, "Packet value out of range\nSetting value to 2");
|
|
Netgame.PacketsPerSec=2;
|
|
}
|
|
|
|
if ((atoi(socket_string))!=IPX_Socket)
|
|
{
|
|
IPX_Socket=atoi(socket_string);
|
|
ipxdrv_change_default_socket( IPX_DEFAULT_SOCKET + IPX_Socket );
|
|
}
|
|
|
|
Netgame.InvulAppear=m[opt_start_invul].value;
|
|
Netgame.BrightPlayers=m[opt_bright].value;
|
|
Netgame.protocol.ipx.ShortPackets=m[opt_short_packets].value;
|
|
|
|
Netgame.Allow_marker_view=m[opt_marker_view].value;
|
|
Netgame.AlwaysLighting=m[opt_light].value;
|
|
Netgame.difficulty=Difficulty_level = m[opt_difficulty].value;
|
|
if (m[opt_show_on_map].value)
|
|
Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
|
|
else
|
|
Netgame.game_flags &= ~NETGAME_FLAG_SHOW_MAP;
|
|
}
|
|
|
|
int net_ipx_more_options_handler( newmenu *menu, d_event *event, void *userdata )
|
|
{
|
|
newmenu_item *menus = newmenu_get_items(menu);
|
|
int citem = newmenu_get_citem(menu);
|
|
|
|
switch (event->type)
|
|
{
|
|
case EVENT_NEWMENU_CHANGED:
|
|
if (citem == opt_cinvul)
|
|
sprintf( menus[opt_cinvul].text, "%s: %d %s", TXT_REACTOR_LIFE, menus[opt_cinvul].value*5, TXT_MINUTES_ABBREV );
|
|
else if (citem == opt_playtime)
|
|
{
|
|
if (Game_mode & GM_MULTI_COOP)
|
|
{
|
|
nm_messagebox ("Sorry",1,TXT_OK,"You can't change those for coop!");
|
|
menus[opt_playtime].value=0;
|
|
return 0;
|
|
}
|
|
|
|
Netgame.PlayTimeAllowed=menus[opt_playtime].value;
|
|
sprintf( menus[opt_playtime].text, "Max Time: %d %s", Netgame.PlayTimeAllowed*5, TXT_MINUTES_ABBREV );
|
|
}
|
|
else if (citem == opt_killgoal)
|
|
{
|
|
if (Game_mode & GM_MULTI_COOP)
|
|
{
|
|
nm_messagebox ("Sorry",1,TXT_OK,"You can't change those for coop!");
|
|
menus[opt_killgoal].value=0;
|
|
return 0;
|
|
}
|
|
|
|
Netgame.KillGoal=menus[opt_killgoal].value;
|
|
sprintf( menus[opt_killgoal].text, "Kill Goal: %d kills", Netgame.KillGoal*5);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
userdata = userdata;
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern void multi_send_light (int,ubyte);
|
|
extern void multi_send_light_specific (int,int,ubyte);
|
|
|
|
void net_ipx_send_smash_lights (int pnum)
|
|
{
|
|
// send the lights that have been blown out
|
|
|
|
int i;
|
|
|
|
pnum=pnum;
|
|
|
|
for (i=0;i<=Highest_segment_index;i++)
|
|
if (Light_subtracted[i])
|
|
multi_send_light_specific(pnum,i,Light_subtracted[i]);
|
|
}
|
|
|
|
extern int Num_triggers;
|
|
extern void multi_send_trigger_specific (char pnum,char);
|
|
|
|
void net_ipx_send_fly_thru_triggers (int pnum)
|
|
{
|
|
// send the fly thru triggers that have been disabled
|
|
|
|
int i;
|
|
|
|
for (i=0;i<Num_triggers;i++)
|
|
if (Triggers[i].flags & TF_DISABLED)
|
|
multi_send_trigger_specific(pnum,i);
|
|
}
|
|
|
|
void net_ipx_send_player_flags()
|
|
{
|
|
int i;
|
|
|
|
for (i=0;i<N_players;i++)
|
|
multi_send_flags(i);
|
|
}
|
|
|
|
void net_ipx_do_refuse_stuff (IPX_sequence_packet *their)
|
|
{
|
|
int i,new_player_num;
|
|
|
|
ClipRank (&their->player.rank);
|
|
|
|
for (i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
if (!strcmp (their->player.callsign,Players[i].callsign))
|
|
{
|
|
net_ipx_welcome_player(their);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!WaitForRefuseAnswer)
|
|
{
|
|
for (i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
if (!strcmp (their->player.callsign,Players[i].callsign))
|
|
{
|
|
net_ipx_welcome_player(their);
|
|
return;
|
|
}
|
|
}
|
|
|
|
digi_play_sample (SOUND_HUD_JOIN_REQUEST,F1_0*2);
|
|
|
|
if (Game_mode & GM_TEAM)
|
|
{
|
|
if (!PlayerCfg.NoRankings)
|
|
{
|
|
HUD_init_message(HM_MULTI, "%s %s wants to join",RankStrings[their->player.rank],their->player.callsign);
|
|
}
|
|
else
|
|
{
|
|
HUD_init_message(HM_MULTI, "%s wants to join",their->player.callsign);
|
|
}
|
|
HUD_init_message(HM_MULTI, "Alt-1 assigns to team %s. Alt-2 to team %s",their->player.callsign,Netgame.team_name[0],Netgame.team_name[1]);
|
|
}
|
|
else
|
|
{
|
|
HUD_init_message(HM_MULTI, "%s wants to join (accept: F6)",their->player.callsign);
|
|
}
|
|
|
|
strcpy (RefusePlayerName,their->player.callsign);
|
|
RefuseTimeLimit=timer_query();
|
|
RefuseThisPlayer=0;
|
|
WaitForRefuseAnswer=1;
|
|
}
|
|
else
|
|
{
|
|
for (i=0;i<MAX_PLAYERS;i++)
|
|
{
|
|
if (!strcmp (their->player.callsign,Players[i].callsign))
|
|
{
|
|
net_ipx_welcome_player(their);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (strcmp(their->player.callsign,RefusePlayerName))
|
|
return;
|
|
|
|
if (RefuseThisPlayer)
|
|
{
|
|
RefuseTimeLimit=0;
|
|
RefuseThisPlayer=0;
|
|
WaitForRefuseAnswer=0;
|
|
if (Game_mode & GM_TEAM)
|
|
{
|
|
new_player_num=net_ipx_get_new_player_num (their);
|
|
|
|
Assert (RefuseTeam==1 || RefuseTeam==2);
|
|
|
|
if (RefuseTeam==1)
|
|
Netgame.team_vector &=(~(1<<new_player_num));
|
|
else
|
|
Netgame.team_vector |=(1<<new_player_num);
|
|
net_ipx_welcome_player(their);
|
|
net_ipx_send_netgame_update ();
|
|
}
|
|
else
|
|
{
|
|
net_ipx_welcome_player(their);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((timer_query()) > RefuseTimeLimit+REFUSE_INTERVAL)
|
|
{
|
|
RefuseTimeLimit=0;
|
|
RefuseThisPlayer=0;
|
|
WaitForRefuseAnswer=0;
|
|
if (!strcmp (their->player.callsign,RefusePlayerName))
|
|
{
|
|
net_ipx_dump_player(their->player.protocol.ipx.server,their->player.protocol.ipx.node, DUMP_DORK);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int net_ipx_get_new_player_num (IPX_sequence_packet *their)
|
|
{
|
|
int i;
|
|
|
|
their=their;
|
|
|
|
if ( N_players < MaxNumNetPlayers)
|
|
return (N_players);
|
|
|
|
else
|
|
{
|
|
// Slots are full but game is open, see if anyone is
|
|
// disconnected and replace the oldest player with this new one
|
|
|
|
int oldest_player = -1;
|
|
fix64 oldest_time = timer_query();
|
|
|
|
Assert(N_players == MaxNumNetPlayers);
|
|
|
|
for (i = 0; i < N_players; i++)
|
|
{
|
|
if ( (!Players[i].connected) && (Netgame.players[i].LastPacketTime < oldest_time))
|
|
{
|
|
oldest_time = Netgame.players[i].LastPacketTime;
|
|
oldest_player = i;
|
|
}
|
|
}
|
|
return (oldest_player);
|
|
}
|
|
}
|
|
|
|
void net_ipx_send_extras ()
|
|
{
|
|
static fix64 last_send_time = 0;
|
|
|
|
if (last_send_time + (F1_0/50) > timer_query())
|
|
return;
|
|
last_send_time = timer_query();
|
|
|
|
Assert (Player_joining_extras>-1);
|
|
|
|
if (!multi_i_am_master())
|
|
{
|
|
// Int3();
|
|
// Network_sending_extras=0;
|
|
// return;
|
|
}
|
|
|
|
|
|
if (Network_sending_extras==8)
|
|
net_ipx_send_fly_thru_triggers(Player_joining_extras);
|
|
if (Network_sending_extras==7)
|
|
net_ipx_send_door_updates(Player_joining_extras);
|
|
if (Network_sending_extras==6)
|
|
net_ipx_send_markers();
|
|
if (Network_sending_extras==5 && (Game_mode & GM_MULTI_ROBOTS))
|
|
multi_send_stolen_items();
|
|
if (Network_sending_extras==4 && (Netgame.PlayTimeAllowed || Netgame.KillGoal))
|
|
multi_send_kill_goal_counts();
|
|
if (Network_sending_extras==3)
|
|
net_ipx_send_smash_lights(Player_joining_extras);
|
|
if (Network_sending_extras==2)
|
|
net_ipx_send_player_flags();
|
|
if (Network_sending_extras==1)
|
|
multi_send_powcap_update();
|
|
// if (Network_sending_extras==5)
|
|
// net_ipx_send_door_updates(Player_joining_extras); // twice!
|
|
|
|
Network_sending_extras--;
|
|
if (!Network_sending_extras)
|
|
Player_joining_extras=-1;
|
|
}
|
|
|
|
|
|
void net_ipx_send_naked_packet(char *buf, short len, int who)
|
|
{
|
|
if (!(Game_mode&GM_NETWORK)) return;
|
|
|
|
if (NakedPacketLen==0)
|
|
{
|
|
NakedBuf[0]=PID_NAKED_PDATA;
|
|
NakedBuf[1]=Player_num;
|
|
NakedPacketLen=2;
|
|
}
|
|
|
|
if (len+NakedPacketLen>NET_XDATA_SIZE)
|
|
{
|
|
ipxdrv_send_packet_data( (ubyte *)NakedBuf, NakedPacketLen, Netgame.players[who].protocol.ipx.server, Netgame.players[who].protocol.ipx.node,Players[who].net_address );
|
|
NakedPacketLen=2;
|
|
memcpy (&NakedBuf[NakedPacketLen],buf,len);
|
|
NakedPacketLen+=len;
|
|
NakedPacketDestPlayer=who;
|
|
}
|
|
else
|
|
{
|
|
memcpy (&NakedBuf[NakedPacketLen],buf,len);
|
|
NakedPacketLen+=len;
|
|
NakedPacketDestPlayer=who;
|
|
}
|
|
}
|
|
|
|
void net_ipx_process_naked_pdata (char *data,int len)
|
|
{
|
|
int pnum=data[1];
|
|
Assert (data[0]=PID_NAKED_PDATA);
|
|
|
|
if (pnum < 0) {
|
|
Int3(); // This packet is bogus!!
|
|
return;
|
|
}
|
|
|
|
if (!multi_quit_game && (pnum >= N_players)) {
|
|
if (Network_status!=NETSTAT_WAITING)
|
|
{
|
|
Int3(); // We missed an important packet!
|
|
multi_consistency_error(0);
|
|
return;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (Endlevel_sequence || (Network_status == NETSTAT_ENDLEVEL) ) {
|
|
int old_Endlevel_sequence = Endlevel_sequence;
|
|
Endlevel_sequence = 1;
|
|
multi_process_bigdata( data+2, len-2);
|
|
Endlevel_sequence = old_Endlevel_sequence;
|
|
return;
|
|
}
|
|
|
|
multi_process_bigdata( data+2, len-2 );
|
|
}
|
|
|
|
void net_ipx_check_for_old_version (char pnum)
|
|
{
|
|
if (Netgame.players[(int)pnum].version_major==1 && (Netgame.players[(int)pnum].version_minor & 0x0F)==0)
|
|
Netgame.players[(int)pnum].rank=0;
|
|
}
|
|
|
|
void net_ipx_request_player_names (int n)
|
|
{
|
|
net_ipx_send_all_info_request (PID_GAME_PLAYERS,Active_ipx_games[n].protocol.ipx.Game_Security);
|
|
NamesInfoSecurity=Active_ipx_games[n].protocol.ipx.Game_Security;
|
|
}
|
|
|
|
void net_ipx_process_names_return (ubyte *data)
|
|
{
|
|
newmenu *menu;
|
|
window *wind;
|
|
newmenu_item m[15];
|
|
char mtext[15][50],temp[50];
|
|
int i,t,l,gnum,num=0,count=5,numplayers;
|
|
|
|
if (NamesInfoSecurity!=(*(int *)(data+1)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
numplayers=data[count]; count++;
|
|
|
|
if (numplayers==255)
|
|
{
|
|
NamesInfoSecurity=-1;
|
|
nm_messagebox(NULL, 1, "OK", "That game is refusing\nname requests.\n");
|
|
return;
|
|
}
|
|
|
|
Assert (numplayers>0 && numplayers<MAX_NUM_NET_PLAYERS);
|
|
|
|
for (i=0;i<12;i++)
|
|
{
|
|
m[i].text=(char *)&mtext[i];
|
|
m[i].type=NM_TYPE_TEXT;
|
|
}
|
|
|
|
for (gnum=-1,i=0;i<num_active_ipx_games;i++)
|
|
{
|
|
if (NamesInfoSecurity==Active_ipx_games[i].protocol.ipx.Game_Security)
|
|
{
|
|
gnum=i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (gnum==-1)
|
|
{
|
|
NamesInfoSecurity=-1;
|
|
nm_messagebox(NULL, 1, "OK", "The game you have requested\nnames from is gone.\n");
|
|
return;
|
|
}
|
|
|
|
sprintf (mtext[num],"Players of game '%s':",Active_ipx_games[gnum].game_name); num++;
|
|
for (i=0;i<numplayers;i++)
|
|
{
|
|
l=data[count++];
|
|
|
|
for (t=0;t<CALLSIGN_LEN+1;t++)
|
|
temp[t]=data[count++];
|
|
if (PlayerCfg.NoRankings)
|
|
sprintf (mtext[num],"%s",temp);
|
|
else
|
|
sprintf (mtext[num],"%s%s",RankStrings[l],temp);
|
|
|
|
num++;
|
|
}
|
|
|
|
if (data[count]==99)
|
|
{
|
|
sprintf (mtext[num++]," ");
|
|
sprintf (mtext[num++],"Short packets: %s",data[count+1]?"On":"Off");
|
|
sprintf (mtext[num++],"Packets Per Second: %d",data[count+2]);
|
|
}
|
|
|
|
menu = newmenu_dotiny( NULL, NULL, num, m, 0, NULL, NULL);
|
|
|
|
wind = newmenu_get_window(menu);
|
|
while (window_exists(wind))
|
|
event_process();
|
|
}
|
|
|
|
char NameReturning=1;
|
|
|
|
void net_ipx_send_player_names (IPX_sequence_packet *their)
|
|
{
|
|
int numconnected=0,count=0,i,t;
|
|
char buf[80];
|
|
|
|
if (!their)
|
|
{
|
|
return;
|
|
}
|
|
|
|
buf[0]=PID_NAMES_RETURN; count++;
|
|
(*(int *)(buf+1))=Netgame.protocol.ipx.Game_Security; count+=4;
|
|
|
|
if (!NameReturning)
|
|
{
|
|
buf[count]=255; count++;
|
|
goto sendit;
|
|
}
|
|
|
|
for (i=0;i<N_players;i++)
|
|
if (Players[i].connected)
|
|
numconnected++;
|
|
|
|
buf[count]=numconnected; count++;
|
|
for (i=0;i<N_players;i++)
|
|
if (Players[i].connected)
|
|
{
|
|
buf[count++]=Netgame.players[i].rank;
|
|
|
|
for (t=0;t<CALLSIGN_LEN+1;t++)
|
|
{
|
|
buf[count]=Netgame.players[i].callsign[t];
|
|
count++;
|
|
}
|
|
}
|
|
|
|
buf[count++]=99;
|
|
buf[count++]=Netgame.protocol.ipx.ShortPackets;
|
|
buf[count++]=Netgame.PacketsPerSec;
|
|
|
|
sendit:
|
|
; // empty statement allows compilation on mac which does not have the
|
|
// statement below and kills the compiler when there is no
|
|
// statement following the label "sendit"
|
|
|
|
ipxdrv_send_internetwork_packet_data((ubyte *)buf, count, their->player.protocol.ipx.server, their->player.protocol.ipx.node);
|
|
}
|
|
|
|
static int show_game_rules_handler(window *wind, d_event *event, netgame_info *netgame)
|
|
{
|
|
int k;
|
|
int w = FSPACX(280), h = FSPACY(170);
|
|
|
|
switch (event->type)
|
|
{
|
|
case EVENT_WINDOW_ACTIVATED:
|
|
game_flush_inputs();
|
|
break;
|
|
|
|
case EVENT_KEY_COMMAND:
|
|
k = event_key_get(event);
|
|
switch (k)
|
|
{
|
|
case KEY_ENTER:
|
|
case KEY_SPACEBAR:
|
|
case KEY_ESC:
|
|
window_close(wind);
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case EVENT_WINDOW_DRAW:
|
|
timer_delay2(50);
|
|
|
|
gr_set_current_canvas(NULL);
|
|
nm_draw_background(((SWIDTH-w)/2)-BORDERX,((SHEIGHT-h)/2)-BORDERY,((SWIDTH-w)/2)+w+BORDERX,((SHEIGHT-h)/2)+h+BORDERY);
|
|
|
|
gr_set_current_canvas(window_get_canvas(wind));
|
|
|
|
grd_curcanv->cv_font = MEDIUM3_FONT;
|
|
|
|
gr_set_fontcolor(gr_find_closest_color_current(29,29,47),-1);
|
|
gr_string( 0x8000, FSPACY(15), "NETGAME INFO");
|
|
|
|
grd_curcanv->cv_font = GAME_FONT;
|
|
gr_printf( FSPACX( 25),FSPACY( 35), "Reactor Life:");
|
|
gr_printf( FSPACX( 25),FSPACY( 41), "Max Time:");
|
|
gr_printf( FSPACX( 25),FSPACY( 47), "Kill Goal:");
|
|
gr_printf( FSPACX( 25),FSPACY( 53), "Short Packets:");
|
|
gr_printf( FSPACX( 25),FSPACY( 59), "Pakets per second:");
|
|
gr_printf( FSPACX(155),FSPACY( 35), "Invul when reappearing:");
|
|
gr_printf( FSPACX(155),FSPACY( 41), "Marker camera views:");
|
|
gr_printf( FSPACX(155),FSPACY( 47), "Indestructible lights:");
|
|
gr_printf( FSPACX(155),FSPACY( 53), "Bright player ships:");
|
|
gr_printf( FSPACX(155),FSPACY( 59), "Show players on automap:");
|
|
gr_printf( FSPACX( 25),FSPACY( 80), "Allowed Objects");
|
|
gr_printf( FSPACX( 25),FSPACY( 90), "Laser Upgrade:");
|
|
gr_printf( FSPACX( 25),FSPACY( 96), "Super Laser:");
|
|
gr_printf( FSPACX( 25),FSPACY(102), "Quad Laser:");
|
|
gr_printf( FSPACX( 25),FSPACY(108), "Vulcan Cannon:");
|
|
gr_printf( FSPACX( 25),FSPACY(114), "Gauss Cannon:");
|
|
gr_printf( FSPACX( 25),FSPACY(120), "Spreadfire Cannon:");
|
|
gr_printf( FSPACX( 25),FSPACY(126), "Helix Cannon:");
|
|
gr_printf( FSPACX( 25),FSPACY(132), "Plasma Cannon:");
|
|
gr_printf( FSPACX( 25),FSPACY(138), "Phoenix Cannon:");
|
|
gr_printf( FSPACX( 25),FSPACY(144), "Fusion Cannon:");
|
|
gr_printf( FSPACX( 25),FSPACY(150), "Omega Cannon:");
|
|
gr_printf( FSPACX(170),FSPACY( 90), "Flash Missile:");
|
|
gr_printf( FSPACX(170),FSPACY( 96), "Homing Missile:");
|
|
gr_printf( FSPACX(170),FSPACY(102), "Guided Missile:");
|
|
gr_printf( FSPACX(170),FSPACY(108), "Proximity Bomb:");
|
|
gr_printf( FSPACX(170),FSPACY(114), "Smart Mine:");
|
|
gr_printf( FSPACX(170),FSPACY(120), "Smart Missile:");
|
|
gr_printf( FSPACX(170),FSPACY(126), "Mercury Missile:");
|
|
gr_printf( FSPACX(170),FSPACY(132), "Mega Missile:");
|
|
gr_printf( FSPACX(170),FSPACY(138), "Earthshaker Missile:");
|
|
gr_printf( FSPACX( 25),FSPACY(160), "Afterburner:");
|
|
gr_printf( FSPACX( 25),FSPACY(166), "Headlight:");
|
|
gr_printf( FSPACX( 25),FSPACY(172), "Energy->Shield Conv:");
|
|
gr_printf( FSPACX(170),FSPACY(160), "Invulnerability:");
|
|
gr_printf( FSPACX(170),FSPACY(166), "Cloaking Device:");
|
|
gr_printf( FSPACX(170),FSPACY(172), "Ammo Rack:");
|
|
|
|
gr_set_fontcolor(BM_XRGB(255,255,255),-1);
|
|
gr_printf( FSPACX(115),FSPACY( 35), "%i Min", netgame->control_invul_time/F1_0/60);
|
|
gr_printf( FSPACX(115),FSPACY( 41), "%i Min", netgame->PlayTimeAllowed*5);
|
|
gr_printf( FSPACX(115),FSPACY( 47), "%i", netgame->KillGoal*5);
|
|
gr_printf( FSPACX(115),FSPACY( 53), "%s", netgame->protocol.ipx.ShortPackets?"ON":"OFF");
|
|
gr_printf( FSPACX(115),FSPACY( 59), "%i", netgame->PacketsPerSec);
|
|
gr_printf( FSPACX(275),FSPACY( 35), netgame->InvulAppear?"ON":"OFF");
|
|
gr_printf( FSPACX(275),FSPACY( 41), netgame->Allow_marker_view?"ON":"OFF");
|
|
gr_printf( FSPACX(275),FSPACY( 47), netgame->AlwaysLighting?"ON":"OFF");
|
|
gr_printf( FSPACX(275),FSPACY( 53), netgame->BrightPlayers?"ON":"OFF");
|
|
gr_printf( FSPACX(275),FSPACY( 59), netgame->game_flags & NETGAME_FLAG_SHOW_MAP?"ON":"OFF");
|
|
gr_printf( FSPACX(130),FSPACY( 90), netgame->AllowedItems & NETFLAG_DOLASER?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY( 96), netgame->AllowedItems & NETFLAG_DOSUPERLASER?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(102), netgame->AllowedItems & NETFLAG_DOQUAD?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(108), netgame->AllowedItems & NETFLAG_DOVULCAN?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(114), netgame->AllowedItems & NETFLAG_DOGAUSS?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(120), netgame->AllowedItems & NETFLAG_DOSPREAD?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(126), netgame->AllowedItems & NETFLAG_DOHELIX?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(132), netgame->AllowedItems & NETFLAG_DOPLASMA?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(138), netgame->AllowedItems & NETFLAG_DOPHOENIX?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(144), netgame->AllowedItems & NETFLAG_DOFUSION?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(150), netgame->AllowedItems & NETFLAG_DOOMEGA?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY( 90), netgame->AllowedItems & NETFLAG_DOFLASH?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY( 96), netgame->AllowedItems & NETFLAG_DOHOMING?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(102), netgame->AllowedItems & NETFLAG_DOGUIDED?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(108), netgame->AllowedItems & NETFLAG_DOPROXIM?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(114), netgame->AllowedItems & NETFLAG_DOSMARTMINE?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(120), netgame->AllowedItems & NETFLAG_DOSMART?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(126), netgame->AllowedItems & NETFLAG_DOMERCURY?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(132), netgame->AllowedItems & NETFLAG_DOMEGA?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(138), netgame->AllowedItems & NETFLAG_DOSHAKER?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(160), netgame->AllowedItems & NETFLAG_DOAFTERBURNER?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(166), netgame->AllowedItems & NETFLAG_DOHEADLIGHT?"YES":"NO");
|
|
gr_printf( FSPACX(130),FSPACY(172), netgame->AllowedItems & NETFLAG_DOCONVERTER?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(160), netgame->AllowedItems & NETFLAG_DOINVUL?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(166), netgame->AllowedItems & NETFLAG_DOCLOAK?"YES":"NO");
|
|
gr_printf( FSPACX(275),FSPACY(172), netgame->AllowedItems & NETFLAG_DOAMMORACK?"YES":"NO");
|
|
gr_set_current_canvas(NULL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void net_ipx_show_game_rules(netgame_info *netgame)
|
|
{
|
|
gr_set_current_canvas(NULL);
|
|
|
|
window_create(&grd_curscreen->sc_canvas, (SWIDTH - FSPACX(320))/2, (SHEIGHT - FSPACY(200))/2, FSPACX(320), FSPACY(200),
|
|
(int (*)(window *, d_event *, void *))show_game_rules_handler, netgame);
|
|
}
|
|
|
|
static int show_game_info_handler(newmenu *menu, d_event *event, netgame_info *netgame)
|
|
{
|
|
if (event->type != EVENT_NEWMENU_SELECTED)
|
|
return 0;
|
|
|
|
if (newmenu_get_citem(menu) != 1)
|
|
return 0;
|
|
|
|
net_ipx_show_game_rules(netgame);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int net_ipx_show_game_stats(int choice)
|
|
{
|
|
char rinfo[512],*info=rinfo;
|
|
int c;
|
|
netgame_info *netgame = &Active_ipx_games[choice];
|
|
|
|
memset(info,0,sizeof(char)*256);
|
|
|
|
info+=sprintf(info,"\nConnected to\n\"%s\"\n",netgame->game_name);
|
|
|
|
if(!netgame->mission_title)
|
|
info+=sprintf(info,"Descent2: CounterStrike");
|
|
else
|
|
info+=sprintf(info,"%s",netgame->mission_title);
|
|
|
|
info+=sprintf (info," - Lvl %i",netgame->levelnum);
|
|
info+=sprintf (info,"\n\nDifficulty: %s",MENU_DIFFICULTY_TEXT(netgame->difficulty));
|
|
info+=sprintf (info,"\nGame Mode: %s",GMNames[netgame->gamemode]);
|
|
info+=sprintf (info,"\nPlayers: %i/%i",netgame->numconnected,netgame->max_numplayers);
|
|
|
|
c=nm_messagebox1("WELCOME", (int (*)(newmenu *, d_event *, void *))show_game_info_handler, netgame, 2, "JOIN GAME", "GAME INFO", rinfo);
|
|
if (c==0)
|
|
return 1;
|
|
//else if (c==1)
|
|
// handled in above callback
|
|
else
|
|
return 0;
|
|
}
|