dxx-rebirth/main/net_ipx.c

3881 lines
100 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-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Routines for managing IPX-protocol network play.
*
*/
#ifdef NETWORK
#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 "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 "vers_id.h"
#include "byteswap.h"
#include "playsave.h"
#include "gamefont.h"
#include "rbaudio.h"
// Prototypes
void net_ipx_send_rejoin_sync(int player_num);
void net_ipx_update_netgame(void);
void net_ipx_send_endlevel_sub(int player_num);
void net_ipx_send_endlevel_packet(void);
void net_ipx_read_endlevel_packet( ubyte *data );
void net_ipx_read_object_packet( ubyte *data );
void net_ipx_read_sync_packet( ubyte * sp );
void net_ipx_flush();
void net_ipx_listen();
void net_ipx_process_pdata(char *data);
void net_ipx_more_game_options();
void net_ipx_read_pdata_packet(IPX_frame_info *pd);
int net_ipx_compare_players(netplayer_info *pl1, netplayer_info *pl2);
void net_ipx_do_refuse_stuff(IPX_sequence_packet *their);
int net_ipx_show_game_stats(int choice);
// Variables
netgame_info Active_ipx_games[IPX_MAX_NETGAMES];
int num_active_ipx_games = 0;
int IPX_active=0;
int num_active_ipx_changed = 0;
IPX_sequence_packet IPX_sync_player; // Who is rejoining now?
IPX_frame_info MySyncPack;
ubyte MySyncPackInitialized = 0; // Set to 1 if the MySyncPack is zeroed.
int IPX_Socket=0;
int IPX_allow_socket_changes = 1;
IPX_sequence_packet IPX_Seq;
extern obj_position Player_init[MAX_PLAYERS];
int net_ipx_wait_for_snyc();
extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
extern char PowerupsInMine[MAX_POWERUP_TYPES];
/* 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)
{
short tmps;
int loc;
ubyte out_buffer[MAX_DATA_SIZE];
loc = 0;
memset(out_buffer, 0, sizeof(out_buffer));
out_buffer[0] = seq.type; loc++;
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;
tmps = INTEL_SHORT(seq.player.protocol.ipx.socket);
memcpy(&(out_buffer[loc]), &tmps, 2); loc += 2;
out_buffer[loc] = seq.player.connected; loc++;
out_buffer[loc] = 1; loc++; // 1 was MULTI_PROTO_D1X_MINOR
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->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;
memcpy(&(seq->player.protocol.ipx.socket), &(data[loc]), 2); loc += 2;
seq->player.connected = data[loc]; loc++;
}
void send_netgame_packet(ubyte *server, ubyte *node)
{
IPX_netgame_info netpkt;
int i, j;
netpkt.type = Netgame.protocol.ipx.Game_pkt_type;
memcpy(&netpkt.game_name, &Netgame.game_name, sizeof(char)*(NETGAME_NAME_LEN+1));
memcpy(&netpkt.team_name, &Netgame.team_name, 2*(CALLSIGN_LEN+1));
netpkt.gamemode = Netgame.gamemode;
netpkt.difficulty = Netgame.difficulty;
netpkt.game_status = Netgame.game_status;
netpkt.numplayers = Netgame.numplayers;
netpkt.max_numplayers = Netgame.max_numplayers;
netpkt.game_flags = Netgame.game_flags;
for (i = 0; i < MAX_PLAYERS; i++)
{
memcpy(&netpkt.players[i].callsign, &Netgame.players[i].callsign, CALLSIGN_LEN);
memcpy(&netpkt.players[i].server, &Netgame.players[i].protocol.ipx.server, 4);
memcpy(&netpkt.players[i].node, &Netgame.players[i].protocol.ipx.node, 6);
netpkt.players[i].socket = Netgame.players[i].protocol.ipx.socket;
netpkt.players[i].connected = Netgame.players[i].connected;
}
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.levelnum = Netgame.levelnum;
netpkt.protocol_version = Netgame.protocol.ipx.protocol_version;
netpkt.team_vector = Netgame.team_vector;
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.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];
}
memcpy(&netpkt.mission_name, &Netgame.mission_name, sizeof(char)*9);
memcpy(&netpkt.mission_title, &Netgame.mission_title, sizeof(char)*(MISSION_NAME_LEN+1));
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)
{
IPX_netgame_info netpkt;
int i, j;
memcpy(&netpkt, data, sizeof(IPX_netgame_info));
netgame->protocol.ipx.Game_pkt_type = netpkt.type;
memcpy(netgame->game_name, &netpkt.game_name, sizeof(char)*(NETGAME_NAME_LEN+1));
memcpy(netgame->team_name, &netpkt.team_name, 2*(CALLSIGN_LEN+1));
netgame->gamemode = netpkt.gamemode;
netgame->difficulty = netpkt.difficulty;
netgame->game_status = netpkt.game_status;
netgame->numplayers = netpkt.numplayers;
netgame->max_numplayers = netpkt.max_numplayers;
netgame->game_flags = netpkt.game_flags;
for (i = 0; i < MAX_PLAYERS; i++)
{
memcpy(netgame->players[i].callsign, &netpkt.players[i].callsign, CALLSIGN_LEN);
memcpy(netgame->players[i].protocol.ipx.server, &netpkt.players[i].server, 4);
memcpy(netgame->players[i].protocol.ipx.node, &netpkt.players[i].node, 6);
netgame->players[i].protocol.ipx.socket = netpkt.players[i].socket;
netgame->players[i].connected = netpkt.players[i].connected;
}
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->levelnum = netpkt.levelnum;
netgame->protocol.ipx.protocol_version = netpkt.protocol_version;
netgame->team_vector = netpkt.team_vector;
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->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];
}
memcpy(netgame->mission_name, &netpkt.mission_name, sizeof(char)*9);
memcpy(netgame->mission_title, &netpkt.mission_title, sizeof(char)*(MISSION_NAME_LEN+1));
// Set defaults for features not available for IPX due to DOS game compability
netgame->AllowedItems = NETFLAG_DOPOWERUP; // enable all powerups
netgame->ShowEnemyNames = 0;
netgame->BrightPlayers = 0;
netgame->KillGoal = 0;
netgame->PlayTimeAllowed = 0;
netgame->NoFriendlyFire = 0;
}
void
net_ipx_init(void)
{
// So you want to play a netgame, eh? Let's a get a few things
// straight
int save_pnum = Player_num, t;
for (t=0;t<MAX_POWERUP_TYPES;t++)
{
MaxPowerupsAllowed[t]=0;
PowerupsInMine[t]=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);
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 );
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();
}
// Change socket to new_socket, returns 1 if really changed
int net_ipx_change_socket(int new_socket)
{
if ( new_socket+IPX_DEFAULT_SOCKET > 0x8000 )
new_socket = 0x8000 - IPX_DEFAULT_SOCKET;
if ( new_socket+IPX_DEFAULT_SOCKET < 0 )
new_socket = IPX_DEFAULT_SOCKET;
if (new_socket != IPX_Socket) {
IPX_Socket = new_socket;
net_ipx_listen();
ipxdrv_change_default_socket( IPX_DEFAULT_SOCKET + IPX_Socket );
return 1;
}
return 0;
}
#define ENDLEVEL_SEND_INTERVAL F1_0*2
#define ENDLEVEL_IDLE_TIME F1_0*10
int net_ipx_endlevel_poll2( newmenu *menu, d_event *event, int *secret );
int net_ipx_endlevel_poll( newmenu *menu, d_event *event, int *secret )
{
// Polling loop for End-of-level menu
static fix64 t1 = 0;
int i = 0;
int num_ready = 0;
int num_escaped = 0;
int goto_secret = 0;
int previous_state[MAX_NUM_NET_PLAYERS];
int previous_seconds_left;
newmenu_item *menus = newmenu_get_items(menu);
switch (event->type)
{
case EVENT_KEY_COMMAND:
if (event_key_get(event) != KEY_ESC)
return 0;
{
newmenu_item m2[2];
int choice;
m2[0].type = m2[1].type = NM_TYPE_MENU;
m2[0].text = TXT_YES; m2[1].text = TXT_NO;
choice = newmenu_do1(NULL, TXT_SURE_LEAVE_GAME, 2, m2, (int (*)( newmenu *, d_event *, void * ))net_ipx_endlevel_poll2, secret, 1);
if (choice == 0)
{
Players[Player_num].connected = CONNECT_DISCONNECTED;
return 0;
}
if (choice > -2)
return 1; // stay in endlevel screen
else
return 0; // go to next level
}
case EVENT_WINDOW_DRAW:
// Send our endlevel packet at regular intervals
if (timer_query() > t1+ENDLEVEL_SEND_INTERVAL)
{
net_ipx_send_endlevel_packet();
t1 = timer_query();
}
for (i = 0; i < N_players; i++)
previous_state[i] = Players[i].connected;
previous_seconds_left = Countdown_seconds_left;
net_ipx_listen();
for (i = 0; i < N_players; i++)
{
if (previous_state[i] != Players[i].connected)
{
sprintf(menus[i].text, "%s %s", Players[i].callsign, CONNECT_STATES(Players[i].connected));
}
if (Players[i].connected == CONNECT_PLAYING)
{
// Check timeout for idle players
if (timer_query() > Netgame.players[i].LastPacketTime+ENDLEVEL_IDLE_TIME)
{
Players[i].connected = CONNECT_DISCONNECTED;
net_ipx_send_endlevel_sub(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_PLAYING)
num_escaped++;
if (Players[i].connected == CONNECT_FOUND_SECRET)
goto_secret = 1;
}
if (num_escaped == N_players) // All players are out of the mine
{
Countdown_seconds_left = -1;
}
if (previous_seconds_left != Countdown_seconds_left)
{
if (Countdown_seconds_left < 0)
{
sprintf(menus[N_players].text, "%s", TXT_REACTOR_EXPLODED);
}
else
{
sprintf(menus[N_players].text, "%s: %d %s ", TXT_TIME_REMAINING, Countdown_seconds_left, TXT_SECONDS);
}
}
if (num_ready == N_players) // All players have checked in or are disconnected
{
if (goto_secret)
*secret = 1; // If any player went to the secret level, we go to the secret level
return -2;
}
break;
case EVENT_WINDOW_CLOSE:
net_ipx_send_endlevel_packet();
net_ipx_send_endlevel_packet();
MySyncPackInitialized = 0;
net_ipx_update_netgame();
d_free(menus[0].text); // frees all text
d_free(menus);
break;
default:
break;
}
return 0;
}
int net_ipx_endlevel_poll2( newmenu *menu, d_event *event, int *secret )
{
// Polling loop for End-of-level menu
static fix64 t1 = 0;
int i = 0;
int num_ready = 0;
int goto_secret = 0;
menu = menu;
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)
*secret = 1;
return -2;
}
return 0;
}
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;
}
int net_ipx_endlevel(int *secret)
{
// Do whatever needs to be done between levels
newmenu *menu;
window *wind;
newmenu_item *m;
char *menu_text;
char *title;
int i;
MALLOC(m, newmenu_item, MAX_NUM_NET_PLAYERS+1);
if (!m)
return 0;
MALLOC(menu_text, char, (MAX_NUM_NET_PLAYERS+2)*80);
if (!menu_text)
{
d_free(m);
return 0;
}
net_ipx_flush();
Network_status = NETSTAT_ENDLEVEL; // We are between levels
net_ipx_listen();
net_ipx_send_endlevel_packet();
// Setup menu text pointers and zero them
for (i=0; i<N_players; i++)
{
m[i].type = NM_TYPE_TEXT;
m[i].text = menu_text + i*80;
sprintf(m[i].text, "%s %s", Players[i].callsign, CONNECT_STATES(Players[i].connected));
Netgame.players[i].LastPacketTime = timer_query();
}
m[N_players].type = NM_TYPE_TEXT;
m[N_players].text = menu_text + N_players*80;
title = menu_text + (N_players + 1)*80;
if (Countdown_seconds_left < 0)
sprintf(m[N_players].text, "%s", TXT_REACTOR_EXPLODED);
else
sprintf(m[N_players].text, "%s: %d %s ", TXT_TIME_REMAINING, Countdown_seconds_left, TXT_SECONDS);
sprintf(title, "%s\n%s", TXT_WAITING, TXT_ESC_ABORT);
menu = newmenu_do3(NULL, title, N_players+1, m, (int (*)( newmenu *, d_event *, void * ))net_ipx_endlevel_poll, secret,
0, NULL);
// Stay here until finished
wind = newmenu_get_window(menu);
while (window_exists(wind))
event_process();
// Player canceled between levels
if (Players[Player_num].connected == CONNECT_DISCONNECTED)
{
if (Game_wind)
window_close(Game_wind);
show_menus();
ipxdrv_close();
}
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;
// Game is in progress, figure out if this guy can re-join it
num_players = game->numplayers;
// Search to see if we were already in this closed netgame in progress
for (i = 0; i < num_players; i++)
if ( (!strcasecmp(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;
if (!(game->game_flags & NETGAME_FLAG_CLOSED)) {
// Look for player that is not connected
if (game->numplayers < game->max_numplayers)
return 1;
for (i = 0; i < num_players; i++) {
if (game->players[i].connected == CONNECT_DISCONNECTED)
return 1;
}
return 0;
}
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;
// 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 pnum;
uint server;
pnum = their->player.connected;
Assert(pnum >= 0);
Assert(pnum < MaxNumNetPlayers);
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);
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;
if (pnum == N_players)
{
N_players++;
Netgame.numplayers = N_players;
}
digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
HUD_init_message(HM_MULTI, "'%s' %s",their->player.callsign, TXT_JOINING);
multi_make_ghost_player(pnum);
multi_send_score();
}
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;
// 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)
{
// 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 ( (!strcasecmp(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);
HUD_init_message(HM_MULTI, "'%s' %s", Players[player_num].callsign, TXT_REJOIN);
}
// 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
void net_ipx_send_door_updates(void)
{
// Send door status when new player joins
int i;
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)))
multi_send_door_open(Walls[i].segnum, Walls[i].sidenum);
else if ((Walls[i].type == WALL_BLASTABLE) && (Walls[i].flags & WALL_BLASTED))
multi_send_door_open(Walls[i].segnum, Walls[i].sidenum);
else if ((Walls[i].type == WALL_BLASTABLE) && (Walls[i].hps != WALL_HPS))
multi_send_hostage_door_status(i);
}
}
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;
int blown_bitmaps[7];
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 <= 7);
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)) &&
(!strcasecmp(IPX_sync_player.player.callsign, their->player.callsign)) )
{
Network_send_objects = 0;
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;
loc += 2; // Placeholder for remote_objnum, not used here
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))
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(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 );
// OLD ipxdrv_send_packet_data(object_buffer, loc, &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);
//OLD ipxdrv_send_packet_data(object_buffer, 8, &IPX_sync_player.player.protocol.ipx.node);
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);
// Turn off send object mode
Network_send_objnum = -1;
Network_send_objects = 0;
obj_count = 0;
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;
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 );
send_netgame_packet(IPX_sync_player.player.protocol.ipx.server, IPX_sync_player.player.protocol.ipx.node );
send_netgame_packet(IPX_sync_player.player.protocol.ipx.server, IPX_sync_player.player.protocol.ipx.node );
net_ipx_send_door_updates();
return;
}
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
}
}
if ( N_players >= MAX_PLAYERS ) {
return; // too many of em
}
memcpy( Netgame.players[N_players].callsign, p->player.callsign, CALLSIGN_LEN+1 );
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 );
Netgame.players[N_players].connected = CONNECT_PLAYING;
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].callsign, Netgame.players[i+1].callsign, CALLSIGN_LEN+1 );
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 );
}
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)
{
// Remove player from game (not chosen, kicked, ...)
IPX_Seq.type = PID_DUMP;
IPX_Seq.player.connected = why;
net_ipx_send_sequence_packet( IPX_Seq, server, node, NULL);
}
void
net_ipx_send_game_list_request(void)
{
// Send a broadcast request for game info
IPX_sequence_packet me;
memset(&me, 0, sizeof(IPX_sequence_packet));
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 );
me.type = PID_GAME_LIST;
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;
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
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));
//added 05/18/99 Matt Mueller - it doesn't use the rest, but we should at least initialize it :)
memset(end.kill_matrix[1], 0, (MAX_PLAYERS-1)*MAX_PLAYERS*sizeof(short));
//end addition -MM
#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;
}
//added 05/18/99 Matt Mueller - similarly, its not used if we aren't connected, but checker complains.
else
end.seconds_left = 127;
//end addition -MM
for (i = 0; i < N_players; i++)
{
if ((i != Player_num) && (i!=player_num) && (Players[i].connected))
{
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);
}
}
}
void
net_ipx_send_endlevel_packet(void)
{
// Send an updated endlevel status to other hosts
net_ipx_send_endlevel_sub(Player_num);
}
void
net_ipx_send_game_info(IPX_sequence_packet *their)
{
// Send game info to someone who requested it
char old_type, old_status;
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;
if (Endlevel_sequence || Control_center_destroyed)
Netgame.game_status = NETSTAT_ENDLEVEL;
if (!their)
send_netgame_packet(NULL, NULL);
else
send_netgame_packet(their->player.protocol.ipx.server, their->player.protocol.ipx.node);
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;
Assert(Netgame.numplayers > 0);
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;
}
void net_ipx_process_gameinfo(ubyte *data)
{
int i, j;
netgame_info netgame;
receive_netgame_packet(data, &netgame);
num_active_ipx_changed = 1;
for (i = 0; i < num_active_ipx_games; i++)
if (!strcasecmp(Active_ipx_games[i].game_name, netgame.game_name) &&
(!memcmp(Active_ipx_games[i].players[0].protocol.ipx.node, netgame.players[0].protocol.ipx.node, 6) &&
!memcmp(Active_ipx_games[i].players[0].protocol.ipx.server, netgame.players[0].protocol.ipx.server, 4)))
break;
if (i == IPX_MAX_NETGAMES)
{
return;
}
memcpy(&Active_ipx_games[i], &netgame, sizeof(netgame_info));
if (i == num_active_ipx_games)
num_active_ipx_games++;
/* Workaround for bug in Descent 1 */
if (Active_ipx_games[i].max_numplayers == 255)
{
if (Active_ipx_games[i].gamemode & GM_MULTI_ROBOTS)
Active_ipx_games[i].max_numplayers = 4;
else
Active_ipx_games[i].max_numplayers = 8;
}
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) && (!strcasecmp(their->player.callsign, Netgame.players[i].callsign))) {
Players[i].connected = CONNECT_PLAYING;
break;
}
}
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
switch( their->type ) {
case PID_GAME_INFO:
if (Network_status == NETSTAT_BROWSING)
net_ipx_process_gameinfo(data);
break;
case PID_GAME_LIST:
// Someone wants a list of games
if ((Network_status == NETSTAT_PLAYING) || (Network_status == NETSTAT_STARTING) || (Network_status == NETSTAT_ENDLEVEL))
if (multi_i_am_master())
net_ipx_send_game_info(their);
break;
case PID_ADDPLAYER:
net_ipx_new_player(their);
break;
case PID_REQUEST:
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)
{
if (Netgame.RefusePlayers)
net_ipx_do_refuse_stuff (their);
else
net_ipx_welcome_player(their);
}
break;
// Begin addition by GF
case PID_DUMP:
if ((Network_status == NETSTAT_WAITING) || (Network_status == NETSTAT_PLAYING))
net_ipx_process_dump(their);
break;
// End addition by GF
case PID_QUIT_JOINING:
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)
net_ipx_read_sync_packet(data);
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_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;
default:
Int3(); // Invalid network packet type, see ROB
}
}
#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);
Assert(playernum < N_players);
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_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);
sprintf( menus[N_players-1].text, "%d. %-16s", N_players, 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++ )
{
sprintf( menus[i].text, "%d. %-16s", i+1, 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 0;
}
static int opt_cinvul, opt_show_on_map;
static int opt_show_on_map, opt_difficulty, opt_socket;
typedef struct param_opt
{
int start_game, name, level, mode, mode_end, moreopts;
int closed, refuse, maxnet, anarchy, team_anarchy, robot_anarchy, coop;
} 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 (citem == opt->team_anarchy)
{
menus[opt->closed].value = 1;
menus[opt->closed-1].value = 0;
menus[opt->closed+1].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;
}
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 (!strnicmp(slevel, "s", 1))
Netgame.levelnum = -atoi(slevel+1);
else
Netgame.levelnum = atoi(slevel);
// if ((Netgame.levelnum < Last_secret_level) || (Netgame.levelnum > Last_level) || (Netgame.levelnum == 0))
// {
// nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE );
// sprintf(slevel, "1");
// return 0;
// }
}
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->mode_end))
{
if ( menus[opt->anarchy].value )
Netgame.gamemode = NETGAME_ANARCHY;
else if (menus[opt->team_anarchy].value) {
Netgame.gamemode = NETGAME_TEAM_ANARCHY;
}
// else if (ANARCHY_ONLY_MISSION) {
// int i = 0;
// nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
// for (i = opt->mode; i <= opt->mode_end; i++)
// menus[i].value = 0;
// menus[opt->anarchy].value = 1;
// return 0;
// }
else if ( menus[opt->robot_anarchy].value )
Netgame.gamemode = NETGAME_ROBOT_ANARCHY;
else if ( menus[opt->coop].value )
Netgame.gamemode = NETGAME_COOPERATIVE;
else Int3(); // Invalid mode -- see Rob
}
if (menus[opt->closed].value)
Netgame.game_flags |= NETGAME_FLAG_CLOSED;
else
Netgame.game_flags &= ~NETGAME_FLAG_CLOSED;
Netgame.RefusePlayers=menus[opt->refuse].value;
break;
case EVENT_NEWMENU_SELECTED:
if ((Netgame.levelnum < Last_secret_level) || (Netgame.levelnum > Last_level) || (Netgame.levelnum == 0))
{
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->coop].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);
for (i=0;i<MAX_PLAYERS;i++)
if (i!=Player_num)
Players[i].callsign[0]=0;
MaxNumNetPlayers = MAX_NUM_NET_PLAYERS;
Netgame.RefusePlayers=0;
sprintf( Netgame.game_name, "%s%s", Players[Player_num].callsign, TXT_S_GAME );
Netgame.difficulty=PlayerCfg.DefaultDifficulty;
Netgame.max_numplayers=MaxNumNetPlayers;
Netgame.protocol.ipx.protocol_version = MULTI_PROTO_VERSION;
// Set defaults for features not available for IPX due to DOS game compability
Netgame.AllowedItems = NETFLAG_DOPOWERUP; // enable all powerups
Netgame.ShowEnemyNames = 0;
Netgame.BrightPlayers = 0;
Netgame.KillGoal = 0;
Netgame.PlayTimeAllowed = 0;
Netgame.NoFriendlyFire = 0;
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);
if (Last_secret_level < -1)
sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level);
else if (Last_secret_level == -1)
sprintf(level_text+strlen(level_text)-1, ", S1)");
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; opt.anarchy=optnum; 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; opt.robot_anarchy=optnum; optnum++;
m[optnum].type = NM_TYPE_RADIO; m[optnum].text = TXT_COOPERATIVE; m[optnum].value=(Netgame.gamemode == NETGAME_COOPERATIVE); m[optnum].group=0; opt.mode_end=opt.coop=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_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*2;
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( ubyte * data )
{
int i, j;
netgame_info *sp = &Netgame;
uint server;
char temp_callsign[CALLSIGN_LEN+1];
// This function is now called by all people entering the netgame.
if (data)
{
receive_netgame_packet( data, &Netgame );
}
N_players = sp->numplayers;
Difficulty_level = sp->difficulty;
Network_status = sp->game_status;
// New code, 11/27
if (Netgame.segments_checksum != my_segments_checksum)
{
Network_status = NETSTAT_MENU;
nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NETLEVEL_NMATCH);
#ifdef NDEBUG
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;
//added/uncommented on 11/12/98 by Victor Rachels... who knows
Players[i].net_killed_total = 0;
//end this section uncomment - VR
}
for (i=0; i<N_players; i++ ) {
if ((!memcmp( sp->players[i].protocol.ipx.node, IPX_Seq.player.protocol.ipx.node, 6 )) &&
(!strcasecmp( sp->players[i].callsign, temp_callsign)) )
{
Assert(Player_num == -1); // Make sure we don't find ourselves twice! Looking for interplay reported bug
change_playernum_to(i);
}
memcpy( Players[i].callsign, sp->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, sp->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( sp->players[i].protocol.ipx.server, sp->players[i].protocol.ipx.node, Players[i].net_address );
#endif // WORDS_NEED_ALIGNMENT
else
memcpy( Players[i].net_address, sp->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 = sp->players[i].connected;
//added/edited on 11/12/98 by Victor Rachels += -> =
Players[i].net_kills_total = sp->player_kills[i];
//end this section edit - VR
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;
}
//added/edited on 11/12/98 by Victor Rachels to hopefully fix joining
//I wonder how much else is foobarred like this?
// if (Network_rejoined)
for (i=0; i<N_players;i++)
Players[i].net_killed_total = sp->killed[i];
//added/killed on 1/5/99 by Victor Rachels to fix next level
//-killed if(!Network_rejoined)
//-killed Players[Player_num].net_killed_total = 0;
//end this section kill
//end this section addition/edit - VR
//added/edited on 11/12/98 by Victor Rachels to hopefully fix eff
PlayerCfg.NetlifeKills -= Players[Player_num].net_kills_total;
PlayerCfg.NetlifeKilled -= Players[Player_num].net_killed_total;
//end this section addition - VR
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;
if (!Network_rejoined)
for (i=0; i<MaxNumNetPlayers; 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_abort_game(void)
{
int i;
for (i=1; i<N_players; i++)
net_ipx_dump_player(Netgame.players[i].protocol.ipx.server,Netgame.players[i].protocol.ipx.node, DUMP_ABORTED);
N_players = Netgame.numplayers = 0;
net_ipx_send_game_info(NULL); // Tell everyone we're bailing
}
int
net_ipx_send_sync(void)
{
int i, j, np;
// Randomize their starting locations...
if (NumNetPlayerPositions < MaxNumNetPlayers) {
nm_messagebox(TXT_ERROR, 1, TXT_OK, "Not enough start positions\n(need %d got %d)\nNetgame aborted", MaxNumNetPlayers, NumNetPlayerPositions);
net_ipx_abort_game();
return -1;
}
//added/changed on 9/13/98 by adb to remove TICKER
d_srand( (fix)timer_query() );
//end change - adb
for (i=0; i<MaxNumNetPlayers; 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_netgame_packet(Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node);
send_netgame_packet(Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node);
send_netgame_packet(Netgame.players[i].protocol.ipx.server, Netgame.players[i].protocol.ipx.node);
}
net_ipx_read_sync_packet(NULL); // Read it myself, as if I had sent it
return 0;
}
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);
goto menu;
}
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, opts, opt_msg;
newmenu_item m[MAX_PLAYERS+1];
char text[MAX_PLAYERS][25];
char title[50];
int save_nplayers;
net_ipx_add_player( &IPX_Seq );
for (i=0; i< MAX_PLAYERS; i++ ) {
sprintf( text[i], "%d. %-16s", i+1, "" );
m[i].type = NM_TYPE_CHECK; m[i].text = text[i]; m[i].value = 0;
}
//added/edited on 11/7/98 by Victor Rachels in an attempt to get msgs going.
opts=MAX_PLAYERS;
opt_msg = opts;
//killed for now to not raise people's hopes - 11/10/98 - VR
// m[opts].type = NM_TYPE_MENU; m[opts].text = "Send message..."; opts++;
m[0].value = 1; // Assume server will play...
sprintf( text[0], "%d. %-16s", 1, Players[Player_num].callsign );
sprintf( title, "%s %d %s", TXT_TEAM_SELECT, MaxNumNetPlayers, TXT_TEAM_PRESS_ENTER );
GetPlayersAgain:
j=opt_msg;
while(j==opt_msg)
{
timer_update();
j=newmenu_do1( NULL, title, opts, m, net_ipx_start_poll, NULL, 1 );
if(j==opt_msg)
{
multi_send_message_dialog();
if (Network_message_reciever != -1)
multi_send_message();
}
}
//end this section addition
save_nplayers = N_players;
if (j<0)
{
// Aborted!
// Dump all players and go back to menu mode
abort:
net_ipx_abort_game();
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 > MaxNumNetPlayers) {
nm_messagebox( TXT_ERROR, 1, TXT_OK, "%s %d %s", TXT_SORRY_ONLY, MaxNumNetPlayers, TXT_NETPLAYERS_IN );
N_players = save_nplayers;
goto GetPlayersAgain;
}
#ifdef NDEBUG
if ( N_players < 2 ) {
nm_messagebox( TXT_ERROR, 1, TXT_OK, TXT_TEAM_ATLEAST_TWO );
N_players = save_nplayers;
goto GetPlayersAgain;
}
#endif
#ifdef NDEBUG
if ( (Netgame.gamemode == NETGAME_TEAM_ANARCHY) && (N_players < 3) ) {
nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_TEAM_ATLEAST_THREE );
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].callsign, Netgame.players[i].callsign, CALLSIGN_LEN+1);
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);
}
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].callsign, 0, CALLSIGN_LEN+1);
memset(Netgame.players[i].protocol.ipx.node, 0, 6);
memset(Netgame.players[i].protocol.ipx.server, 0, 4);
}
if (Netgame.gamemode == NETGAME_TEAM_ANARCHY)
if (!net_ipx_select_teams())
goto abort;
return(1);
}
int net_ipx_start_game(void)
{
Assert( sizeof(IPX_frame_info) < 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 );
}
N_players = 0;
Netgame.game_status = NETSTAT_STARTING;
Netgame.numplayers = 0;
net_ipx_set_game_mode(Netgame.gamemode);
MaxNumNetPlayers = Netgame.max_numplayers;
Netgame.protocol.ipx.protocol_version = MULTI_PROTO_VERSION;
Network_status = NETSTAT_STARTING;
if(net_ipx_select_players())
{
StartNewLevel(Netgame.levelnum);
}
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);
}
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;
for (j = 0; j < Active_ipx_games[i].numplayers; j++)
if (Active_ipx_games[i].players[j].connected)
nplayers++;
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)
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;
}
int
net_ipx_level_sync(void)
{
// Do required syncing between (before) levels
int result = 0;
MySyncPackInitialized = 0;
// my_segments_checksum = netmisc_calc_checksum(Segments, sizeof(segment)*(Highest_segment_index+1));
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)
result = net_ipx_send_sync();
}
else
result = net_ipx_wait_for_sync();
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].game_status == NETSTAT_ENDLEVEL)
{
nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_NET_GAME_BETWEEN2);
return 0;
}
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 (!load_mission_by_name(Active_ipx_games[choice].mission_name))
{
nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
return 0;
}
if (!net_ipx_can_join_netgame(&Active_ipx_games[choice]))
{
if (Active_ipx_games[choice].numplayers == Active_ipx_games[choice].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));
Difficulty_level = Netgame.difficulty;
MaxNumNetPlayers = Netgame.max_numplayers;
change_playernum_to(1);
net_ipx_set_game_mode(Netgame.gamemode);
StartNewLevel(Netgame.levelnum);
return 1; // look ma, we're in a game!!!
}
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_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);
}
void net_ipx_leave_game()
{
net_ipx_do_frame(1, 1);
if ((multi_i_am_master()) &&
(Netgame.numplayers == 1 || Network_status == NETSTAT_STARTING))
{
N_players = Netgame.numplayers = 0;
net_ipx_send_game_info(NULL);
}
Players[Player_num].connected = CONNECT_DISCONNECTED;
net_ipx_send_endlevel_packet();
change_playernum_to(0);
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];
if (!IPX_active) return;
size = ipxdrv_get_packet_data( packet );
while ( size > 0 ) {
net_ipx_process_packet( packet, size );
size = ipxdrv_get_packet_data( packet );
}
}
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]);
(void)check;
}
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)
{
//added/changed on 10/11/98 by Victor Rachels cuz this is annoying as a box
//-killed- nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
HUD_init_message(HM_MULTI, "You are the only person remaining in this netgame");
//end this section change - VR
}
}
void net_ipx_do_frame(int force, int listen)
{
int i;
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 (WaitForRefuseAnswer && timer_query()>(RefuseTimeLimit+(F1_0*12)))
WaitForRefuseAnswer=0;
last_send_time += FrameTime;
last_timeout_check += FrameTime;
// Send out packet 10 times per second maximum... unless they fire, then send more often...
if ( (last_send_time > (F1_0 / 10)) ||
(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;
{
int 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;
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)
net_ipx_send_endlevel_packet();
}
}
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);
}
//added on 11/18/98 by Victor Rachels to hack ghost disconnects
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);
}
//end this section addition - VR
}
last_timeout_check = 0;
}
listen:
if (!listen)
{
MySyncPack.data_size = 0;
return;
}
net_ipx_listen();
if (Network_send_objects)
net_ipx_send_objects();
}
void net_ipx_process_pdata (char *data)
{
Assert (Game_mode & GM_NETWORK);
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 (!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++;
Netgame.players[TheirPlayernum].LastPacketTime = timer_query();
if ( 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);
HUD_init_message(HM_MULTI, "'%s' %s", 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 );
}
}
int net_ipx_more_options_handler( newmenu *menu, d_event *event, void *userdata );
void net_ipx_more_game_options ()
{
int opt=0;
char srinvul[50],socket_string[5];
newmenu_item m[21];
sprintf (socket_string,"%d",IPX_Socket);
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_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++;
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++;
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 ((atoi(socket_string))!=IPX_Socket)
{
IPX_Socket=atoi(socket_string);
ipxdrv_change_default_socket( IPX_DEFAULT_SOCKET + IPX_Socket );
}
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);
switch (event->type)
{
case EVENT_NEWMENU_CHANGED:
if (newmenu_get_citem(menu) == opt_cinvul)
sprintf( menus[opt_cinvul].text, "%s: %d %s", TXT_REACTOR_LIFE, menus[opt_cinvul].value*5, TXT_MINUTES_ABBREV );
break;
default:
break;
}
userdata = userdata;
return 0;
}
void net_ipx_do_refuse_stuff (IPX_sequence_packet *their)
{
int i;
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_CONTROL_CENTER_WARNING_SIREN,F1_0*2);
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;
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;
}
}
}
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;
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,"Descent: First Strike");
else
info+=sprintf(info,"%s",netgame->mission_title);
if( netgame->levelnum >= 0 )
{
info+=sprintf (info," - Lvl %i",netgame->levelnum);
}
else
{
info+=sprintf (info," - Lvl S%i",(netgame->levelnum*-1));
}
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->numplayers,netgame->max_numplayers);
c=nm_messagebox1("WELCOME", (int (*)(newmenu *, d_event *, void *))show_game_info_handler, netgame, 1, "JOIN GAME", rinfo);
if (c==0)
return 1;
//else if (c==1)
// handled in above callback
else
return 0;
}
#endif