From fa8b97acb7a6070852c33b9e6a0a3b77d442f533 Mon Sep 17 00:00:00 2001 From: Taylor Richards Date: Sun, 9 Mar 2014 23:19:13 -0400 Subject: [PATCH] first pass at PXO reintegration --- include/chat_api.h | 79 + include/gamesequence.h | 4 + include/gtrack.h | 143 + include/multi_fstracker.h | 164 + include/multi_options.h | 1 + include/multi_pxo.h | 125 + include/multi_sw.h | 81 + include/psnet2.h | 6 +- include/ptrack.h | 338 ++ include/timer.h | 1 + include/unix.h | 1 + include/valid.h | 60 + src/freespace2/freespace.cpp | 42 + src/gamesequence/gamesequence.cpp | 6 +- src/inetfile/chttpget.cpp | 7 +- src/io/timer.cpp | 5 + src/menuui/optionsmenumulti.cpp | 5 +- src/network/chat_api.cpp | 1336 +++++++ src/network/gtrack.cpp | 454 +++ src/network/multi_fstracker.cpp | 1618 +++++++++ src/network/multi_pxo.cpp | 5653 +++++++++++++++++++++++++++++ src/network/multi_sw.cpp | 373 ++ src/network/ptrack.cpp | 637 ++++ src/network/valid.cpp | 503 +++ 24 files changed, 11631 insertions(+), 11 deletions(-) create mode 100644 include/chat_api.h create mode 100644 include/gtrack.h create mode 100644 include/multi_fstracker.h create mode 100644 include/multi_pxo.h create mode 100644 include/multi_sw.h create mode 100644 include/ptrack.h create mode 100644 include/valid.h create mode 100644 src/network/chat_api.cpp create mode 100644 src/network/gtrack.cpp create mode 100644 src/network/multi_fstracker.cpp create mode 100644 src/network/multi_pxo.cpp create mode 100644 src/network/multi_sw.cpp create mode 100644 src/network/ptrack.cpp create mode 100644 src/network/valid.cpp diff --git a/include/chat_api.h b/include/chat_api.h new file mode 100644 index 0000000..aafd944 --- /dev/null +++ b/include/chat_api.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +#ifndef PXO_CHAT_API_HEADER_FILE +#define PXO_CHAT_API_HEADER_FILE + +// chat server port +#define PXO_CHAT_PORT 7117 + +// motd prefix +#define PXO_CHAT_MOTD_PREFIX "!MOTD$#!" +#define PXO_CHAT_END_OF_MOTD_PREFIX "!EMOTD$#!" + +//Commands +#define CC_USER_JOINING 1 //A user had joined this channel (add him/her from the user listbox if any) +#define CC_USER_LEAVING 2 //A user has left the channel (remove him/her from the user listbox if any) +#define CC_DISCONNECTED 3 //We have been disconnected from the server (close the chat screen down) +#define CC_KICKED 4 //We were kicked out of the channel! (close the chat screen down?) +#define CC_NICKCHANGED 5 //Informing that your nickname has changed (data = "oldnick newnick") +#define CC_YOURCHANNEL 6 //data = name of the channel you are in. Only generated when you are joining #autoselect + +#define MAXLOCALSTRING 600 +#define MSG_REMOTE 0 +#define MSG_LOCAL 1 + +typedef struct _Chat_user +{ + char nick_name[33]; + _Chat_user *next; +}Chat_user; + +typedef struct _Chat_channel +{ + char channel_name[33]; + unsigned short users; + char topic[100]; + _Chat_channel *next; +}Chat_channel; + + +typedef struct _Chat_command +{ + short command; + char data[100]; + _Chat_command *next; +}Chat_command; + +//Prototypes +void ChatInit(void); +int ConnectToChatServer(char *serveraddr,char *nickname,char *trackerid); +void DisconnectFromChatServer(); +char * GetChatText(); +const char *SendChatString(const char *line, int raw=0); +Chat_command *GetChatCommand(); +char *GetChatUserList(); +int SetNewChatChannel(char *channel); +char *GetChannelList(void); +char *GetTrackerIdByUser(char *nickname); +char *GetChannelByUser(char *nickname); + +char *ChatGetString(void); +const char *GetWordNum(int num, const char *l_String); +char * ParseIRCMessage(char *Line, int iMode); +int AddChatUser(const char *nickname); +int RemoveChatUser(char *nickname); +void RemoveAllChatUsers(void); +void AddChatCommandToQueue(int command, const void *data, int len); +Chat_command *GetChatCommandFromQueue(void); +void FlushChatCommandQueue(void); +void AddChannel(char *channel,unsigned short numusers,char *topic); +void FlushChannelList(void); + +#endif diff --git a/include/gamesequence.h b/include/gamesequence.h index 692f326..297e683 100644 --- a/include/gamesequence.h +++ b/include/gamesequence.h @@ -401,6 +401,8 @@ #define GS_EVENT_END_DEMO 60 // end of demo campaign #define GS_EVENT_LOOP_BRIEF 61 // campaign loop brief #define GS_EVENT_CAMPAIGN_CHEAT 62 // skip to a mission in a campaign +#define GS_EVENT_PXO 74 +#define GS_EVENT_PXO_HELP 75 // IMPORTANT: When you add a new event, update the initialization for GS_event_text[] // which is done in GameSequence.cpp @@ -460,6 +462,8 @@ extern const char *GS_event_text[]; // text description for the GS_EVENT_* #def #define GS_STATE_GAMEPLAY_HELP 47 #define GS_STATE_END_DEMO 48 // end of demo campaign (upsell then main menu) #define GS_STATE_LOOP_BRIEF 49 +#define GS_STATE_PXO 59 +#define GS_STATE_PXO_HELP 60 // IMPORTANT: When you add a new state, update the initialization for GS_state_text[] diff --git a/include/gtrack.h b/include/gtrack.h new file mode 100644 index 0000000..190f2fc --- /dev/null +++ b/include/gtrack.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +#ifndef _gtrack_header +#define _gtrack_header + +//Game Tracker client code header + +#define GAMEPORT 7802 + +#define MAX_NET_RETRIES 30 +#define NET_ACK_TIMEOUT 2500 +#define NET_GAME_TIMEOUT 300 //time in seconds + +#define MAX_GAME_DATA_SIZE 500 + +#define MAX_GENERIC_GAME_NAME_LEN 32 + +#define MAX_GAME_LISTS_PER_PACKET 10 + +#define CHANNEL_LEN 33 + +#define MAX_FREESPACE_PLAYERS 16 +#define MAX_FREESPACE_PLAYER_NAME_LEN 32 +#define MAX_FREESPACE_MISSION_NAME_LEN 32 +#define MAX_FREESPACE_PLAYERS 16 + +#define GNT_SERVER_ACK 0 +#define GNT_CLIENT_ACK 1 +#define GNT_GAMESTARTED 2 +#define GNT_GAMEOVER 3 +#define GNT_GAMEUPDATE 4 +#define GNT_GAMELIST_REQ 5 +#define GNT_GAMELIST_DATA 6 +#define GNT_GAME_COUNT_REQ 7 +#define GNT_GAME_COUNT_DATA 8 + +#define GT_FREESPACE 1 +#define GT_DESCENT3 2 +#define GT_TUBERACER 3 +#define GT_FREESPACE2 4 +#define GT_UNUSED 0 + +#define GAME_HEADER_ONLY_SIZE (sizeof(game_packet_header)-MAX_GAME_DATA_SIZE) + +#pragma pack(push, 1) + typedef struct { + unsigned int len; //Length of entire packet; + unsigned char game_type; //1==freespace (GT_FREESPACE), 2==D3, 3==tuberacer, etc. + SOCKADDR_IN addr; + int type; //Used to specify what to do ie. Add a new net game (GNT_GAMESTARTED), remove a net game (game over), etc. + unsigned int sig; //Unique identifier for client ACKs (The server always fills this in, the client responds) + + char data[MAX_GAME_DATA_SIZE]; + } game_packet_header; +#pragma pack(pop) + +#ifdef MAKE_FS1 + +typedef struct { + char game_name[MAX_GENERIC_GAME_NAME_LEN]; + int difficulty; + int type; //game type; + int state; + int max_players; + int current_num_players; + char mission_name[MAX_FREESPACE_MISSION_NAME_LEN]; +// char mission_file[MAX_FREESPACE_MISSION_FILE_LEN]; +// char mission_url[MAX_FREESPACE_MISSION_URL_LEN]; + char players[MAX_FREESPACE_PLAYERS][MAX_FREESPACE_PLAYER_NAME_LEN]; + int player_rank[MAX_FREESPACE_PLAYERS]; + char channel[CHANNEL_LEN]; +} freespace_net_game_data; + +#else + +typedef struct { + char game_name[MAX_GENERIC_GAME_NAME_LEN]; + int difficulty; + int type; //game type; + int state; + int max_players; + int current_num_players; + char mission_name[MAX_FREESPACE_MISSION_NAME_LEN]; + char channel[CHANNEL_LEN]; +} freespace2_net_game_data; + +#endif + +typedef struct _active_games{ + int game_type; //ie. GT_FREESPACE GT_DESCENT3, etc. + SOCKADDR addr; + unsigned int last_update; //Time we last got an update from this game + char data[MAX_GAME_DATA_SIZE]; //memory to hold the game specific data + _active_games *next; +} active_games; + +typedef struct { + unsigned char game_type; + char game_name[MAX_GAME_LISTS_PER_PACKET][MAX_GENERIC_GAME_NAME_LEN]; + unsigned int game_server[MAX_GAME_LISTS_PER_PACKET]; + unsigned short port[MAX_GAME_LISTS_PER_PACKET]; +} game_list; + +typedef struct { + int rank; // Try to find opponents with a rank similar to this + char channel[CHANNEL_LEN]; // only give us games in this channel +} filter_game_list_struct; + + +//Function prototypes + +int InitGameTrackerClient(int gametype); +void IdleGameTracker(); +void UpdateGameData(void *buffer); +game_list * GetGameList(); +void RequestGameList(); +void RequestGameListWithFilter(void *filter); +void RequestGameCountWithFilter(void *filter); + +// void SendGameOver(); +//Start New 7-9-98 +int SendGameOver(); +//End New 7-9-98 + +void StartTrackerGame(void *buffer); + +void AckPacket(int sig); + +//Definitions +#define MAX_GAME_BUFFERS 20 //Thats a lot considering 20 games per game_list struct + +#define TRACKER_UPDATE_INTERVAL 300 //300 seconds +#define TRACKER_RESEND_TIME 2000 //2000ms + + +#endif diff --git a/include/multi_fstracker.h b/include/multi_fstracker.h new file mode 100644 index 0000000..712ed06 --- /dev/null +++ b/include/multi_fstracker.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +/* + * $Logfile: /Freespace2/code/Network/multi_fstracker.h $ + * $Revision: 8 $ + * $Date: 8/25/99 4:38p $ + * $Author: Dave $ + * + * $Log: /Freespace2/code/Network/multi_fstracker.h $ + * + * 8 8/25/99 4:38p Dave + * Updated PXO stuff. Make squad war report stuff much more nicely. + * + * 7 6/07/99 9:51p Dave + * Consolidated all multiplayer ports into one. + * + * 6 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 5 2/12/99 6:16p Dave + * Pre-mission Squad War code is 95% done. + * + * 4 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 3 2/03/99 6:06p Dave + * Groundwork for FS2 PXO usertracker support. Gametracker support next. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 12 9/04/98 3:51p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 11 5/21/98 9:45p Dave + * Lengthened tracker polling times. Put in initial support for PXO + * servers with channel filters. Fixed several small UI bugs. + * + * 10 5/18/98 9:15p Dave + * Put in network config file support. + * + * 9 5/15/98 9:52p Dave + * Added new stats for freespace. Put in artwork for viewing stats on PXO. + * + * 8 5/05/98 2:10p Dave + * Verify campaign support for testing. More new tracker code. + * + * 7 4/29/98 12:11a Dave + * Put in first rev of full API support for new master tracker. + * + * 6 4/28/98 7:50p Dave + * Fixing a broken makefile. + * + * 5 4/28/98 5:10p Dave + * Fixed multi_quit_game() client side sequencing problem. Turn off + * afterburners when ending multiplayer mission. Begin integration of mt + * API from Kevin Bentley. + * + * 4 2/02/98 8:44p Dave + * Finished redoing master tracker stats transfer. + * + * 3 1/31/98 4:32p Dave + * Put in new support for VMT player validation, game logging in, and game + * logging out. Need to finish stats transfer. + * + * 2 1/30/98 5:53p Dave + * Revamped master tracker API + * + * 1 1/30/98 5:50p Dave + * + * $NoKeywords: $ + */ +#ifndef _FREESPACE_SPECIFIC_MASTER_TRACKER_HEADER +#define _FREESPACE_SPECIFIC_MASTER_TRACKER_HEADER + +// ----------------------------------------------------------------------------------- +// FREESPACE MASTER TRACKER DEFINES/VARS +// + +// tracker mission validation status +#define MVALID_STATUS_UNKNOWN -1 +#define MVALID_STATUS_VALID 0 +#define MVALID_STATUS_INVALID 1 + +// tracker squad war validation status +#define MSW_STATUS_UNKNOWN -1 +#define MSW_STATUS_VALID 0 +#define MSW_STATUS_INVALID 1 + +struct vmt_freespace2_struct; +struct scoring_struct; +struct squad_war_request; +struct squad_war_result; + +// channel to associate when creating a server +extern char Multi_fs_tracker_channel[255]; + +// channel to use when polling the tracker for games +extern char Multi_fs_tracker_filter[255]; + +// ----------------------------------------------------------------------------------- +// FREESPACE MASTER TRACKER DECLARATIONS +// + +// give some processor time to the tracker API +void multi_fs_tracker_process(); + +// initialize the master tracker API for Freespace +void multi_fs_tracker_init(); + +// validate the current player with the master tracker (will create the pilot on the MT if necessary) +int multi_fs_tracker_validate(int show_error); + +// attempt to log the current game server in with the master tracker +void multi_fs_tracker_login_freespace(); + +// attempt to update all player statistics and scores on the tracker +int multi_fs_tracker_store_stats(); + +// attempt to update all player statistics (standalone mode) +int multi_fs_std_tracker_store_stats(); + +// log freespace out of the tracker +void multi_fs_tracker_logout(); + +// send a request for a list of games +void multi_fs_tracker_send_game_request(); + +// if the API has successfully been initialized and is running +int multi_fs_tracker_inited(); + +// update our settings on the tracker regarding the current netgame stuff +void multi_fs_tracker_update_game(netgame_info *ng); + +// if we're currently busy performing some tracker operation (ie, you should wait or not) +int multi_fs_tracker_busy(); + +// copy a freespace stats struct to a tracker-freespace stats struct +void multi_stats_fs_to_tracker(scoring_struct *fs, vmt_freespace2_struct *vmt, player *pl, int tracker_id); + +// copy a tracker-freespace stats struct to a freespace stats struct +void multi_stats_tracker_to_fs(vmt_freespace2_struct *vmt, scoring_struct *fs); + +// return an MVALID_STATUS_* value, or -2 if the user has "cancelled" +int multi_fs_tracker_validate_mission(char *filename); + +// return an MSW_STATUS_* value +int multi_fs_tracker_validate_sw(squad_war_request *sw_req, char *bad_reply); + +// store the results of a squad war mission on PXO, return 1 on success +int multi_fs_tracker_store_sw(squad_war_result *sw_res, char *bad_reply); + +#endif diff --git a/include/multi_options.h b/include/multi_options.h index acca4f2..1941ec4 100644 --- a/include/multi_options.h +++ b/include/multi_options.h @@ -119,6 +119,7 @@ typedef struct multi_global_options { ushort port; // port we're running on - for allowing multiple servers on one machine int log; // use a logfile int datarate_cap; // datarate cap for OBJ_UPDATE_HIGH + int pxo; // PXO enabled char user_tracker_ip[MULTI_OPTIONS_STRING_LEN]; // ip address of user tracker char game_tracker_ip[MULTI_OPTIONS_STRING_LEN]; // ip address of game tracker char pxo_ip[MULTI_OPTIONS_STRING_LEN]; // ip address of pxo chat server diff --git a/include/multi_pxo.h b/include/multi_pxo.h new file mode 100644 index 0000000..c8842e1 --- /dev/null +++ b/include/multi_pxo.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +/* + * $Logfile: /Freespace2/code/Network/multi_pxo.h $ + * $Revision: 5 $ + * $Date: 11/02/99 2:32p $ + * $Author: Jefff $ + * + * $Log: /Freespace2/code/Network/multi_pxo.h $ + * + * 5 11/02/99 2:32p Jefff + * updated some URLs + * + * 4 10/06/99 10:31a Jefff + * url open fuction available to all + * + * 3 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 9 7/08/98 4:54p Dave + * Join last used channel when returning from the games list. + * + * 8 5/23/98 3:02a Dave + * Pxo tweaks. + * + * 7 5/21/98 9:45p Dave + * Lengthened tracker polling times. Put in initial support for PXO + * servers with channel filters. Fixed several small UI bugs. + * + * 6 5/19/98 8:35p Dave + * Revamp PXO channel listing system. Send campaign goals/events to + * clients for evaluation. Made lock button pressable on all screens. + * + * 5 5/19/98 1:35a Dave + * Tweaked pxo interface. Added rankings url to pxo.cfg. Make netplayer + * local options update dynamically in netgames. + * + * 4 5/18/98 9:15p Dave + * Put in network config file support. + * + * 3 5/15/98 12:09a Dave + * New tracker api code. New game tracker code. Finished up first run of + * the PXO screen. Fixed a few game server list exceptions. + * + * 2 5/12/98 2:46a Dave + * Rudimentary communication between Parallax Online and freespace. Can + * get and store channel lists. + * + * 1 5/11/98 11:47p Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _PARALLAX_ONLINE_HEADER_FILE +#define _PARALLAX_ONLINE_HEADER_FILE + +// ---------------------------------------------------------------------------------------------------- +// PXO DEFINES/VARS +// + +// default url for PXO rankings +//#define MULTI_PXO_RANKINGS_URL "http://www.volition-inc.com" +#define MULTI_PXO_RANKINGS_URL "http://www.pxo.net/rankings/fs2full.cfm" + + +// default url for PXO account creation +//#define MULTI_PXO_CREATE_URL "http://www.parallaxonline.com/register.html" +#define MULTI_PXO_CREATE_URL "http://www.pxo.net/newaccount.cfm" + +// default url for PXO account verification +//#define MULTI_PXO_VERIFY_URL "http://www.parallaxonline.com/verify.html" +#define MULTI_PXO_VERIFY_URL "http://www.pxo.net/verify.cfm" + +// default url for PXO banners +#define MULTI_PXO_BANNER_URL "http://www.pxo.net/files/banners" + +// tracker and PXO addresses +#define MULTI_PXO_USER_TRACKER_IP "ut.pxo.net" +#define MULTI_PXO_GAME_TRACKER_IP "gt.pxo.com" +#define MULTI_PXO_CHAT_IP "chat.pxo.net" + +// ---------------------------------------------------------------------------------------------------- +// PXO FUNCTIONS +// + +// initialize the PXO screen +void multi_pxo_init(int use_last_channel); + +// do frame for the PXO screen +void multi_pxo_do(); + +// close the PXO screen +void multi_pxo_close(); + + +// initialize the PXO help screen +void multi_pxo_help_init(); + +// do frame for PXO help +void multi_pxo_help_do(); + +// close the pxo screen +void multi_pxo_help_close(); + +// open up a URL +void multi_pxo_url(char *url); + +// called from the game tracker API - server count update for a channel +void multi_pxo_channel_count_update(char *name,int count); + +#endif diff --git a/include/multi_sw.h b/include/multi_sw.h new file mode 100644 index 0000000..460676e --- /dev/null +++ b/include/multi_sw.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +/* + * $Logfile: /Freespace2/code/Network/multi_sw.h $ + * $Revision: 7 $ + * $Date: 9/13/99 11:30a $ + * $Author: Dave $ + * + * $Log: /Freespace2/code/Network/multi_sw.h $ + * + * 7 9/13/99 11:30a Dave + * Added checkboxes and functionality for disabling PXO banners as well as + * disabling d3d zbuffer biasing. + * + * 6 8/25/99 4:38p Dave + * Updated PXO stuff. Make squad war report stuff much more nicely. + * + * 5 6/07/99 9:51p Dave + * Consolidated all multiplayer ports into one. + * + * 4 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 3 2/12/99 6:16p Dave + * Pre-mission Squad War code is 95% done. + * + * 2 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE2_SQUAD_WAR_HEADER_FILE +#define __FREESPACE2_SQUAD_WAR_HEADER_FILE + +#include "ptrack.h" + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER SQUAD WAR DEFINES/VARS +// + +// the min # of players required from each squad for the mission to be valid +#define MULTI_SW_MIN_PLAYERS 1 + +// set on the host in response to a standalone sw query, -1 == waiting, 0 == fail, 1 == success +extern int Multi_sw_std_query; + +// match code +#define MATCH_CODE_LEN 34 // from ptrack.h +extern char Multi_sw_match_code[MATCH_CODE_LEN]; + +// reply from a standalone on a bad response +extern char Multi_sw_bad_reply[MAX_SQUAD_RESPONSE_LEN+1]; + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER SQUAD WAR FUNCTIONS +// + +// call before loading level - mission sync phase. only the server need do this +void multi_sw_level_init(); + +// determine if everything is ok to move forward for a squad war match +int multi_sw_ok_to_commit(); + +// query PXO on the standalone +void multi_sw_std_query(char *match_code); + +// call to update everything on the tracker +void multi_sw_report(int stats_saved); + +#endif + diff --git a/include/psnet2.h b/include/psnet2.h index 2e2ad2f..000976f 100644 --- a/include/psnet2.h +++ b/include/psnet2.h @@ -177,11 +177,11 @@ struct timeval; #endif // wrappers around select() and recvfrom() for lagging/losing data, and for sorting through different packet types -int RECVFROM(uint s, char * buf, int len, int flags, sockaddr *from, int *fromlen, int psnet_type); -int SELECT(int nfds, fd_set *readfds, fd_set *writefds, fd_set*exceptfds, const timeval* timeout, int psnet_type); +int RECVFROM(SOCKET s, char * buf, int len, int flags, sockaddr *from, int *fromlen, int psnet_type); +int SELECT(int nfds, fd_set *readfds, fd_set *writefds, fd_set*exceptfds, struct timeval* timeout, int psnet_type); // wrappers around sendto to sorting through different packet types -int SENDTO(uint s, char * buf, int len, int flags, sockaddr * to, int tolen, int psnet_type); +int SENDTO(SOCKET s, char * buf, int len, int flags, sockaddr * to, int tolen, int psnet_type); // call this once per frame to read everything off of our socket void PSNET_TOP_LAYER_PROCESS(); diff --git a/include/ptrack.h b/include/ptrack.h new file mode 100644 index 0000000..8219b45 --- /dev/null +++ b/include/ptrack.h @@ -0,0 +1,338 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +#ifndef _pilot_track_header +#define _pilot_track_header + +#include "scoring.h" // for medals count + +//Pilot tracker client header +#ifdef FS2_DEMO + #define REGPORT 7802 +#else + #define REGPORT 8811 +#endif + +#define MAX_NET_RETRIES 30 +#define NET_ACK_TIMEOUT 2500 + +#define MAX_PXO_FILENAME_LEN 32 + +//This is for type +#define UNT_CONTROL 0 +#define UNT_NEW_ID_REQUEST 1 +#define UNT_VALIDAT_ID_REQUEST 2 +#define UNT_LOOKUP_ID_REQUEST 3 +#define UNT_UPDATE_ID_REQUEST 4 +#define UNT_MOTD_REQUEST 5 +#define UNT_MOTD_RESPONSE 6 +#define UNT_PILOT_DATA_READ 7 // Request from the game for pilot info +#define UNT_PILOT_DATA_RESPONSE 8 // Mastertracker's response to a read request (has pilot data in this packet) +#define UNT_PILOT_DATA_WRITE 9 // Request from the server to write a user record +#define UNT_PILOT_WRITE_FAILED 10 // Server didn't update the pilots record for some reason +#define UNT_PILOT_WRITE_SUCCESS 11 // Server updated the pilots record +#define UNT_PILOT_READ_FAILED 12 // Couldn't find the record +#define UNT_LOGIN_AUTH_REQUEST 13 // Request login authentication by login/password only (returns tracker id) +#define UNT_LOGIN_NO_AUTH 14 // Couldn't login this user (code has reason) +#define UNT_LOGIN_AUTHENTICATED 15 // User was authenticated (data has sz string with tracker id) +#define UNT_VALID_FS_MSN_REQ 18 // Client asking if this is a valid mission +#define UNT_VALID_FS_MSN_RSP 19 // Server Response (code has the answer) + +#define UNT_PILOT_DATA_READ_NEW 20 // New packet for requesting reads (tracker will get new security field when receiving this) +#define UNT_PILOT_DATA_WRITE_NEW 21 // New packet for requesting writes (tracker will compare security fields if this packet is used) + +// 22 through 45 are D3 specific codes +#define UNT_VALID_FS2_MSN_REQ 46 // validated mission request to PXO for FS2 +#define UNT_VALID_FS2_MSN_RSP 47 // validated mission response from PXO for FS2 + +// validate a squad war mission +#define UNT_VALID_SW_MSN_REQ 48 // send info about a squad war mission, wait for tracker response +#define UNT_VALID_SW_MSN_RSP 49 // response from the tracker +#define UNT_SW_RESULT_WRITE 50 // report on a finished squad war mission to the tracker +#define UNT_SW_RESULT_RESPONSE 51 // response on a squad war mission write request + +#define UNT_CONTROL_VALIDATION 70 // UNT_CONTROL for validation packets + +//This is for code +#define CMD_NEW_USER_ACK 1 +#define CMD_NEW_USER_NAK 2 +#define CMD_VALIDATED_USER_ACK 3 +#define CMD_UPDATED_USER_ACK 4 +#define CMD_CLIENT_RECEIVED 5 +#define CMD_FIND_USER_NAK 6 +#define CMD_FIND_USER_ACK 7 +#define CMD_UPDATED_USER_NAK 8 +#define CMD_VALIDATED_USER_NAK 9 + //Game designators for UNT_PILOT_DATA_REQUEST and UNT_PILOT_DATA_RESPONSE +#define CMD_GAME_FREESPACE 10 +#define CMD_GAME_DESCENT3 11 +#define CMD_GAME_FREESPACE2 12 + +//This is for xcode +#define REG_NAK_EMAIL 0 // failed to register the guy because of an invalid email address +#define REG_NAK_LOGIN 1 // failed to register the guy because an existing login exists +#define REG_NAK_ERROR 2 // failed to register because of an error on the tracker +#define REG_NAK_STRINGS 3 // failed to validate because of invalid password/login match +#define REG_NAK_UNKNOWN 4 // failed to validate because the player is unknown +#define REG_NAK_UPDATE_PL 5 // update info failed because login/passwd were not correct +#define REG_NAK_UPDATE_GEN 6 // update info failed in general (tracker problem) +#define REG_NAK_UPDATE_LOG 7 // update failed because login not found +#define REG_ACK_NEW_ID 8 // New id created, just used for return code, not net packets. + +#define MAX_UDP_DATA_LENGH 480 +#define PACKED_HEADER_ONLY_SIZE (sizeof(udp_packet_header)-MAX_UDP_DATA_LENGH) +//sizeof(update_id_request) //The largest packet + +#define LOGIN_LEN 33 +#define REAL_NAME_LEN 66 +#define PASSWORD_LEN 17 +#define EMAIL_LEN 100 +#define TRACKER_ID_LEN 10 +#define PILOT_NAME_LEN 20 +#define MATCH_CODE_LEN 34 + +#define MAX_SQUAD_PLAYERS 4 +#define MAX_SQUAD_RESPONSE_LEN 255 + +// data could be one of the following: + +// type == UNT_NEW_ID_REQUEST +// Respond with ACK +typedef struct { + char first_name[REAL_NAME_LEN]; // Real Name + char last_name[REAL_NAME_LEN]; // Real Name + char login[LOGIN_LEN]; // Login id + char password[PASSWORD_LEN]; // password + char email[EMAIL_LEN]; // Email Address + unsigned char showemail; // 0==don't show 1 == show + unsigned char showname; // 0==don't show 1 == show +} new_id_request; + + +// type == UNT_VALIDAT_ID_REQUEST or UNT_LOOKUP_ID_REQUEST +typedef struct { + char login[LOGIN_LEN]; // Login id + char password[PASSWORD_LEN]; // password + char tracker_id[TRACKER_ID_LEN]; // Tracker ID +} validate_id_request; + +// type == UNT_UPDATE_ID_REQUEST +typedef struct { + char old_login[LOGIN_LEN]; // Login before it's changed. + char old_password[PASSWORD_LEN]; // Password before it's changed + char tracker_id[TRACKER_ID_LEN]; // Tracker ID (not sure if we need it for updating, but maybe) + char first_name[REAL_NAME_LEN]; // Real Name + char last_name[REAL_NAME_LEN]; // Real Name + char login[LOGIN_LEN]; // Login id (new) + char password[PASSWORD_LEN]; // password (new) + char email[EMAIL_LEN]; // Email Address (new) + unsigned char showemail; // 0==don't show 1 == show + unsigned char showname; // 0==don't show 1 == show +} update_id_request; + +typedef struct { + char pilot_name[PILOT_NAME_LEN]; // Login id + char tracker_id[TRACKER_ID_LEN]; // Tracker ID +} pilot_request; + +// type == UNT_VALID_SW_MSN_REQ +typedef struct squad_war_request { + int squad_plr1[MAX_SQUAD_PLAYERS]; // id #'s for all squad members in the match + int squad_plr2[MAX_SQUAD_PLAYERS]; // id #'s for all squad memebrs in the match + + ubyte squad_count1; // # of players present in squad 1 + ubyte squad_count2; // # of players present in squad 2 + + char match_code[MATCH_CODE_LEN]; // code for the match + + char mission_filename[MAX_PXO_FILENAME_LEN]; // filename of mission + int mission_checksum; // mission checksum +} squad_war_request; + +// type == UNT_SW_RESULT_WRITE +typedef struct squad_war_result { + char match_code[MATCH_CODE_LEN]; // code for the match + ubyte result; // result of the match, 0 == tie, 1 == one team won + ubyte squad_count1; // # of players in winning squad + ubyte squad_count2; // # of players in the losing squad + int squad_winners[MAX_SQUAD_PLAYERS]; // list of players on the winning team + int squad_losers[MAX_SQUAD_PLAYERS]; // list of players on the losing team +} squad_war_result; + +// type == UNT_VALID_SW_MSN_RSP and UNT_SW_RESULT_RESPONSE +typedef struct squad_war_response { + char reason[MAX_SQUAD_RESPONSE_LEN]; + unsigned char accepted; +} squad_war_response; + +#pragma pack(push, 1) + typedef struct { + unsigned char type; // type + unsigned short len; // Length of total packet, including this header + unsigned int code; // For control messages + unsigned short xcode; // For control/NAK messages and for sigs. + unsigned int sig; // To identify unique return ACKs + unsigned int security; // Just a random value, we store the last value used in the user record + // So we don't process the same request twice. + unsigned char data[MAX_UDP_DATA_LENGH]; + } udp_packet_header; +#pragma pack(pop) + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// GAME SPECIFIC structures + + +#ifdef MAKE_FS1 +// ------------------------------------------------------------------------------------------------------------ +// FREESPACE +// NOTE : this is obsolete for FS2 - we use our own FS2 struct +#define MAX_FS_MEDALS 16 // these should correspond directly to those defined inside of freespace. +#define MAX_FS_SHIP_TYPES 75 // this one will be scaled down to be the correct # eventually +typedef struct vmt_freespace_struct { + char tracker_id[TRACKER_ID_LEN]; + char pilot_name[PILOT_NAME_LEN]; + + int score; + int rank; + int medals[MAX_FS_MEDALS]; + + int kills[MAX_FS_SHIP_TYPES]; // only valid kills (i.e. not on friendlies). + int assists; + int kill_count; // total alltime kills + int kill_count_ok; // total valid alltime kills (no friendlies) + unsigned int p_shots_fired; // primary weapon + unsigned int s_shots_fired; // secondary weapon + + unsigned int p_shots_hit; // primary + unsigned int s_shots_hit; // secondary + + unsigned int p_bonehead_hits; // hits/kills on the players own team + unsigned int s_bonehead_hits; + int bonehead_kills; + + int security; + unsigned char virgin_pilot; //This pilot was just created if TRUE + unsigned int checksum; //This value needs to be equal to whatever the checksum is once the packet is decoded + + unsigned int missions_flown; // # of missions flown to completion + unsigned int flight_time; // total hours of flight time + unsigned int last_flown; // data/time of last mission flown +} vmt_freespace_struct; +#define FREESPACE_BLOCK_SIZE (sizeof(vmt_freespace_struct)) + +#else + +// ------------------------------------------------------------------------------------------------------------ +// FREESPACE2 +#ifdef FS2_DEMO + #define MAX_FS2_MEDALS 16 // these should correspond directly to those defined inside of freespace. + #define MAX_FS2_SHIP_TYPES 120 // this one will be scaled down to be the correct # eventually + typedef struct vmt_freespace2_struct { + char tracker_id[TRACKER_ID_LEN]; + char pilot_name[PILOT_NAME_LEN]; + + int score; + int rank; + int medals[MAX_FS2_MEDALS]; + + ushort kills[MAX_FS2_SHIP_TYPES]; // only valid kills (i.e. not on friendlies). + int assists; + int kill_count; // total alltime kills + int kill_count_ok; // total valid alltime kills (no friendlies) + unsigned int p_shots_fired; // primary weapon + unsigned int s_shots_fired; // secondary weapon + + unsigned int p_shots_hit; // primary + unsigned int s_shots_hit; // secondary + + unsigned int p_bonehead_hits; // hits/kills on the players own team + unsigned int s_bonehead_hits; + int bonehead_kills; + + int security; + unsigned char virgin_pilot; //This pilot was just created if TRUE + unsigned int checksum; //This value needs to be equal to whatever the checksum is once the packet is decoded + + unsigned int missions_flown; // # of missions flown to completion + unsigned int flight_time; // total hours of flight time + unsigned int last_flown; // data/time of last mission flown + } vmt_freespace2_struct; +#else + #define MAX_FS2_MEDALS NUM_MEDALS // these should correspond directly to those defined inside of freespace. + #define MAX_FS2_SHIP_TYPES MAX_SHIP_TYPES // this one will be scaled down to be the correct # eventually + typedef struct vmt_freespace2_struct { + char tracker_id[TRACKER_ID_LEN]; + char pilot_name[PILOT_NAME_LEN]; + + int score; + int rank; + int assists; + int kill_count; // total alltime kills + int kill_count_ok; // total valid alltime kills (no friendlies) + unsigned int p_shots_fired; // primary weapon + unsigned int s_shots_fired; // secondary weapon + + unsigned int p_shots_hit; // primary + unsigned int s_shots_hit; // secondary + + unsigned int p_bonehead_hits; // hits/kills on the players own team + unsigned int s_bonehead_hits; + int bonehead_kills; + + int security; + unsigned char virgin_pilot; //This pilot was just created if TRUE + unsigned int checksum; //This value needs to be equal to whatever the checksum is once the packet is decoded + + unsigned int missions_flown; // # of missions flown to completion + unsigned int flight_time; // total hours of flight time + unsigned int last_flown; // data/time of last mission flown + + unsigned short num_medals; + unsigned short num_ship_types; + + int medals[MAX_FS2_MEDALS]; + unsigned short kills[MAX_FS2_SHIP_TYPES]; // only valid kills (i.e. not on friendlies). + } vmt_freespace2_struct; +#endif +#define FREESPACE2_BLOCK_SIZE (sizeof(vmt_freespace2_struct)) + +#endif // MAKE_FS1 + +//Function prototypes +int InitPilotTrackerClient(); +void AckServer(unsigned int sig); + +#ifdef MAKE_FS1 +int SendFSPilotData(vmt_freespace2_struct *fs_pilot); +int GetFSPilotData(vmt_freespace2_struct *fs_pilot, const char *pilot_name, const char *tracker_id, int update_security); +#else +int SendFSPilotData(vmt_freespace2_struct *fs_pilot); +int GetFSPilotData(vmt_freespace2_struct *fs_pilot, const char *pilot_name, const char *tracker_id, int update_security); +#endif +int SendSWData(squad_war_result *sw_res, squad_war_response *sw_resp); +void PollPTrackNet(); + +//Definitions +#define STATE_IDLE 0 +#define STATE_SENDING_PILOT 1 +#define STATE_READING_PILOT 2 +#define STATE_RECEIVED_PILOT 3 +#define STATE_WROTE_PILOT 4 +#define STATE_TIMED_OUT 5 +#define STATE_PILOT_NOT_FOUND 6 +#define STATE_WRITE_PILOT_FAILED 7 + +#define PILOT_REQ_TIMEOUT 30000 +#define PILOT_REQ_RESEND_TIME 3500 + + +#endif + + diff --git a/include/timer.h b/include/timer.h index 9861da5..c33b3cc 100644 --- a/include/timer.h +++ b/include/timer.h @@ -88,6 +88,7 @@ extern void timer_set_function( void * function ); extern fix timer_get_fixed_seconds(); // Rolls about every 9 hours... extern fix timer_get_fixed_secondsX(); // Assume interrupts already disabled extern fix timer_get_approx_seconds(); // Returns time since program started... accurate to 1/120th of a second +extern int timer_get_seconds(); extern int timer_get_milliseconds(); // extern int timer_get_microseconds(); diff --git a/include/unix.h b/include/unix.h index a0c147d..e966bcb 100644 --- a/include/unix.h +++ b/include/unix.h @@ -13,6 +13,7 @@ extern int filelength (int fd); +#define ioctlsocket(A,B,C) ioctl(A,B,C) #define ioctlsocket(A,B,C) ioctl(A,B,C) #define closesocket(A) close(A) #define SOCKET int diff --git a/include/valid.h b/include/valid.h new file mode 100644 index 0000000..78f2ea9 --- /dev/null +++ b/include/valid.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + + +#ifndef _valid_client_header +#define _valid_client_header + +#include "ptrack.h" + +//Validate User Header + +//Function prototypes + + +//Call with a valid struct to validate a user +//Call with NULL to poll + +//Return codes: +// -3 Still waiting (returned if we were waiting for a tracker response and ValidateUser was called with a non-NULL value +// -2 Timeout waiting for tracker to respond +// -1 User invalid +// 0 Still waiting for response from tracker/Idle +// 1 User valid +int ValidateUser(validate_id_request *valid_id, char *trackerid); +void AckValidServer(unsigned int sig); + +int InitValidateClient(void); +void ValidIdle(); + + +//Definitions + + +// #define PILOT_REQ_TIMEOUT 10000 +// #define PILOT_REQ_RESEND_TIME 750 + +#define VALID_STATE_IDLE 1 +#define VALID_STATE_WAITING 2 +#define VALID_STATE_VALID 3 +#define VALID_STATE_INVALID 4 +#define VALID_STATE_TIMEOUT 5 + +typedef struct vmt_validate_mission_req_struct { + long checksum; + char file_name[100]; +} vmt_validate_mission_req_struct; + +// query the usertracker to validate a mission +int ValidateMission(vmt_validate_mission_req_struct *valid_msn); + +// query the usertracker to validate a squad war match +int ValidateSquadWar(squad_war_request *sw_req, squad_war_response *sw_resp); + +#endif diff --git a/src/freespace2/freespace.cpp b/src/freespace2/freespace.cpp index 64275d2..1a08d10 100644 --- a/src/freespace2/freespace.cpp +++ b/src/freespace2/freespace.cpp @@ -604,6 +604,7 @@ #include "multiutil.h" #include "multimsgs.h" #include "multiui.h" +#include "multi_pxo.h" #include "cfile.h" #include "player.h" #include "freespace.h" @@ -5156,6 +5157,14 @@ void game_process_event( int current_state, int event ) // multiplayer stuff follow these comments + case GS_EVENT_PXO: + gameseq_set_state(GS_STATE_PXO); + break; + + case GS_EVENT_PXO_HELP: + gameseq_set_state(GS_STATE_PXO_HELP); + break; + case GS_EVENT_MULTI_JOIN_GAME: gameseq_set_state( GS_STATE_MULTI_JOIN_GAME ); break; @@ -5699,6 +5708,16 @@ void game_leave_state( int old_state, int new_state ) case GS_STATE_LOOP_BRIEF: loop_brief_close(); break; + + case GS_STATE_PXO: + if (new_state != GS_STATE_PXO_HELP) { + multi_pxo_close(); + } + break; + + case GS_STATE_PXO_HELP: + multi_pxo_help_close(); + break; } } @@ -6143,6 +6162,19 @@ void mouse_force_pos(int x, int y); loop_brief_init(); break; + case GS_STATE_PXO: + if (old_state != GS_STATE_PXO_HELP) { + STUB_FUNCTION; + // TODO: use_last_channel? + + multi_pxo_init(0); + } + break; + + case GS_STATE_PXO_HELP: + multi_pxo_help_init(); + break; + } // end switch } @@ -6433,6 +6465,16 @@ void game_do_state(int state) loop_brief_do(); break; + case GS_STATE_PXO: + game_set_frametime(GS_STATE_PXO); + multi_pxo_do(); + break; + + case GS_STATE_PXO_HELP: + game_set_frametime(GS_STATE_PXO_HELP); + multi_pxo_help_do(); + break; + } // end switch(gs_current_state) } diff --git a/src/gamesequence/gamesequence.cpp b/src/gamesequence/gamesequence.cpp index 681c59d..2f2a096 100644 --- a/src/gamesequence/gamesequence.cpp +++ b/src/gamesequence/gamesequence.cpp @@ -341,7 +341,9 @@ const char *GS_event_text[] = "GS_EVENT_TOGGLE_GLIDE", // 70 "GS_EVENT_RED_ALERT", "GS_EVENT_SIMULATOR_ROOM", - "GS_EVENT_EMD_CAMPAIGN", + "GS_EVENT_EMD_CAMPAIGN", + "GS_EVENT_PXO", + "GS_EVENT_PXO_HELP" }; //XSTR:ON @@ -408,6 +410,8 @@ const char *GS_state_text[] = "GS_STATE_CMD_BRIEF", "GS_STATE_RED_ALERT", "GS_STATE_END_OF_CAMPAIGN", + "GS_STATE_PXO", + "GS_STATE_PXO_HELP" // 60 }; //XSTR:ON diff --git a/src/inetfile/chttpget.cpp b/src/inetfile/chttpget.cpp index ece61f9..622dd94 100644 --- a/src/inetfile/chttpget.cpp +++ b/src/inetfile/chttpget.cpp @@ -163,7 +163,7 @@ int HTTPObjThread( void * obj ) void ChttpGet::AbortGet() { m_Aborting = true; - while(!m_Aborted) SDL_Delay(10); //Wait for the thread to end + while(!m_Aborted) SDL_Delay(50); //Wait for the thread to end } ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport) @@ -260,16 +260,11 @@ void ChttpGet::GetFile(char *URL,char *localfile) SDL_strlcpy(m_szDir, dirstart, SDL_arraysize(m_szDir));//,(filestart-dirstart)); int len = min((dirstart-pURL), (int)SDL_arraysize(m_szHost)); SDL_strlcpy(m_szHost, pURL, len); - } SDL_Thread *thread = SDL_CreateThread(HTTPObjThread, "HTTPObjThread", this); if(thread == NULL) - { - m_State = HTTP_STATE_INTERNAL_ERROR; m_Aborted = true; - return; - } else { int ret_val = 0; diff --git a/src/io/timer.cpp b/src/io/timer.cpp index 76a29dc..d80b11a 100644 --- a/src/io/timer.cpp +++ b/src/io/timer.cpp @@ -142,6 +142,11 @@ fix timer_get_approx_seconds() return timer_get_fixed_seconds(); } +int timer_get_seconds() +{ + return SDL_GetTicks() / 1000; +} + int timer_get_milliseconds() { return SDL_GetTicks(); diff --git a/src/menuui/optionsmenumulti.cpp b/src/menuui/optionsmenumulti.cpp index f30824e..b568990 100644 --- a/src/menuui/optionsmenumulti.cpp +++ b/src/menuui/optionsmenumulti.cpp @@ -1116,7 +1116,7 @@ void options_multi_init_protocol_vars() Om_local_broadcast = (Player->m_local_options.flags & MLO_FLAG_LOCAL_BROADCAST) ? 1 : 0; // whether or not we're playing on the tracker - Om_tracker_flag = 0; // (Multi_options_g.protocol == NET_TCP) && Multi_options_g.pxo ? 1 : 0; + Om_tracker_flag = (Multi_options_g.protocol == NET_TCP) && Multi_options_g.pxo ? 1 : 0; // load the ip address list Om_ip_disp_count = 0; @@ -1264,6 +1264,9 @@ void options_multi_protocol_accept() // active protocol Multi_options_g.protocol = Om_protocol; + // VMT status + Multi_options_g.pxo = Om_tracker_flag; + // copy the VMT login and password data Om_tracker_login.get_text(Multi_tracker_login); Om_tracker_passwd.get_text(Multi_tracker_passwd); diff --git a/src/network/chat_api.cpp b/src/network/chat_api.cpp new file mode 100644 index 0000000..c45b2b2 --- /dev/null +++ b/src/network/chat_api.cpp @@ -0,0 +1,1336 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + + +#ifdef PLAT_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WSAGetLastError() (errno) +#else +#include +typedef int socklen_t; +#endif + +#include "pstypes.h" +#include "chat_api.h" + +#define MAXCHATBUFFER 500 + +static SOCKET Chatsock; +static SOCKADDR_IN Chataddr; +static int Socket_connecting = 0; +static char Nick_name[33]; +static char Orignial_nick_name[33]; +static int Nick_variety = 0; +static char szChat_channel[33] = ""; +static char Input_chat_buffer[MAXCHATBUFFER] = ""; +static char Chat_tracker_id[33]; +static char Getting_user_channel_info_for[33] = ""; +static char Getting_user_tracker_info_for[33] = ""; +static int Getting_user_channel_error = 0; +static int Getting_user_tracker_error = 0; +static char User_req_tracker_id[100] = ""; //These are oversized for saftey +static char User_req_channel[100] = ""; +static char *User_list = NULL; +static char *Chan_list = NULL; +static int Socket_connected = 0; +static int Chat_server_connected = 0; +static int Joining_channel = 0; +static int Joined_channel = 0; +static int GettingChannelList = 0; +static int GettingUserTID = 0; +static int GettingUserChannel = 0; + +static Chat_user *Firstuser,*Curruser; +static Chat_command *Firstcommand,*Currcommand; +static Chat_channel *Firstchannel,*Currchannel; + +void ChatInit(void) +{ + Socket_connecting = 0; + memset(Nick_name, 0, sizeof(Nick_name)); + memset(Orignial_nick_name, 0, sizeof(Orignial_nick_name)); + Nick_variety = 0; + memset(szChat_channel, 0, sizeof(szChat_channel)); + memset(Input_chat_buffer, 0, sizeof(Input_chat_buffer)); + memset(Chat_tracker_id, 0, sizeof(Chat_tracker_id)); + memset(Getting_user_channel_info_for, 0, sizeof(Getting_user_channel_info_for)); + memset(Getting_user_tracker_info_for, 0, sizeof(Getting_user_tracker_info_for)); + Getting_user_channel_error = 0; + Getting_user_tracker_error = 0; + memset(User_req_tracker_id, 0, sizeof(User_req_tracker_id)); + memset(User_req_channel, 0, sizeof(User_req_channel)); + User_list = NULL; + Chan_list = NULL; + Socket_connected = 0; + Chat_server_connected = 0; + Joining_channel = 0; + Joined_channel = 0; + GettingChannelList = 0; + GettingUserTID = 0; + GettingUserChannel = 0; + +} + + +// Return codes: +//-2 Already connected +//-1 Failed to connect +// 0 Connecting +// 1 Connected +// Call it once with the server IP address, and it will return immediately +// with 0. Keep calling it until it returns something other than 0 +// note: the nickname may be changed if someone with that name already +// exists (Scourge1 for instance) +int ConnectToChatServer(char *serveraddr,char *nickname,char *trackerid) +{ + short chat_port; + char chat_server[50]; + char *p; + unsigned long argp = 1; + char signon_str[100]; + + //if(Socket_connected && ) return -2; + + if(!Socket_connecting) + { + unsigned long iaddr; + + strcpy(Nick_name,nickname); + strcpy(Orignial_nick_name,nickname); + strcpy(Chat_tracker_id,trackerid); + + Firstuser = NULL; + Firstcommand = NULL; + Chat_server_connected = 0; + FlushChatCommandQueue(); + + p = strchr(serveraddr,':'); + + if(NULL==p) + { + //AfxMessageBox("Invalid chat server, must be host.com:port (ie. irc.dal.net:6667)"); + return -1; + } + strncpy(chat_server,serveraddr,(p-serveraddr)); + chat_server[p-serveraddr]='\0'; + chat_port = (short)atoi(p+1); + if(0==chat_port) + { + //AfxMessageBox("Invalid chat port, must be host.com:port (ie. irc.dal.net:6667)"); + return -1; + } + + Chatsock = socket(AF_INET,SOCK_STREAM,0); + if(INVALID_SOCKET == Chatsock) + { + //AfxMessageBox("Unable to open socket!"); + return -1; + } + + memset( &Chataddr, 0, sizeof(SOCKADDR_IN) ); + Chataddr.sin_family = AF_INET; + Chataddr.sin_addr.s_addr = INADDR_ANY; + Chataddr.sin_port = 0; + + if (SOCKET_ERROR==bind(Chatsock, (SOCKADDR*)&Chataddr, sizeof (sockaddr))) + { + //AfxMessageBox("Unable to bind socket!"); + return -1; + } + ioctlsocket(Chatsock,FIONBIO,&argp); + + // first try and resolve by name + iaddr = inet_addr( chat_server ); + if ( iaddr == INADDR_NONE ) { + HOSTENT *he; + he = gethostbyname(chat_server); + if(!he) + { + return 0; + /* + //AfxMessageBox("Unable to gethostbyname.\n"); + + // try and resolve by address + unsigned int n_order = inet_addr(chat_server); + he = gethostbyaddr((char*)&n_order,4,PF_INET); + + if(!he){ + return -1; + } + */ + } + memcpy(&iaddr, he->h_addr_list[0],4); + } + + memcpy(&Chataddr.sin_addr.s_addr, &iaddr,4); //&iaddr, 4); + + + // Chataddr.sin_addr.s_addr = inet_addr(chat_server); + + Chataddr.sin_port = htons( chat_port ); + + if(SOCKET_ERROR == connect(Chatsock,(SOCKADDR *)&Chataddr,sizeof(SOCKADDR_IN))) + { + if(WSAEWOULDBLOCK == WSAGetLastError()) + { + Socket_connecting = 1; + return 0; + } + } + else + { + //This should never happen, connect should always return WSAEWOULDBLOCK + Socket_connecting = 1; + Socket_connected = 1; + return 1; + } + } + else + { + if(Chat_server_connected) + { + return 1; + } + + if(!Socket_connected) + { + //Do a few select to check for an error, or to see if we are writeable (connected) + fd_set write_fds,error_fds; + TIMEVAL timeout; + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&write_fds); + FD_SET(Chatsock,&write_fds); + //Writable -- that means it's connected + if(select(0,NULL,&write_fds,NULL,&timeout)) + { + Socket_connected = 1; + sprintf(signon_str,NOX("/USER %s %s %s :%s"),NOX("user"),NOX("user"),NOX("user"),Chat_tracker_id); + SendChatString(signon_str,1); + sprintf(signon_str,NOX("/NICK %s"),Nick_name); + SendChatString(signon_str,1); + return 0; + //Now we are waiting for Chat_server_connected + } + FD_ZERO(&error_fds); + FD_SET(Chatsock,&error_fds); + //error -- that means it's not going to connect + if(select(0,NULL,NULL,&error_fds,&timeout)) + { + return -1; + } + return 0; + } + } + + return 0; +} + +// Call it to close the connection. It returns immediately +void DisconnectFromChatServer() +{ + if(!Socket_connected) return; + SendChatString(NOX("/QUIT"),1); + shutdown(Chatsock,2); + closesocket(Chatsock); + Socket_connecting = 0; + Socket_connected = 0; + Input_chat_buffer[0] = '\0'; + if(User_list) + { + free(User_list); + User_list = NULL; + } + if(Chan_list) + { + free(Chan_list); + Chan_list = NULL; + } + + Chat_server_connected = 0; + Joining_channel = 0; + Joined_channel = 0; + RemoveAllChatUsers(); + FlushChatCommandQueue(); + return; +} + +// returns NULL if no line is there to print, otherwise returns a string to +// print (all preformatted of course) +char * GetChatText() +{ + + if(!Socket_connected) return NULL; + + //ChatGetString will do the formatting + return ChatGetString(); + +} + +// Send a string to be sent as chat, or scanned for messages (/msg +// string) +const char * SendChatString(const char *line,int raw) +{ + char szCmd[200]; + char szTarget[50]; + if(!Socket_connected) return NULL; + + if(line[0]=='/') + { + + //Start off by getting the command + strcpy(szCmd,GetWordNum(0,line+1)); + if(SDL_strcasecmp(szCmd,NOX("msg"))==0) + { + strcpy(szTarget,GetWordNum(1,line+1)); + sprintf(szCmd,NOX("PRIVMSG %s :%s\n\r"),szTarget,line+strlen(NOX("/msg "))+strlen(szTarget)+1); + send(Chatsock,szCmd,strlen(szCmd),0); + szCmd[strlen(szCmd)-2]='\0'; + return ParseIRCMessage(szCmd,MSG_LOCAL); + + } + if(SDL_strcasecmp(szCmd,NOX("me"))==0) + { + sprintf(szCmd,NOX("PRIVMSG %s :\001ACTION %s\001\n\r"),szChat_channel,line+strlen(NOX("/me "))); + send(Chatsock,szCmd,strlen(szCmd),0); + szCmd[strlen(szCmd)-2]='\0'; + return ParseIRCMessage(szCmd,MSG_LOCAL); + + } + if(SDL_strcasecmp(szCmd,NOX("xyz"))==0) + { + //Special command to send raw irc commands + sprintf(szCmd,"%s\n\r",line+strlen(NOX("/xyz "))); + send(Chatsock,szCmd,strlen(szCmd),0); + return NULL; + } + if(SDL_strcasecmp(szCmd,NOX("list"))==0) + { + sprintf(szCmd,"%s\n\r",line+1); + send(Chatsock,szCmd,strlen(szCmd),0); + return NULL; + } + if(raw) + { + sprintf(szCmd,"%s\n\r",line+1); + send(Chatsock,szCmd,strlen(szCmd),0); + return NULL; + } + return XSTR("Unrecognized command",634); + + } + else + { + if(szChat_channel[0]) + { + /* + CString sndstr; + sndstr.Format("PRIVMSG %s :%s\n\r",szChat_channel,line); + send(Chatsock,LPCSTR(sndstr),sndstr.GetLength(),0); + sndstr = sndstr.Left(sndstr.GetLength()-2); + return ParseIRCMessage((char *)LPCSTR(sndstr),MSG_LOCAL); + */ + + sprintf(szCmd,NOX("PRIVMSG %s :%s\n\r"),szChat_channel,line); + send(Chatsock,szCmd,strlen(szCmd),0); + if(strlen(szCmd) >= 2){ + szCmd[strlen(szCmd)-2] = '\0'; + return ParseIRCMessage(szCmd,MSG_LOCAL); + } + + return NULL; + } + } + + return NULL; +} + + +// Returns a structure which contains a command and possible some data (like +// a user joining or leaving) if one is waiting +// This tells you if you need to add a user from the userlist, remove a user, +// etc. Also for status messages, like if you get knocked +// off the server for some reason. +Chat_command *GetChatCommand() +{ + if(!Socket_connected) return NULL; + return GetChatCommandFromQueue(); +} + +// This function returns a list of users in the current channel, in one +// string, separated by spaces, terminated by a null +// (Spaces aren't allowed as part of a nickname) +char *GetChatUserList() +{ + int iuser_list_length = 0;; + if(User_list) + { + free(User_list); + User_list = NULL; + } + if(!Socket_connected) return NULL; + + Curruser = Firstuser; + while(Curruser) + { + iuser_list_length += strlen(Curruser->nick_name)+1; + Curruser = Curruser->next; + } + Curruser = Firstuser; + User_list = (char *)malloc(iuser_list_length+1); + User_list[0] = '\0'; + while(Curruser) + { + strcat(User_list,Curruser->nick_name); + strcat(User_list," "); + Curruser = Curruser->next; + } + + return User_list; +} + +// Call this to set/join a channel. Since we can't be sure that we will be +// able to join that channel, check it for completion +// You can't be in more than one channel at a time with this API, so you +// leave the current channel before trying to join +// a new one. Because of this if the join fails, make sure you try to join +// another channel, or the user wont be able to chat +//-1 Failed to join +// 0 joining +// 1 successfully joined +int SetNewChatChannel(char *channel) +{ + char partstr[100]; + if(!Socket_connected) return -1; + if(Joining_channel==1) + { + if(Joined_channel==1) + { + //We made it in! + Joining_channel = 0; + return 1; + } + else if(Joined_channel==-1) + { + //Error -- we got a message that the channel was invite only, or we were banned or something + Joining_channel = 0; + strcpy(szChat_channel,""); + return -1; + } + } + else + { + if(szChat_channel[0]) + { + sprintf(partstr,NOX("/PART %s"),szChat_channel); + SendChatString(partstr,1); + } + strcpy(szChat_channel,channel); + sprintf(partstr,NOX("/JOIN %s"),szChat_channel); + SendChatString(partstr,1); + Joining_channel = 1; + Joined_channel = 0; + } + + return 0; +} + + +char *ChatGetString(void) +{ + fd_set read_fds; + TIMEVAL timeout; + char ch[2]; + char *p; + int bytesread; + static char return_string[MAXCHATBUFFER]; + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&read_fds); + FD_SET(Chatsock,&read_fds); + //Writable -- that means it's connected + while(select(0,&read_fds,NULL,NULL,&timeout)) + { + bytesread = recv(Chatsock,ch,1,0); + if(bytesread) + { + ch[1] = '\0'; + + if((ch[0] == 0x0a)||(ch[0]==0x0d)) + { + if(Input_chat_buffer[0]=='\0') + { + //Blank line, ignore it + return NULL; + } + strcpy(return_string,Input_chat_buffer); + Input_chat_buffer[0] = '\0'; + + p = ParseIRCMessage(return_string,MSG_REMOTE); + + return p; + } + SDL_assert(strlen(Input_chat_buffer) < MAXCHATBUFFER-1); + strcat(Input_chat_buffer,ch); + } + else + { + //Select said we had read data, but 0 bytes read means disconnected + AddChatCommandToQueue(CC_DISCONNECTED,NULL,0); + return NULL; + } + + } + return NULL; +} + + +const char * GetWordNum(int num, const char * l_String) +{ + static char strreturn[600]; + static char ptokstr[600]; + char seps[10] = NOX(" \n\r\t"); + char *token,*strstart; + + strstart = ptokstr; + + strcpy(ptokstr,l_String); + + token=strtok(ptokstr,seps); + + for(int i=0;i!=num;i++) + { + token=strtok(NULL,seps); + } + if(token) + { + strcpy(strreturn,token); + } + else + { + return ""; + } + //check for the ':' char.... + if(token[0]==':') + { + //Its not pretty, but it works, return the rest of the string + strcpy(strreturn,l_String+((token-strstart)+1)); + } + + //return the appropriate response. + return strreturn; +} + +int AddChatUser(const char *nickname) +{ + Curruser = Firstuser; + while(Curruser) + { + if(SDL_strcasecmp(nickname,Curruser->nick_name)==0) return 0; + Curruser = Curruser->next; + } + + Curruser = Firstuser; + if(Firstuser==NULL) + { + Firstuser = (Chat_user *)malloc(sizeof(Chat_user)); + SDL_assert(Firstuser); + strcpy(Firstuser->nick_name,nickname); + Firstuser->next = NULL; + AddChatCommandToQueue(CC_USER_JOINING,nickname,strlen(nickname)+1); + return 1; + } + else + { + while(Curruser->next) + { + Curruser = Curruser->next; + } + Curruser->next = (Chat_user *)malloc(sizeof(Chat_user)); + Curruser = Curruser->next; + SDL_assert(Curruser); + strcpy(Curruser->nick_name,nickname); + Curruser->next = NULL; + AddChatCommandToQueue(CC_USER_JOINING,nickname,strlen(nickname)+1); + return 1; + } + +} + +int RemoveChatUser(char *nickname) +{ + Chat_user *prv_user = NULL; + + Curruser = Firstuser; + while(Curruser) + { + if(SDL_strcasecmp(nickname,Curruser->nick_name)==0) + { + if(prv_user) + { + prv_user->next = Curruser->next; + + } + else + { + Firstuser = Curruser->next; + } + AddChatCommandToQueue(CC_USER_LEAVING,Curruser->nick_name,strlen(Curruser->nick_name)+1); + free(Curruser); + return 1; + } + prv_user = Curruser; + Curruser = Curruser->next; + } + return 0; + +} + +void RemoveAllChatUsers(void) +{ + Chat_user *tmp_user = NULL; + Curruser = Firstuser; + while(Curruser) + { + tmp_user = Curruser->next; + AddChatCommandToQueue(CC_USER_LEAVING,Curruser->nick_name,strlen(Curruser->nick_name)+1); + free(Curruser); + Curruser = tmp_user; + } + Firstuser = NULL; +} + + +char * ParseIRCMessage(char *Line, int iMode) +{ + char szRemLine[MAXLOCALSTRING] =""; + const char *pszTempStr; + char szPrefix[MAXLOCALSTRING] = ""; + char szHackPrefix[MAXLOCALSTRING] = ""; + char szTarget[MAXLOCALSTRING] = ""; + char szNick[MAXLOCALSTRING] = ""; + char szCmd[MAXLOCALSTRING] = ""; + char szCTCPCmd[MAXLOCALSTRING] = ""; + + static char szResponse[MAXLOCALSTRING] = ""; + + int iNickLen; + int iPrefixLen = 0; // JAS: Get rid of optimized warning + + if(strlen(Line)>=MAXLOCALSTRING) + { + return NULL; + } + //Nick included.... + if(iMode==MSG_REMOTE) + { + strcpy(szRemLine,Line); + //Start by getting the prefix + if(Line[0]==':') + { + // + pszTempStr=GetWordNum(0,Line+1); + strcpy(szPrefix,pszTempStr); + strcpy(szHackPrefix,pszTempStr); + strcpy(szRemLine,Line+1+strlen(szPrefix)); + } + //Next, get the Nick + pszTempStr=strtok(szHackPrefix,"!"); + if(pszTempStr) + { + strcpy(szNick,pszTempStr); + } + else + { + strncpy(szNick,szPrefix,31); + szNick[31]=0; + } + //strcpy(NewMsg.Nickname,szNick); + iNickLen=strlen(szNick); + iPrefixLen=strlen(szPrefix); + } + else if(iMode==MSG_LOCAL) + { + strcpy(szRemLine,Line); + strcpy(szNick,Nick_name); + strcpy(szPrefix,Nick_name); + //strcpy(NewMsg.Nickname,szNick); + iNickLen=-2; + iPrefixLen=-2; + } + //Next is the command + pszTempStr=GetWordNum(0,szRemLine); + if(pszTempStr[0]) + { + strcpy(szCmd,pszTempStr); + } + else + { + //Shouldn't ever happen, but we can't be sure of what the host will send us. + return NULL; + } + + //Move the szRemLine string up + strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+2); + //Now parse the commands! + //printf("%s",szCmd); + if(SDL_strcasecmp(szCmd,NOX("PRIVMSG"))==0) + { + pszTempStr=GetWordNum(0,szRemLine); + strcpy(szTarget,pszTempStr); + strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4); + if(szRemLine[0]==':') + { + strcpy(szCTCPCmd,GetWordNum(0,szRemLine+1)); + if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00; + + } + else + { + strcpy(szCTCPCmd,GetWordNum(0,szRemLine)); + if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00; + } + if(szCTCPCmd[0]==0x01) + { + //Handle ctcp message + strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+strlen(szCTCPCmd)+6); + szRemLine[strlen(szRemLine)-1]='\0';//null out the ending 0x01 + if(SDL_strcasecmp(szCTCPCmd+1,NOX("ACTION"))==0) + { + //Posture + sprintf(szResponse,"* %s %s",szNick,szRemLine); + return szResponse; + } + if(iMode==MSG_LOCAL) + { + strcpy(szHackPrefix,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4); + szRemLine[strlen(szRemLine)-1]='\0'; + sprintf(szResponse,NOX("** CTCP %s %s %s"),szTarget,szCTCPCmd+1,szRemLine); + return szResponse; + } + if(SDL_strcasecmp(szCTCPCmd+1,NOX("PING"))==0) + { + sprintf(szResponse,NOX("/NOTICE %s :\001PING %s\001"),szNick,szRemLine);//Don't need the trailing \001 because szremline has it. + SendChatString(szResponse,1); + return NULL; + } + if(SDL_strcasecmp(szCTCPCmd+1,NOX("VERSION"))==0) + { + //reply with a notice version & copyright + //sprintf(szTempLine,"NOTICE %s :\001VERSION Copyright(c)\001\n",szNick); + + return NULL; + } + strcpy(szRemLine,1 + GetWordNum(0,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4)); + szRemLine[strlen(szRemLine)-1]='\0'; + sprintf(szResponse,NOX("** CTCP Message from %s (%s)"),szNick,szRemLine); + return szResponse; + + } + //differentiate between channel and private + if(szTarget[0]=='#') + { + pszTempStr=GetWordNum(0,szRemLine); + sprintf(szResponse,"[%s] %s",szNick,pszTempStr); + return szResponse; + } + else + { + if(iMode == MSG_LOCAL) + { + pszTempStr=GetWordNum(0,szRemLine); + sprintf(szResponse,NOX("Private Message to <%s>: %s"),szNick,pszTempStr); + } + else + { + pszTempStr=GetWordNum(0,szRemLine); + sprintf(szResponse,NOX("Private Message from <%s>: %s"),szNick,pszTempStr); + } + return szResponse; + } + + } + //don't handle any other messages locally. + if(iMode==MSG_LOCAL) + { + return NULL; + } + + if(SDL_strcasecmp(szCmd,NOX("NOTICE"))==0) + { + + + pszTempStr=GetWordNum(0,szRemLine); + strcpy(szTarget,pszTempStr); + strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4); + if(szRemLine[0]==':') + { + strcpy(szCTCPCmd,GetWordNum(0,szRemLine+1)); + if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00; + + } + else + { + strcpy(szCTCPCmd,GetWordNum(0,szRemLine)); + if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00; + } + if(szCTCPCmd[0]==0x01) + { + //Handle ctcp message + strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+strlen(szCTCPCmd)+6); + szRemLine[strlen(szRemLine)-1]='\0';//null out the ending 0x01 + if(SDL_strcasecmp(szCTCPCmd+1,NOX("PING"))==0) + { + //This is a ping response, figure out time and print + //sprintf(NewMsg.Message,"** Ping Response from %s: %ums",szNick,ulping); + return NULL; + } + + //Default message + strcpy(szRemLine,1 + GetWordNum(0,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4)); + szRemLine[strlen(szRemLine)-1]='\0'; + sprintf(szResponse,XSTR("** CTCP Message from %s (%s)",635),szNick,szRemLine); + return szResponse; + + } + sprintf(szResponse,"%s",szRemLine); + return NULL; + } + if(SDL_strcasecmp(szCmd,NOX("JOIN"))==0) + { + //see if it is me! + if(SDL_strcasecmp(Nick_name,szNick)==0) + { + //Yup, it's me! + //if(strcmpi(szChat_channel,GetWordNum(0,szRemLine))==0) + //{ + Joined_channel = 1; + if(SDL_strcasecmp(szChat_channel,NOX("#autoselect"))==0) + { + strcpy(szChat_channel,GetWordNum(0,szRemLine)); + AddChatCommandToQueue(CC_YOURCHANNEL,szChat_channel,strlen(szChat_channel)+1); + + } + //CC_YOURCHANNEL + //} + } + AddChatUser(szNick); + + + pszTempStr=GetWordNum(0,szRemLine); + strcpy(szTarget,pszTempStr); + //strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+3); + + //strcpy(NewMsg.Channel,szTarget); + + AddChatUser(szNick); + sprintf(szResponse,XSTR("** %s has joined %s",636),szNick,szTarget); + return NULL;//szResponse; + //Add them to the userlist too! + } + if(SDL_strcasecmp(szCmd,NOX("PART"))==0) + { + pszTempStr=GetWordNum(0,szRemLine); + strcpy(szTarget,pszTempStr); + strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+3); + //see if it is me! + if(SDL_strcasecmp(Nick_name,szNick)==0) + { + //Yup, it's me! + //szChat_channel[0]=NULL; + RemoveAllChatUsers(); + } + + RemoveChatUser(szNick); + return NULL; + //Remove them to the userlist too! + } + if(SDL_strcasecmp(szCmd,NOX("KICK"))==0) + { + pszTempStr=GetWordNum(0,szRemLine); + strcpy(szTarget,pszTempStr); + pszTempStr=GetWordNum(1,szRemLine); + strcpy(szHackPrefix,pszTempStr); + pszTempStr=GetWordNum(2,szRemLine); + //see if it is me! + if(SDL_strcasecmp(Nick_name,GetWordNum(1,szRemLine))==0) + { + //Yup, it's me! + szChat_channel[0]='\0'; + //bNewStatus=1; + AddChatCommandToQueue(CC_KICKED,NULL,0); + RemoveAllChatUsers(); + } + sprintf(szResponse,XSTR("*** %s has kicked %s from channel %s (%s)",637),szNick,szHackPrefix,szTarget,pszTempStr); + //Remove them to the userlist too! + RemoveChatUser(szNick); + return szResponse; + + } + if(SDL_strcasecmp(szCmd,NOX("NICK"))==0) + { + //see if it is me! + if(SDL_strcasecmp(Nick_name,szNick)==0) + { + //Yup, it's me! + strcpy(Nick_name,GetWordNum(0,szRemLine)); + } + char nicks[70]; + sprintf(nicks,"%s %s",szNick,GetWordNum(0,szRemLine)); + AddChatCommandToQueue(CC_NICKCHANGED,nicks,strlen(nicks)+1); + RemoveChatUser(szNick); + AddChatUser(GetWordNum(0,szRemLine)); + sprintf(szResponse,XSTR("*** %s is now known as %s",638),szNick,GetWordNum(0,szRemLine)); + return szResponse; + } + if(SDL_strcasecmp(szCmd,NOX("PING"))==0) + { + //respond with pong (GetWordNum(0,szRemLine)) + sprintf(szResponse,NOX("/PONG :%s"),GetWordNum(0,szRemLine)); + SendChatString(szResponse,1); + return NULL; + } + if(SDL_strcasecmp(szCmd,NOX("MODE"))==0) + { + //Channel Mode info + return NULL; + } + + + if(SDL_strcasecmp(szCmd,"401")==0) + { + //This is whois user info, we can get their tracker info from here. -5 + char szWhoisUser[33]; + strcpy(szWhoisUser,GetWordNum(1,szRemLine)); + Getting_user_tracker_error = 1; + Getting_user_channel_error = 1; + + sprintf(szResponse,XSTR("**Error: %s is not online!",639),szWhoisUser); + return szResponse; + + } + if(SDL_strcasecmp(szCmd,"311")==0) + { + char szWhoisUser[33]; + strcpy(szWhoisUser,GetWordNum(1,szRemLine)); + //This is whois user info, we can get their tracker info from here. -5 + //if(strcmpi(Getting_user_tracker_info_for,szWhoisUser)==0) + //{ + strcpy(User_req_tracker_id,GetWordNum(5,szRemLine)); + //} + return NULL; + } + if(SDL_strcasecmp(szCmd,"319")==0) + { + char szWhoisUser[33]; + strcpy(szWhoisUser,GetWordNum(1,szRemLine)); + //This is whois channel info -- what channel they are on -2 + //if(strcmpi(Getting_user_channel_info_for,szWhoisUser)==0) + //{ + strcpy(User_req_channel,GetWordNum(2,szRemLine)); + //} + return NULL; + } + + //End of whois and we didn't get a channel means they aren't in a channel. + if(SDL_strcasecmp(szCmd,"318")==0) + { + if(!*User_req_channel) + { + User_req_channel[0] = '*'; + } + } + + + if(SDL_strcasecmp(szCmd,"321")==0) + { + //start of channel list + FlushChannelList(); + GettingChannelList = 1; + return NULL; + } + if(SDL_strcasecmp(szCmd,"322")==0) + { + //channel list data + if(GettingChannelList == 1) + { + char channel_list_name[33]; + char sztopic[200]; + strcpy(sztopic,GetWordNum(3,szRemLine)); + strcpy(channel_list_name,GetWordNum(1,szRemLine)); + AddChannel(channel_list_name,(short)atoi(GetWordNum(2,szRemLine)),sztopic); + } + return NULL; + } + if(SDL_strcasecmp(szCmd,"323")==0) + { + //end of channel list + GettingChannelList = 2; + return NULL; + } + if(SDL_strcasecmp(szCmd,"324")==0) + { + //Channel Mode info + return NULL; + } + + if(SDL_strcasecmp(szCmd,"332")==0) + { + //Channel Topic, update status bar. + if(SDL_strcasecmp(szChat_channel,szTarget)==0) + { + //strncpy(szChanTopic,GetWordNum(2,szRemLine),70); + } + //sprintf(NewMsg.Message,"*** %s has changed the topic to: %s",szNick,GetWordNum(2,szRemLine)); + + return NULL; + } + if(SDL_strcasecmp(szCmd,NOX("TOPIC"))==0) + { + //Channel Topic, update status bar. + if(SDL_strcasecmp(szChat_channel,szTarget)==0) + { + //strncpy(szChanTopic,GetWordNum(1,szRemLine),70); + } + //sprintf(NewMsg.Message,"*** %s has changed the topic to: %s",szNick,GetWordNum(1,szRemLine)); + return NULL; + } + if(SDL_strcasecmp(szCmd,NOX("QUIT"))==0) + { + //Remove the user! + RemoveChatUser(szNick); + return NULL; + } + if(SDL_strcasecmp(szCmd,"376")==0) //end of motd, trigger autojoin... + { + if (!Chat_server_connected) + { + Chat_server_connected=1; + } + + // end of motd + strcpy(szResponse, PXO_CHAT_END_OF_MOTD_PREFIX); + return szResponse; + } + if((SDL_strcasecmp(szCmd,"377")==0)|| + (SDL_strcasecmp(szCmd,"372")==0)|| + (SDL_strcasecmp(szCmd,"372")==0) + + ) + { + //Stip the message, and display it. + pszTempStr=GetWordNum(3,Line); + strcpy(szResponse, PXO_CHAT_MOTD_PREFIX); + strcat(szResponse, pszTempStr); + return szResponse; + } + //Ignore these messages + if(((SDL_strcasecmp(szCmd,"366")==0))|| + (SDL_strcasecmp(szCmd,"333")==0) || //Who set the topic + (SDL_strcasecmp(szCmd,"329")==0)) //Time Channel created + /* + (SDL_strcasecmp(szCmd,"305")==0) || + (SDL_strcasecmp(szCmd,"306")==0) || + (SDL_strcasecmp(szCmd,"311")==0) || //WHOIS stuff + (SDL_strcasecmp(szCmd,"312")==0) || + (SDL_strcasecmp(szCmd,"313")==0) || + (SDL_strcasecmp(szCmd,"317")==0) || + (SDL_strcasecmp(szCmd,"318")==0) || + (SDL_strcasecmp(szCmd,"319")==0) || + */ + + { + return NULL; + } + if(SDL_strcasecmp(szCmd,"353")==0) + { + + //Names in the channel. + pszTempStr = GetWordNum(3,Line+iPrefixLen+strlen(szCmd)+2); + strcpy(szRemLine,pszTempStr); + pszTempStr = strtok(szRemLine," "); + + while(pszTempStr) + { + if(pszTempStr[0]=='@') + { + AddChatUser(pszTempStr+1); + } + else if(pszTempStr[0]=='+') + { + AddChatUser(pszTempStr+1); + } + else + { + AddChatUser(pszTempStr); + } + pszTempStr=strtok(NULL," "); + } + return NULL; + } + //MOTD Codes + if((SDL_strcasecmp(szCmd,"001")==0)|| + (SDL_strcasecmp(szCmd,"002")==0)|| + (SDL_strcasecmp(szCmd,"003")==0)|| + (SDL_strcasecmp(szCmd,"004")==0)|| + (SDL_strcasecmp(szCmd,"251")==0)|| + (SDL_strcasecmp(szCmd,"254")==0)|| + (SDL_strcasecmp(szCmd,"255")==0)|| + (SDL_strcasecmp(szCmd,"265")==0)|| + (SDL_strcasecmp(szCmd,"375")==0)|| + (SDL_strcasecmp(szCmd,"372")==0)|| + (SDL_strcasecmp(szCmd,"375")==0) + ) + { + // Stip the message, and display it. + // pszTempStr = GetWordNum(3, Line); + // strcpy(szResponse, PXO_CHAT_MOTD_PREFIX); + // strcat(szResponse, pszTempStr); + return NULL; + // return szResponse; + } + if(SDL_strcasecmp(szCmd,"432")==0) + { + //Channel Mode info + strcpy(szResponse,XSTR("Your nickname contains invalid characters",640)); + AddChatCommandToQueue(CC_DISCONNECTED,NULL,0); + return szResponse; + } + if(SDL_strcasecmp(szCmd,"433")==0) + { + //Channel Mode info + char new_nick[33]; + sprintf(new_nick,"%s%d",Orignial_nick_name,Nick_variety); + strcpy(Nick_name,new_nick); + Nick_variety++; + sprintf(szResponse,NOX("/NICK %s"),new_nick); + SendChatString(szResponse,1); + return NULL; + } + //Default print + strcpy(szResponse,Line); + //return szResponse; + return NULL; + +} + + +void AddChatCommandToQueue(int command,const void *data,int len) +{ + Currcommand = Firstcommand; + if(Firstcommand==NULL) + { + Firstcommand = (Chat_command *)malloc(sizeof(Chat_command)); + SDL_assert(Firstcommand); + Firstcommand->next = NULL; + Currcommand = Firstcommand; + } + else + { + while(Currcommand->next) + { + Currcommand = Currcommand->next; + } + Currcommand->next = (Chat_command *)malloc(sizeof(Chat_command)); + SDL_assert(Currcommand->next); + Currcommand = Currcommand->next; + } + Currcommand->command = (short)command; + if(len&&data) memcpy(&Currcommand->data,data,len); + Currcommand->next = NULL; + return; +} + +Chat_command *GetChatCommandFromQueue(void) +{ + static Chat_command response_cmd; + Chat_command *tmp_cmd; + if(!Firstcommand) return NULL; + Currcommand = Firstcommand; + memcpy(&response_cmd,Currcommand,sizeof(Chat_command)); + tmp_cmd = Currcommand->next; + free(Firstcommand); + Firstcommand = tmp_cmd; + return &response_cmd; +} + +void FlushChatCommandQueue(void) +{ + Chat_command *tmp_cmd; + Currcommand = Firstcommand; + + while(Currcommand) + { + tmp_cmd = Currcommand->next; + free(Currcommand); + Currcommand = tmp_cmd; + } + Firstcommand = NULL; +} + + +void FlushChannelList(void) +{ + Chat_channel *tmp_chan; + Currchannel = Firstchannel; + + while(Currchannel) + { + tmp_chan = Currchannel->next; + free(Currchannel); + Currchannel = tmp_chan; + } + Firstchannel = NULL; + + +} +char *GetChannelList(void) +{ + int ichan_list_length = 0; + char sznumusers[10]; + + if(GettingChannelList != 2) return NULL; + if(!Socket_connected) return NULL; + + if(Chan_list) + { + free(Chan_list); + Chan_list = NULL; + } + + + Currchannel = Firstchannel; + while(Currchannel) + { + ichan_list_length += strlen(Currchannel->topic)+1+strlen(Currchannel->channel_name)+1+5;//1 for the space, and 4 for the number of users 0000-9999 + space + Currchannel = Currchannel->next; + } + Currchannel = Firstchannel; + Chan_list = (char *)malloc(ichan_list_length+1); + Chan_list[0] = '\0'; + while(Currchannel) + { + strcat(Chan_list,"$"); + strcat(Chan_list,Currchannel->channel_name); + strcat(Chan_list," "); + sprintf(sznumusers,"%d ",Currchannel->users); + strcat(Chan_list,sznumusers); + strcat(Chan_list,Currchannel->topic);//fgets + strcat(Chan_list," "); + Currchannel = Currchannel->next; + } + FlushChannelList(); + GettingChannelList = 0; + return Chan_list; +} + +void AddChannel(char *channel,unsigned short numusers,char *topic) +{ + Currchannel = Firstchannel; + if(Firstchannel==NULL) + { + Firstchannel = (Chat_channel *)malloc(sizeof(Chat_channel)); + SDL_assert(Firstchannel); + strcpy(Firstchannel->channel_name,channel); + strcpy(Firstchannel->topic,topic); + Firstchannel->users = numusers; + Firstchannel->next = NULL; + Currchannel = Firstchannel; + } + else + { + while(Currchannel->next) + { + Currchannel = Currchannel->next; + } + Currchannel->next = (Chat_channel *)malloc(sizeof(Chat_channel)); + SDL_assert(Currchannel->next); + Currchannel = Currchannel->next; + strcpy(Currchannel->channel_name,channel); + strcpy(Currchannel->topic,topic); + Currchannel->users = numusers; + } + Currchannel->next = NULL; + return; +} + + +char *GetTrackerIdByUser(char *nickname) +{ + char szWhoisCmd[100]; + + + if(GettingUserTID) + { + if(Getting_user_tracker_error) + { + Getting_user_tracker_error = 0; + GettingUserTID = 0; + return (char *)-1; + } + + if(*User_req_tracker_id) + { + GettingUserTID = 0; + return User_req_tracker_id; + } + } + else + { + strcpy(Getting_user_tracker_info_for,nickname); + sprintf(szWhoisCmd,NOX("/WHOIS %s"),nickname); + User_req_tracker_id[0] = '\0'; + SendChatString(szWhoisCmd,1); + GettingUserTID = 1; + } + return NULL; +} + +char *GetChannelByUser(char *nickname) +{ + char szWhoisCmd[100]; + + if(GettingUserChannel) + { + if(Getting_user_channel_error) + { + Getting_user_channel_error = 0; + GettingUserChannel = 0; + return (char *)-1; + } + if(*User_req_channel) + { + GettingUserChannel = 0; + return User_req_channel; + } + } + else + { + strcpy(Getting_user_channel_info_for,nickname); + User_req_channel[0] = '\0'; + sprintf(szWhoisCmd,NOX("/WHOIS %s"),nickname); + SendChatString(szWhoisCmd,1); + GettingUserChannel = 1; + } + return NULL; +} + diff --git a/src/network/gtrack.cpp b/src/network/gtrack.cpp new file mode 100644 index 0000000..6623958 --- /dev/null +++ b/src/network/gtrack.cpp @@ -0,0 +1,454 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + + +//Game Tracker client code +/* + + InitGameTracker(int game_type); //D3 or Freespace + + //Call periodically so we can send our update, and make sure it is received + IdleGameTracker(); + + StartTrackerGame(void *buffer) //Call with a freespace_net_game_data or d3_net_game_data structure + //Call with a freespace_net_game_data or d3_net_game_data structure + //Updates our memory so when it is time, we send the latest game data + UpdateGameData(gamedata); + + + //Call to signify end of game + + RequestGameList();//Sends a GNT_GAMELIST_REQ packet to the server. + + game_list * GetGameList();//returns a pointer to a game_list struct + +*/ + + +#ifdef PLAT_UNIX +#include +#include +#include +#include +#endif + +#include "pstypes.h" +#include "timer.h" +#include "multi.h" +#include "multi_pxo.h" +#include "gtrack.h" + + +//Variables +// SOCKET gamesock; +SOCKADDR_IN gtrackaddr; + +game_list GameBuffer[MAX_GAME_BUFFERS]; +int GameType;//d3 or fs + +unsigned int LastTrackerUpdate; +unsigned int LastSentToTracker; +unsigned int TrackerAckdUs; +unsigned int TrackerGameIsRunning; + +game_packet_header TrackerGameData; +game_packet_header GameListReq; +game_packet_header TrackAckPacket; +game_packet_header GameOverPacket; + +#ifdef MAKE_FS1 +freespace_net_game_data *FreeSpaceTrackerGameData; +#else +freespace2_net_game_data *FreeSpace2TrackerGameData; +#endif + +//Start New 7-9-98 +unsigned int LastGameOverPacket; +unsigned int FirstGameOverPacket; + +int SendingGameOver; +//End New 7-9-98 + + +int InitGameTrackerClient(int gametype) +{ + SOCKADDR_IN sockaddr; + unsigned int iaddr; + + GameType = gametype; + LastTrackerUpdate = 0; + switch(gametype) + { + case GT_FREESPACE: +#ifndef MAKE_FS1 + Int3(); + return 0; +#else + TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace_net_game_data); +#endif + break; + + case GT_FREESPACE2: +#ifdef MAKE_FS1 + Int3(); + return 0; +#else + TrackerGameData.len = GAME_HEADER_ONLY_SIZE+sizeof(freespace2_net_game_data); +#endif + break; + + default: + Int3(); + return 0; + } + TrackerGameData.game_type = (unsigned char)gametype; //1==freespace (GT_FREESPACE), 2==D3, 3==tuberacer, etc. + TrackerGameData.type = GNT_GAMEUPDATE; //Used to specify what to do ie. Add a new net game (GNT_GAMESTARTED), remove a net game (game over), etc. + +#ifdef MAKE_FS1 + FreeSpaceTrackerGameData = (freespace_net_game_data *)&TrackerGameData.data; +#else + FreeSpace2TrackerGameData = (freespace2_net_game_data *)&TrackerGameData.data; +#endif + + GameListReq.game_type = (unsigned char)gametype; + GameListReq.type = GNT_GAMELIST_REQ; + GameListReq.len = GAME_HEADER_ONLY_SIZE; + + TrackAckPacket.game_type = (unsigned char)gametype; + TrackAckPacket.len = GAME_HEADER_ONLY_SIZE; + TrackAckPacket.type = GNT_CLIENT_ACK; + + GameOverPacket.game_type = (unsigned char)gametype; + GameOverPacket.len = GAME_HEADER_ONLY_SIZE; + GameOverPacket.type = GNT_GAMEOVER; + + // gamesock = socket(AF_INET,SOCK_DGRAM,0); + + /* + if ( gamesock == INVALID_SOCKET ) + { + printf("Unable to open a socket.\n"); + return 0; + } + */ + + memset( &sockaddr, 0, sizeof(SOCKADDR_IN) ); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; + sockaddr.sin_port = 0;//htons(GAMEPORT); + + /* + if (SOCKET_ERROR==bind(gamesock, (SOCKADDR*)&sockaddr, sizeof (sockaddr))) + { + printf("Unable to bind a socket.\n"); + printf("WSAGetLastError() returned %d.\n",WSAGetLastError()); + return 0; + } + */ + + iaddr = inet_addr ( Multi_options_g.game_tracker_ip ); + if ( iaddr == INADDR_NONE ) { + // first try and resolve by name + HOSTENT *he; + he = gethostbyname( Multi_options_g.game_tracker_ip ); + if(!he) + { + return 0; + /* + // try and resolve by address + unsigned int n_order = inet_addr(Multi_game_tracker_ip_address); + he = gethostbyaddr((char*)&n_order,4,PF_INET); + + if(!he){ + return 0; + } + */ + } + memcpy(&iaddr, he->h_addr_list[0],4); + } + + // This would be a good place to resolve the IP based on a domain name + memcpy(>rackaddr.sin_addr.s_addr, &iaddr, 4); + gtrackaddr.sin_family = AF_INET; + gtrackaddr.sin_port = htons( GAMEPORT ); + + //Start New 7-9-98 + SendingGameOver = 0; + //End New 7-9-98 + + return 1; +} + +void IdleGameTracker() +{ + fd_set read_fds; + TIMEVAL timeout; + + PSNET_TOP_LAYER_PROCESS(); + + timeout.tv_sec=0; + timeout.tv_usec=0; + if((TrackerGameIsRunning) && ((timer_get_seconds()-LastTrackerUpdate)>TRACKER_UPDATE_INTERVAL) && !SendingGameOver) + { + //Time to update the tracker again + SENDTO(Unreliable_socket, (char *)&TrackerGameData,TrackerGameData.len,0,(SOCKADDR *)>rackaddr,sizeof(SOCKADDR_IN), PSNET_TYPE_GAME_TRACKER); + TrackerAckdUs = 0; + LastTrackerUpdate = timer_get_seconds(); + } + else if((TrackerGameIsRunning)&&(!TrackerAckdUs)&&((timer_get_milliseconds()-LastSentToTracker)>TRACKER_RESEND_TIME)) + { + //We still haven't been acked by the last packet and it's time to resend. + SENDTO(Unreliable_socket, (char *)&TrackerGameData,TrackerGameData.len,0,(SOCKADDR *)>rackaddr,sizeof(SOCKADDR_IN), PSNET_TYPE_GAME_TRACKER); + TrackerAckdUs = 0; + LastTrackerUpdate = timer_get_seconds(); + LastSentToTracker = timer_get_milliseconds(); + } + + //Start New 7-9-98 + if(SendingGameOver){ + if((timer_get_milliseconds()-LastGameOverPacket)>TRACKER_RESEND_TIME){ + //resend + LastGameOverPacket = timer_get_milliseconds(); + SENDTO(Unreliable_socket, (char *)&GameOverPacket,GameOverPacket.len,0,(SOCKADDR *)>rackaddr,sizeof(SOCKADDR_IN), PSNET_TYPE_GAME_TRACKER); + } + /* + else if((timer_get_milliseconds()-FirstGameOverPacket)>NET_ACK_TIMEOUT) { + //Giving up, it timed out. + SendingGameOver = 2; + } + */ + } + //End New 7-9-98 + + //Check for incoming + + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + +#ifndef PLAT_UNIX + if(SELECT(0,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_GAME_TRACKER)) +#else + if(SELECT(Unreliable_socket+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_GAME_TRACKER)) +#endif + { + unsigned int bytesin; + int addrsize; + SOCKADDR_IN fromaddr; + + game_packet_header inpacket; + addrsize = sizeof(SOCKADDR_IN); + + bytesin = RECVFROM(Unreliable_socket, (char *)&inpacket,sizeof(game_packet_header),0,(SOCKADDR *)&fromaddr,&addrsize, PSNET_TYPE_GAME_TRACKER); + if((int)bytesin==-1) + { + int wserr=WSAGetLastError(); + printf("RECVFROM() failure. WSAGetLastError() returned %d\n",wserr); + + } + + // subtract one from the header + inpacket.len--; + + //Check to make sure the packets ok + if(bytesin==inpacket.len) + { + switch(inpacket.type) + { + case GNT_SERVER_ACK: + //The server got our packet so we can stop sending now + TrackerAckdUs = 1; + + // 7/13/98 -- because of the FreeSpace iterative frame process -- set this value to 0, instead + // of to 2 (as it originally was) since we call SendGameOver() only once. Once we get the ack + // from the server, we can assume that we are done. + // need to mark this as 0 + SendingGameOver = 0; + break; + case GNT_GAMELIST_DATA: + int i; + //Woohoo! Game data! put it in the buffer (if one's free) + for(i=0;ichannel, sizeof(filter_game_list_struct) - 4); + SENDTO(Unreliable_socket, (char *)&GameCountReq, GameCountReq.len, 0, (SOCKADDR *)>rackaddr, sizeof(SOCKADDR_IN), PSNET_TYPE_GAME_TRACKER); +} diff --git a/src/network/multi_fstracker.cpp b/src/network/multi_fstracker.cpp new file mode 100644 index 0000000..9a33d55 --- /dev/null +++ b/src/network/multi_fstracker.cpp @@ -0,0 +1,1618 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +/* + * $Logfile: /Freespace2/code/Network/multi_fstracker.cpp $ + * $Revision: 19 $ + * $Date: 9/13/99 11:30a $ + * $Author: Dave $ + * + * $Log: /Freespace2/code/Network/multi_fstracker.cpp $ + * + * 19 9/13/99 11:30a Dave + * Added checkboxes and functionality for disabling PXO banners as well as + * disabling d3d zbuffer biasing. + * + * 18 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 17 8/25/99 4:38p Dave + * Updated PXO stuff. Make squad war report stuff much more nicely. + * + * 16 8/19/99 10:59a Dave + * Packet loss detection. + * + * 15 6/07/99 11:30p Dave + * Whoops. + * + * 14 4/30/99 12:18p Dave + * Several minor bug fixes. + * + * 13 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 12 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 11 2/24/99 2:25p Dave + * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn + * bug for dogfight more. + * + * 10 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 9 2/12/99 6:16p Dave + * Pre-mission Squad War code is 95% done. + * + * 8 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 7 2/08/99 5:07p Dave + * FS2 chat server support. FS2 specific validated missions. + * + * 6 2/04/99 6:29p Dave + * First full working rev of FS2 PXO support. Fixed Glide lighting + * problems. + * + * 5 2/03/99 6:06p Dave + * Groundwork for FS2 PXO usertracker support. Gametracker support next. + * + * 4 12/03/98 5:22p Dave + * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl + * checksumming. + * + * 3 10/19/98 11:15a Dave + * Changed requirements for stats storing in PXO mode. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 46 9/18/98 2:22a Dave + * Fixed freespace-side PXO api to correctly handle length 10 id strings. + * Fixed team select screen to handle alpha/beta/gamma ships which are not + * marked as OF_PLAYER_SHIP + * + * 45 9/16/98 6:54p Dave + * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort + * the ship list box. Added code so that tracker stats are not stored with + * only 1 player. + * + * 44 9/15/98 7:24p Dave + * Minor UI changes. Localized bunch of new text. + * + * 43 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 42 9/10/98 1:17p Dave + * Put in code to flag missions and campaigns as being MD or not in Fred + * and Freespace. Put in multiplayer support for filtering out MD + * missions. Put in multiplayer popups for warning of non-valid missions. + * + * 41 9/09/98 5:53p Dave + * Put in new tracker packets in API. Change cfile to be able to checksum + * portions of a file. + * + * 40 9/04/98 3:51p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 39 9/01/98 6:48p Dave + * Energy suck weapon. Removed a couple of now-bogus asserts in tracker + * code. + * + * 38 8/21/98 1:14p Dave + * Put in log system hooks in useful places. + * + * 37 8/07/98 10:39a Allender + * fixed debug standalone problem where stats would continually get sent + * to tracker. more debug code to help find stats problem + * + * 36 7/24/98 11:14a Allender + * preparation for validated missions + * + * 35 6/17/98 10:56a Dave + * Put in debug code for detecting potential tracker stats update + * problems. + * + * 34 6/13/98 9:32p Mike + * Kill last character in file which caused "Find in Files" to report the + * file as "not a text file." + * + * 33 6/13/98 6:01p Hoffoss + * Externalized all new (or forgot to be added) strings to all the code. + * + * 32 5/24/98 11:33a Dave + * Simplified the stats store process somewhat. Put in checks to find + * invalid situations. + * + * 31 5/24/98 10:36a Dave + * Put in more checks/verifications for tracker stats updating. + * + * 30 5/22/98 9:35p Dave + * Put in channel based support for PXO. Put in "shutdown" button for + * standalone. UI tweaks for TvT + * + * 29 5/21/98 9:45p Dave + * Lengthened tracker polling times. Put in initial support for PXO + * servers with channel filters. Fixed several small UI bugs. + * + * 28 5/21/98 1:52a Dave + * Remove obsolete command line functions. Reduce shield explosion packets + * drastically. Tweak PXO screen even more. Fix file xfer system so that + * we can guarantee file uniqueness. + * + * 27 5/20/98 2:24a Dave + * Fixed server side voice muting. Tweaked multi debrief/endgame + * sequencing a bit. Much friendlier for stats tossing/accepting now. + * + * 26 5/18/98 9:15p Dave + * Put in network config file support. + * + * 25 5/18/98 10:39a Dave + * Put in support for new tracker stats. + * + * 24 5/14/98 12:40a Dave + * Still more additions to the PXO screen. Updated tracker code. + * + * 23 5/13/98 6:54p Dave + * More sophistication to PXO interface. Changed respawn checking so + * there's no window for desynchronization between the server and the + * clients. + * + * 22 5/08/98 7:08p Dave + * Lots of UI tweaking. + * + * 21 5/07/98 6:26p Dave + * Fix strange boundary conditions which arise when players die/respawn + * while the game is being ended. Spiff up the chatbox doskey thing a bit. + * + * 20 5/05/98 3:12p Chad + * Process _all_ server entries in a tracker game_list struct. + * + * 19 5/05/98 2:10p Dave + * Verify campaign support for testing. More new tracker code. + * + * 18 5/04/98 10:39p Dave + * Put in endgame sequencing. Need to check campaign situations. + * Realigned ship info on team select screen. + * + * 17 5/04/98 1:43p Dave + * Fixed up a standalone resetting problem. Fixed multiplayer stats + * collection for clients. Make sure all multiplayer ui screens have the + * correct palette at all times. + * + * 16 5/02/98 5:38p Dave + * Put in new tracker API code. Put in ship information on mp team select + * screen. Make standalone server name permanent. Fixed standalone server + * text messages. + * + * 15 4/30/98 12:57a Dave + * Put in new mode for ship/weapon selection. Rearranged how game querying + * is done a bit. + * + * 14 4/29/98 12:11a Dave + * Put in first rev of full API support for new master tracker. + * + * 13 4/28/98 7:50p Dave + * Fixing a broken makefile. + * + * 12 4/28/98 5:10p Dave + * Fixed multi_quit_game() client side sequencing problem. Turn off + * afterburners when ending multiplayer mission. Begin integration of mt + * API from Kevin Bentley. + * + * 11 4/04/98 4:22p Dave + * First rev of UDP reliable sockets is done. Seems to work well if not + * overly burdened. + * + * 10 3/15/98 4:17p Dave + * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size + * of network orientation matrices. + * + * 9 2/10/98 8:39p Dave + * Fixed bugs. Made endgame sequencing more clear. + * + * 8 2/07/98 1:51p Dave + * Centralized single and multiplayer stats tallying. + * + * 7 2/05/98 7:13p Dave + * Added some more security to MT communications. + * + * 6 2/04/98 6:35p Dave + * Changed psnet to use raw data with no headers. Started putting in + * support for master tracker security measures. + * + * 5 2/03/98 8:18p Dave + * More MT stats transfer stuff. + * + * 4 2/02/98 8:44p Dave + * Finished redoing master tracker stats transfer. + * + * 3 1/31/98 4:32p Dave + * Put in new support for VMT player validation, game logging in, and game + * logging out. Need to finish stats transfer. + * + * 2 1/30/98 5:53p Dave + * Revamped master tracker API + * + * 1 1/30/98 5:50p Dave + * + * $NoKeywords: $ + */ + + +#ifdef PLAT_UNIX +#include +#endif + +#include "freespace.h" +#include "timer.h" +#include "gamesequence.h" +#include "popup.h" +#include "psnet.h" +#include "valid.h" // tracker API +#include "gtrack.h" // tracker API +#include "ptrack.h" // tracker API +#include "multi.h" +#include "multi_fstracker.h" +#include "multiutil.h" +#include "multiui.h" +#include "multimsgs.h" +#include "multi_log.h" +#include "stand_gui.h" +#include "multi_pmsg.h" + +// ----------------------------------------------------------------------------------- +// FREESPACE MASTER TRACKER DEFINES/VARS +// + +// if the fs tracker module has been successfully initialized +int Multi_fs_tracker_inited = 0; + +// if we're currently performing some operation with the tracker +int Multi_fs_tracker_busy = 0; + +// channel to associate when creating a server +char Multi_fs_tracker_channel[255] = ""; + +// channel to use when polling the tracker for games +char Multi_fs_tracker_filter[255] = ""; + + +// ----------------------------------------------------------------------------------- +// FREESPACE MASTER TRACKER FORWARD DECLARATIONS +// + +// used with popup_till_condition() for validating freespace pilots +#define MT_VALIDATE_NOT_DONE 0 // still in the process of validation +#define MT_VALIDATE_SUCCEED 1 // successfully validated the pilot +#define MT_VALIDATE_FAIL 2 // failed in validating the pilot +#define MT_VALIDATE_TIMEOUT 3 // timedout on contacting the tracker +#define MT_VALIDATE_CANCEL 4 // if the action was cancelled +#define MT_PILOT_VAL_TIMEOUT 5000 // timeout for validating a pilot +int Multi_validate_mode; // 0 == getting player id, 1 == getting player stats +int multi_fs_validate_process(); + +// used with popup_till_condition() for logging in freespace games +#define MT_LOGIN_NOT_DONE 0 // still in the process of logging in +#define MT_LOGIN_SUCCEED 1 // successfully logged the game in +#define MT_LOGIN_TIMEOUT 2 // timedout on contacting the tracker + +// used with popup_till_condition() for storing player stats at the end of a freespace game +#define MT_STATS_NOT_DONE 0 // still in the process of storing stats +#define MT_STATS_SUCCEED 1 // successfully logged all player stats + +// used with popup_till_condition() for validating missions +#define MT_MVALID_NOT_DONE 0 // still in the process of validating +#define MT_MVALID_VALID 1 // mission is valid +#define MT_MVALID_INVALID 2 // mission is invalid +#define MT_MVALID_ERROR 3 // error while performing operation. assume invalid + +// store stats mode defined +#define MT_STORE_STATS_VALIDATE 0 +#define MT_STORE_STATS_GET_STATS 1 +#define MT_STORE_STATS_ACCEPT 2 +#define MT_STORE_STATS_SEND_STATS 3 + +int Multi_store_stats_mode; // 0 == initial request for player stats, 1 == waiting for player stats, 2 == tallying stats locally, 3 == sending stats to tracker +int Multi_store_stats_player_index; // player we're currently working with +int Multi_store_stats_player_flag; // if we're finished with the current guy +vmt_freespace2_struct Multi_store_stats_stats; // +int multi_fs_store_stats_do(); // manage all master tracker stats storing +int multi_fs_store_stats_get_next_player(int cur_player); + +int Multi_tracker_player_is_valid = 0; +int Multi_tracker_got_response = 0; + +// copy a freespace stats struct to a tracker-freespace stats struct +void multi_stats_fs_to_tracker(scoring_struct *fs,vmt_freespace2_struct *vmt,player *pl,int tracker_id); + +// copy a tracker-freespace stats struct to a freespace stats struct +void multi_stats_tracker_to_fs(vmt_freespace2_struct *vmt,scoring_struct *fs); + +// process an incoming active game item +void multi_fs_tracker_process_game_item(game_list *gl); + +// verify that there are no duplicate tracker id's to this one +void multi_fs_tracker_check_dup(int tracker_id,int player_index); + +// verify that there are no duplicate pilot callsigns +void multi_fs_tracker_check_dup_callsign(net_player *player,int player_index); + +// report on the results of the stats store procedure +void multi_fs_tracker_report_stats_results(); + +// tracker specific data structures +freespace2_net_game_data Multi_tracker_game_data; +vmt_freespace2_struct Multi_tracker_fs_pilot; +squad_war_response Multi_tracker_sw_response; + +// ----------------------------------------------------------------------------------- +// FREESPACE MASTER TRACKER DEFINITIONS +// + +// give some processor time to the tracker API +void multi_fs_tracker_process() +{ + game_list *gl; + + PSNET_TOP_LAYER_PROCESS(); + + if(Multi_fs_tracker_inited){ + // pilot validation system + ValidIdle(); + + // pilot tracing system + PollPTrackNet(); + + // game tracking system + IdleGameTracker(); + + // set if we've got any pending game list items + gl = GetGameList(); + if(gl != NULL){ + multi_fs_tracker_process_game_item(gl); + gl = NULL; + } + } +} + +// initialize the master tracker API for Freespace +void multi_fs_tracker_init() +{ + // don't do anything if we're already initialized + if(Multi_fs_tracker_inited){ + return; + } + + // initialize the low-level validation stuff + if(!InitValidateClient()){ + ml_printf("Error initializing tracker api (validateclient)\n"); + return; + } + + // initialize the low-level pilot tracking stuff + if(!InitPilotTrackerClient()){ + ml_printf("Error initializing tracker api (pilotclient)\n"); + return; + } + + // intialize the low-level game tracking stuff + if(!InitGameTrackerClient(GT_FREESPACE2)){ + ml_printf("Error initializing tracker api (gameclient)\n"); + return; + } + + nprintf(("Network","Successfully initialized tracker api\n")); + + // we've successfully initialized the tracker stuff + Multi_fs_tracker_inited = 1; +} + +// validate the current player with the master tracker (will create the pilot on the MT if necessary) +int multi_fs_tracker_validate(int show_error) +{ + validate_id_request vir; + + if(!Multi_fs_tracker_inited){ + popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666)); + return 0; + } + + // set this to false for now + Multi_tracker_player_is_valid = 0; + Multi_tracker_got_response = 0; + + // mark the module as busy + Multi_fs_tracker_busy = 1; + + while(1){ + // validate our pilot on the master tracker if possible + memset(&vir,0,sizeof(vir)); + memset(Multi_tracker_id_string,0,255); + strcpy(vir.login,Multi_tracker_login); + strcpy(vir.password,Multi_tracker_passwd); + ValidateUser(&vir,Multi_tracker_id_string); + + // set validation mode + Multi_validate_mode = 0; + + int rval = popup_till_condition(multi_fs_validate_process,XSTR("&Cancel",667),XSTR("Attempting to validate pilot ...",668)); + switch(rval){ + // if we failed for one reason or another + case MT_VALIDATE_FAIL : + // if we're supposed to show error codes + if(show_error){ + popup(PF_USE_AFFIRMATIVE_ICON | PF_BODY_BIG,1,XSTR("&Ok",669),XSTR("Pilot rejected by Parallax Online!",670)); + } + + Multi_validate_mode = -1; + + Multi_fs_tracker_busy = 0; + return 0; + + case MT_VALIDATE_SUCCEED : + // notify the user + if(Multi_tracker_fs_pilot.virgin_pilot){ + multi_common_add_notify(XSTR("Successfully created and validated new pilot!",671)); + } else { + multi_common_add_notify(XSTR("Parallax Online pilot validation succeeded!",672)); + } + + // copy my statistics into my pilot file + multi_stats_tracker_to_fs(&Multi_tracker_fs_pilot,&Player->stats); + + Multi_validate_mode = -1; + + Multi_fs_tracker_busy = 0; + return 1; + + case MT_VALIDATE_TIMEOUT : + rval = popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_BODY_BIG,2,XSTR("&Abort",673),XSTR("&Retry",674),XSTR("Validation timed out",675)); + + // if the user clicked abort, then leave. otherwise try again + if(rval == 0){ + Multi_validate_mode = -1; + + Multi_fs_tracker_busy = 0; + return 0; + } + break; + + default : + Multi_validate_mode = -1; + + Multi_fs_tracker_busy = 0; + + // essentially, cancel + return -1; + } + } +} + +// attempt to log the current game server in with the master tracker +void multi_fs_tracker_login_freespace() +{ + if(!Multi_fs_tracker_inited){ + popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Warning, Parallax Online startup failed. Will not be able to play tracker games!",666)); + return; + } + + // if we're already logged into a game, don't do anything + SDL_assert((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Net_player->flags & NETINFO_FLAG_MT_CONNECTED)); + + // pretty much all we do is make 1 call + memset(&Multi_tracker_game_data, 0, sizeof(freespace2_net_game_data)); + strcpy(Multi_tracker_game_data.game_name, Netgame.name); + Multi_tracker_game_data.difficulty = 99; + Multi_tracker_game_data.type = 0; + Multi_tracker_game_data.state = 1; + Multi_tracker_game_data.max_players = 12; + Multi_tracker_game_data.current_num_players = 0; + + // if we have a valid channel string, use it + if(strlen(Multi_fs_tracker_channel)){ + strcpy(Multi_tracker_game_data.channel,Multi_fs_tracker_channel); + } + + StartTrackerGame(&Multi_tracker_game_data); + Net_player->flags |= NETINFO_FLAG_MT_CONNECTED; + + // NETLOG + ml_string(NOX("Server connected to Game Tracker")); +} + +// attempt to update all player statistics and scores on the tracker +int multi_fs_tracker_store_stats() +{ + int idx; + + // NETLOG + ml_string(NOX("Server storing stats on User Tracker")); + + // retrieve stats from tracker + Multi_store_stats_mode = MT_STORE_STATS_VALIDATE; + + // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker + Multi_store_stats_player_index = -1; + Multi_store_stats_player_flag = 1; + + // mark the module as busy + Multi_fs_tracker_busy = 1; + + // unmark everyone's GET_FAILED flag + for(idx=0;idx", 1044), MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(XSTR("", 1044), 0, 0); + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked ships.tbl, your stats will not be saved", 1045) ); + multi_fs_tracker_report_stats_results(); + Multi_fs_tracker_busy = 0; + return 0; + } + + // if playing with an invalid weapons.tbl + if(!Game_weapons_tbl_valid){ + send_game_chat_packet(Net_player, XSTR("", 1046), MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(XSTR("", 1046), 0, 0); + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You are playing with a hacked weapons.tbl, your stats will not be saved", 1047) ); + multi_fs_tracker_report_stats_results(); + Multi_fs_tracker_busy = 0; + return 0; + } + + // if there is only 1 player, don't store the stats + if((multi_num_players() <= 1) && (Multi_num_players_at_start <= 1)){ + send_game_chat_packet(Net_player, XSTR("", 1048), MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(XSTR("", 1048), 0, 0); + multi_fs_tracker_report_stats_results(); + Multi_fs_tracker_busy = 0; + return 0; + } + + // if any players have hacked info + for(idx=0; idx", 1049), MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(XSTR("", 1049), 0, 0); + popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK, XSTR("This is not a PXO validated mission, your stats will not be saved", 1050)); + Multi_fs_tracker_busy = 0; + return 0; + } +#endif + + popup_till_condition(multi_fs_store_stats_do,XSTR("&Cancel",667), XSTR("Sending player stats requests ...",676)); + + // send appropriate chat messages indicating stats store failure + multi_fs_tracker_report_stats_results(); + + // mark the module as not busy anymore + Multi_fs_tracker_busy = 0; + + return 1; +} + +// attempt to update all player statistics (standalone mode) +int multi_fs_std_tracker_store_stats() +{ + int ret_val; + int idx; + + // don't do anything if this is a tracker game + if(!(MULTI_IS_TRACKER_GAME)){ + return 0; + } + + if(!Multi_fs_tracker_inited){ + return 0; + } + + // NETLOG + ml_string(NOX("Standalone server storing stats on User Tracker")); + + // retrieve stats from tracker + Multi_store_stats_mode = MT_STORE_STATS_VALIDATE; + + // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker + Multi_store_stats_player_index = -1; + Multi_store_stats_player_flag = 1; + + // mark the module as busy + Multi_fs_tracker_busy = 1; + + // unmark everyone's GET_FAILED flag + for(idx=0;idx", 1044), MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(XSTR("", 1044), 0, 0); + multi_fs_tracker_report_stats_results(); + Multi_fs_tracker_busy = 0; + return 0; + } + + // if playing with an invalid weapons.tbl + if(!Game_weapons_tbl_valid){ + send_game_chat_packet(Net_player, XSTR("", 1046), MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(XSTR("", 1046), 0, 0); + multi_fs_tracker_report_stats_results(); + Multi_fs_tracker_busy = 0; + return 0; + } + + // if any players have hacked info + for(idx=0; idx", 1048), MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(XSTR("", 1048), 0, 0); + multi_fs_tracker_report_stats_results(); + Multi_fs_tracker_busy = 0; + return 0; + } + + // check to see if the mission is valid + if(multi_fs_tracker_validate_mission(Game_current_mission_filename) != MVALID_STATUS_VALID){ + send_game_chat_packet(Net_player, XSTR("", 1049), MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(XSTR("", 1049), 0, 0); + Multi_fs_tracker_busy = 0; + return 0; + } +#endif + + // multi_fs_store_stats_do() will handle all details of negotiating stats transfer with the tracker + do { + ret_val = multi_fs_store_stats_do(); + game_set_frametime(GS_STATE_STANDALONE_POSTGAME); + multi_do_frame(); + } while(ret_val == MT_STATS_NOT_DONE); + + // report on the results + multi_fs_tracker_report_stats_results(); + + // mark the module as no longer busy + Multi_fs_tracker_busy = 0; + + return 1; +} + +// log freespace out of the tracker +void multi_fs_tracker_logout() +{ + if(!Multi_fs_tracker_inited){ + return; + } + + // make sure we're connected + if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER) || !(Net_player->flags & NETINFO_FLAG_MT_CONNECTED)){ + return; + } + + // otherwise, log us out + SendGameOver(); + + // clear our data + memset(&Multi_tracker_game_data, 0, sizeof(freespace2_net_game_data)); + Net_player->flags &= ~(NETINFO_FLAG_MT_CONNECTED); + + // NETLOG + ml_string(NOX("Server disconnecting from Game Tracker")); +} + +// send a request for a list of games +void multi_fs_tracker_send_game_request() +{ + filter_game_list_struct filter; + int len; + + // if we're not initialized, don't do anything + if(!Multi_fs_tracker_inited){ + return; + } + + // if we have a valid filter, use that instead + len = strlen(Multi_fs_tracker_filter); + if((len > 0) && (len < CHANNEL_LEN-1) ){ + memset(&filter,0,sizeof(filter_game_list_struct)); + + strcpy(filter.channel,Multi_fs_tracker_filter); + RequestGameListWithFilter(&filter); + } else { + // simple API call + RequestGameList(); + } +} + +// if the API has successfully been initialized and is running +int multi_fs_tracker_inited() +{ + return Multi_fs_tracker_inited; +} + +// update our settings on the tracker regarding the current netgame stuff +void multi_fs_tracker_update_game(netgame_info *ng) +{ + // int idx,count; + + if(!Multi_fs_tracker_inited){ + return; + } + + // copy in the relevant data + Multi_tracker_game_data.max_players = ng->max_players; + Multi_tracker_game_data.current_num_players = multi_num_players(); + /* + memset(Multi_tracker_game_data.players, 0 ,MAX_FREESPACE_PLAYERS * MAX_FREESPACE_PLAYER_NAME_LEN); + count = 0; + for(idx=0;idxcallsign); + Multi_tracker_game_data.player_rank[count] = Net_players[idx].player->stats.rank; + count++; + } + } + */ + memset(Multi_tracker_game_data.mission_name, 0, MAX_FREESPACE_MISSION_NAME_LEN); + strcpy(Multi_tracker_game_data.mission_name, ng->name); + + // NETLOG + ml_string(NOX("Server updating netgame info for Game Tracker")); +} + +// if we're currently busy performing some tracker operation (ie, you should wait or not) +int multi_fs_tracker_busy() +{ + return Multi_fs_tracker_busy; +} + + +// ----------------------------------------------------------------------------------- +// FREESPACE MASTER TRACKER FORWARD DEFINITIONS +// + +// used with popup_till_condition() for validating freespace pilots +int multi_fs_validate_process() +{ + // should never be here if this is not true + SDL_assert(Multi_fs_tracker_inited); + + PSNET_TOP_LAYER_PROCESS(); + + // if we're still in player validation mode + if(Multi_validate_mode == 0){ + switch(ValidateUser(NULL,NULL)){ + // timeout on waiting for response + case -2 : + return MT_VALIDATE_TIMEOUT; + + // user invalid + case -1: + // set tracker id to -1 + strcpy(Multi_tracker_id_string,"-1"); + Multi_tracker_id = -1; + return MT_VALIDATE_FAIL; + + // still waiting + case 0: + return MT_VALIDATE_NOT_DONE; + + // user valid + case 1: + // now we need to try and receive stats + + // mark me as being valid + Multi_tracker_player_is_valid = 1; + + // change the popup text + popup_change_text(XSTR("Attempting to get pilot stats ...",679)); + + // get my tracker id# + Multi_tracker_id = atoi(Multi_tracker_id_string); + SDL_assert(Multi_tracker_id != -1); + + GetFSPilotData((vmt_freespace2_struct*)0xffffffff,NULL,NULL,0); + GetFSPilotData(&Multi_tracker_fs_pilot,Player->callsign,Multi_tracker_id_string,1); + + // set to mode 1 + Multi_validate_mode = 1; + return MT_VALIDATE_NOT_DONE; + + default : + Int3(); + } + } else { + switch(GetFSPilotData(NULL,NULL,NULL,0)){ + // timedout + case -1: + return MT_VALIDATE_TIMEOUT; + + // still waiting + case 0: + return MT_VALIDATE_NOT_DONE; + + // got data + case 1: + return MT_VALIDATE_SUCCEED; + + // failure + case 3: + return MT_VALIDATE_FAIL; + } + } + + // we're not done yet - probably should never get here + return MT_VALIDATE_NOT_DONE; +} + +// used with popup_till_condition() for storing player stats at the end of a freespace game +int multi_fs_store_stats_do() +{ + char tracker_id_string[512]; + char popup_text[100]; + + SDL_assert(Multi_fs_tracker_inited); + + PSNET_TOP_LAYER_PROCESS(); + + switch(Multi_store_stats_mode){ + // get stats for all players + case MT_STORE_STATS_VALIDATE : + Multi_store_stats_mode = MT_STORE_STATS_GET_STATS; + break; + + case MT_STORE_STATS_GET_STATS: + // if we need to get the next player + if(Multi_store_stats_player_flag){ + Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index); + + // if it returns < 0 we're done with all players and should move onto the next stage (applying mission stats) + if(Multi_store_stats_player_index < 0){ + Multi_store_stats_mode = MT_STORE_STATS_ACCEPT; + return MT_STATS_NOT_DONE; + } + + // unset this flag so we process the request + Multi_store_stats_player_flag = 0; + + // fill out the information request + memset(tracker_id_string,0,512); + SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0); + + // verify that there are no duplicate tracker id's to this one + multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index); + multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index); + + sprintf(tracker_id_string,"%d",Net_players[Multi_store_stats_player_index].tracker_player_id); + Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1; + Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0; + + // send the request itself + GetFSPilotData((vmt_freespace2_struct*)0xffffffff, NULL, NULL,0); + memset(&Multi_store_stats_stats, 0, sizeof(vmt_freespace2_struct)); + if(GetFSPilotData(&Multi_store_stats_stats, Net_players[Multi_store_stats_player_index].player->callsign,tracker_id_string,1) != 0){ + Int3(); + + // move onto the next player + Multi_store_stats_player_flag = 1; + return MT_STATS_NOT_DONE; + } + + // set the popup text + if(!(Game_mode & GM_STANDALONE_SERVER)){ + memset(popup_text,0,100); + sprintf(popup_text,XSTR("Getting player stats for %s...\n",680),Net_players[Multi_store_stats_player_index].player->callsign); + popup_change_text(popup_text); + } + return MT_STATS_NOT_DONE; + } + + // process the request + switch(GetFSPilotData(NULL,NULL,NULL,0)){ + // got data + case 1: + // copy his stats, then flag him as done so we move onto the next guys + multi_stats_tracker_to_fs(&Multi_store_stats_stats,&Net_players[Multi_store_stats_player_index].player->stats); + + // make sure we apply his mission stats now + scoring_do_accept(&Net_players[Multi_store_stats_player_index].player->stats); + +#ifndef NDEBUG + { + // debug code to check for bogus stats + scoring_struct *ssp = &(Net_players[Multi_store_stats_player_index].player->stats); + vmt_freespace2_struct *vmt = &Multi_store_stats_stats; + + if ( (ssp->missions_flown < vmt->missions_flown) || (ssp->flight_time < ssp->flight_time) || (ssp->kill_count < vmt->kill_count) ) { + Int3(); + } + } +#endif + + // flag him as being completed + Multi_store_stats_player_flag = 1; + + // also store this last security value so we can properly update him + Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = Multi_store_stats_stats.security; + Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = Multi_store_stats_stats.checksum; + break; + + // in progress + case 0: + break; + + // failure + case 3: case -2: case 2: case -3: case -1: + // this shouldn't be happening under most conditions. For debugging.... + Int3(); + + // flag him as done so we move onto the next guy + Multi_store_stats_player_flag = 1; + Net_players[Multi_store_stats_player_index].s_info.tracker_security_last = -1; + Net_players[Multi_store_stats_player_index].s_info.tracker_checksum = 0; + + // mark down that the stats get for him failed + Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_GET_FAILED; + Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE; + break; + } + break; + + // update all stats for all players locally and on client machines + case MT_STORE_STATS_ACCEPT : + // tell everyone to save their stats + send_store_stats_packet(1); + + // reset status flags and indices + Multi_store_stats_player_index = -1; + Multi_store_stats_player_flag = 1; + + Multi_store_stats_mode = MT_STORE_STATS_SEND_STATS; + break; + + // send stats to the tracker + case MT_STORE_STATS_SEND_STATS: + // if we need to get the next player + if(Multi_store_stats_player_flag){ + Multi_store_stats_player_index = multi_fs_store_stats_get_next_player(Multi_store_stats_player_index); + + // if it returns < 0 we need to move onto the next player + if(Multi_store_stats_player_index < 0){ + return MT_STATS_SUCCEED; + } + + Multi_store_stats_player_flag = 0; + + // fill in the information + memset(&Multi_store_stats_stats,0,sizeof(vmt_freespace2_struct)); + + SDL_assert(Net_players[Multi_store_stats_player_index].tracker_player_id > 0); + + // verify that there are no duplicate tracker id's to this one + multi_fs_tracker_check_dup(Net_players[Multi_store_stats_player_index].tracker_player_id,Multi_store_stats_player_index); + multi_fs_tracker_check_dup_callsign(&Net_players[Multi_store_stats_player_index],Multi_store_stats_player_index); + multi_stats_fs_to_tracker(&Net_players[Multi_store_stats_player_index].player->stats,&Multi_store_stats_stats,Net_players[Multi_store_stats_player_index].player,Net_players[Multi_store_stats_player_index].tracker_player_id); + + Multi_store_stats_stats.security = Net_players[Multi_store_stats_player_index].s_info.tracker_security_last; + + // SDL_assert(Net_players[Multi_store_stats_player_index].s_info.tracker_checksum != 0); + Multi_store_stats_stats.checksum = Net_players[Multi_store_stats_player_index].s_info.tracker_checksum; + + // send the request + SendFSPilotData((vmt_freespace2_struct*)0xffffffff); + if(SendFSPilotData(&Multi_store_stats_stats) != 0){ + Int3(); + + // failed to send, try another player the next time around + Multi_store_stats_player_flag = 1; + return MT_STATS_NOT_DONE; + } + + // set the popup text + if(!(Game_mode & GM_STANDALONE_SERVER)){ + memset(popup_text,0,100); + sprintf(popup_text,XSTR("Updating player stats for %s...\n",681),Net_players[Multi_store_stats_player_index].player->callsign); + popup_change_text(popup_text); + } + + return MT_STATS_NOT_DONE; + } + + // otherwise check on his status + switch(SendFSPilotData(NULL)){ + // error + case -1: case -2: case -3: case 2: case 3: + // flag him as done so we move onto the next guy + Multi_store_stats_player_flag = 1; + + Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_SEND_FAILED; + Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE; + break; + + // got data + case 1: + // flag him as done so we move onto the next guys + Multi_store_stats_player_flag = 1; + Net_players[Multi_store_stats_player_index].flags |= NETINFO_FLAG_MT_DONE; + break; + } + + break; + } + + // return not done yet + return MT_STATS_NOT_DONE; +} + +// copy a freespace stats struct to a tracker-freespace stats struct +void multi_stats_fs_to_tracker(scoring_struct *fs, vmt_freespace2_struct *vmt, player *pl, int tracker_id) +{ + char tracker_id_string[256]; + + // tracker id string + memset(vmt->tracker_id,0,TRACKER_ID_LEN); + memset(tracker_id_string, 0, 256); + sprintf(tracker_id_string,"%d",tracker_id); + strncpy(vmt->tracker_id, tracker_id_string, TRACKER_ID_LEN); + + // pilot callsign + memset(vmt->pilot_name,0,PILOT_NAME_LEN); + strcpy(vmt->pilot_name,pl->callsign); + + // score, rank and medals + vmt->score = fs->score; + vmt->rank = fs->rank; + SDL_assert(MAX_FS2_MEDALS == NUM_MEDALS); + memcpy(vmt->medals, fs->medals, sizeof(int) * MAX_FS2_MEDALS); + vmt->num_medals = MAX_FS2_MEDALS; + + // kills and assists + SDL_assert(MAX_FS2_SHIP_TYPES == MAX_SHIP_TYPES); + memcpy(vmt->kills, fs->kills, sizeof(ushort) * MAX_FS2_SHIP_TYPES); + vmt->assists = fs->assists; + vmt->kill_count = fs->kill_count; + vmt->kill_count_ok = fs->kill_count_ok; + vmt->num_ship_types = MAX_FS2_SHIP_TYPES; + + // shot statistics + vmt->p_shots_fired = fs->p_shots_fired; + vmt->s_shots_fired = fs->s_shots_fired; + vmt->p_shots_hit = fs->p_shots_hit; + vmt->s_shots_hit = fs->s_shots_hit; + vmt->p_bonehead_hits = fs->p_bonehead_hits; + vmt->s_bonehead_hits = fs->s_bonehead_hits; + vmt->bonehead_kills = fs->bonehead_kills; + + // missions flown information + vmt->missions_flown = fs->missions_flown; + vmt->flight_time = fs->flight_time; + vmt->last_flown = (unsigned int)fs->last_flown; +} + +// copy a tracker-freespace stats struct to a freespace stats struct +void multi_stats_tracker_to_fs(vmt_freespace2_struct *vmt,scoring_struct *fs) +{ + int num_medals, num_ship_types; + + // score, rank and medals + fs->score = vmt->score; + fs->rank = vmt->rank; + num_medals = vmt->num_medals; + if(num_medals > NUM_MEDALS){ + Int3(); + num_medals = NUM_MEDALS; + } + memset(fs->medals, 0, sizeof(int) * NUM_MEDALS); + memcpy(fs->medals, vmt->medals, sizeof(int) * num_medals); + + // kills and assists + num_ship_types = vmt->num_ship_types; + if(num_ship_types > MAX_SHIP_TYPES){ + Int3(); + num_ship_types = MAX_SHIP_TYPES; + } + memset(fs->kills, 0, sizeof(ushort) * MAX_SHIP_TYPES); + memcpy(fs->kills, vmt->kills, sizeof(ushort) * num_ship_types); + fs->assists = vmt->assists; + fs->kill_count = vmt->kill_count; + fs->kill_count_ok = vmt->kill_count_ok; + + // shot statistics + fs->p_shots_fired = vmt->p_shots_fired; + fs->s_shots_fired = vmt->s_shots_fired; + fs->p_shots_hit = vmt->p_shots_hit; + fs->s_shots_hit = vmt->s_shots_hit; + fs->p_bonehead_hits = vmt->p_bonehead_hits; + fs->s_bonehead_hits = vmt->s_bonehead_hits; + fs->bonehead_kills = vmt->bonehead_kills; + + // missions flown information + fs->missions_flown = vmt->missions_flown; + fs->flight_time = vmt->flight_time; + if(fs->flight_time < 0){ + fs->flight_time = 0; + } + fs->last_flown = (time_t)vmt->last_flown; + if(fs->last_flown < 0){ + fs->last_flown = 0; + } +} + +// process an incoming active game item +void multi_fs_tracker_process_game_item(game_list *gl) +{ + active_game ag; + int idx; + + for(idx=0;idxgame_server[idx] == 0){ + continue; + } + + // package up the game information + memset(&ag,0,sizeof(active_game)); + strcpy(ag.name,gl->game_name[idx]); + memcpy(&ag.server_addr.addr[0],&gl->game_server[idx],sizeof(unsigned long)); + ag.server_addr.type = NET_TCP; + ag.server_addr.port = DEFAULT_GAME_PORT; + + // add to the active game list + // multi_update_active_games(&ag); + + // query this server + send_server_query(&ag.server_addr); + } +} + +int multi_fs_store_stats_get_next_player(int cur_player) +{ + int idx; + + // if we're at the end of the list + if(cur_player == (MAX_PLAYERS - 1)){ + return -2; + } + + // find the next player + for(idx=cur_player+1;idxplayer->callsign,Net_players[idx].player->callsign)); + } + } +} + +// return an MVALID_STATUS_* constant +int multi_fs_tracker_validate_mission_std() +{ + int ret_val; + + // wait for a response from the tracker + do { + ret_val = ValidateMission(NULL); + } while(ret_val == 0); + + // report on the results + switch(ret_val){ + // timeout + case -2: + std_destroy_gen_dialog(); + return MVALID_STATUS_UNKNOWN; + + // invalid + case -1: + std_destroy_gen_dialog(); + return MVALID_STATUS_INVALID; + + // valid, success + case 1: + std_destroy_gen_dialog(); + return MVALID_STATUS_VALID; + } + + Int3(); + return 0; +} + +// special return values : +// 1 for timeout +// 2 for invalid +// 3 for valid +int multi_fs_tracker_validate_mission_normal() +{ + switch(ValidateMission(NULL)){ + // timeout + case -2: + return 1; + + // invalid + case -1 : + return 2; + + // valid + case 1: + return 3; + } + + // not done yet + return 0; +} + +// return an MVALID_STATUS_* (see multiui.h) value, or -2 if the user has "cancelled" +int multi_fs_tracker_validate_mission(char *filename) +{ + vmt_validate_mission_req_struct mission; + char popup_string[512] = ""; + + if(!Multi_fs_tracker_inited){ + return MVALID_STATUS_UNKNOWN; + } + + // get the checksum of the local file + memset(&mission, 0, sizeof(mission)); + strcpy(mission.file_name, filename); + if(!cf_chksum_long(mission.file_name, (uint*)&mission.checksum)){ + return MVALID_STATUS_UNKNOWN; + } + + // try and validate the mission + if(ValidateMission(&mission) != 0){ + return MVALID_STATUS_UNKNOWN; + } + + // do frames for standalone and non-standalone + if(Game_mode & GM_STANDALONE_SERVER){ + int ret_code; + + // set the filename in the dialog + std_gen_set_text(filename, 2); + + // validate the mission + ret_code = multi_fs_tracker_validate_mission_std(); + + // if the dialog is no longer active, cancel everything + //if(!std_gen_is_active()){ + // return MVALID_STATUS_UNKNOWN; + //} + + return ret_code; + } else { + memset(popup_string, 0, 512); + sprintf(popup_string, XSTR("Validating mission %s", 1074),filename); + + // run a popup + switch(popup_till_condition(multi_fs_tracker_validate_mission_normal, XSTR("&Cancel", 667), popup_string)){ + // cancel + case 0: + // bash some API values here so that next time we try and verify, everything works + extern int MissionValidState; + MissionValidState = VALID_STATE_IDLE; + return -2; + + // timeout + case 1: + return MVALID_STATUS_UNKNOWN; + + // invalid + case 2: + return MVALID_STATUS_INVALID; + + // valid + case 3: + return MVALID_STATUS_VALID; + } + } + + return MVALID_STATUS_UNKNOWN; +} + +// report on the results of the stats store procedure +void multi_fs_tracker_report_stats_results() +{ + int idx; + char str[512] = ""; + + // tell everyone stats store is complete + memset(str, 0, 512); + strcpy(str, XSTR("", 1001)); + send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1); + multi_display_chat_msg(str, 0, 0); + ml_string(str); + + // for all players + for(idx=0; idx", 1002), Net_players[idx].player->callsign); + send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL, NULL, 1); + + multi_display_chat_msg(str, 0, 0); + ml_string(str); + } + } + } +} + +// return an MSW_STATUS_* constant +int multi_fs_tracker_validate_sw_std() +{ + int ret_val; + + // wait for a response from the tracker + do { + ret_val = ValidateSquadWar(NULL, &Multi_tracker_sw_response); + } while(ret_val == 0); + + // report on the results + switch(ret_val){ + // timeout + case -2: + return MVALID_STATUS_UNKNOWN; + + // invalid + case -1: + return MVALID_STATUS_INVALID; + + // valid, success + case 1: + return MVALID_STATUS_VALID; + } + + return MVALID_STATUS_UNKNOWN; +} + +// special return values : +// 1 for timeout +// 2 for invalid +// 3 for valid +int multi_fs_tracker_validate_sw_normal() +{ + switch(ValidateSquadWar(NULL, &Multi_tracker_sw_response)){ + // timeout + case -2: + return 1; + + // invalid + case -1 : + return 2; + + // valid + case 1: + return 3; + } + + // not done yet + return 0; +} + +#define STUFF_SW_RESPONSE(_c) do {\ + strcpy(_c, "");\ + int _idx;\ + int _bogus = 1;\ + for(_idx=0; _idx +#endif + +#include "multi_pxo.h" +#include "animplay.h" +#include "ui.h" +#include "key.h" +#include "bmpman.h" +#include "palman.h" +#include "gamesnd.h" +#include "gamesequence.h" +#include "cfile.h" +#include "chat_api.h" +#include "popup.h" +#include "freespace.h" +#include "font.h" +#include "multi.h" +#include "multiui.h" +#include "multi_fstracker.h" +#include "ptrack.h" +#include "gtrack.h" +#include "medals.h" +#include "multi_update.h" +#include "alphacolors.h" +#include "timer.h" +#include "inetgetfile.h" +#include "cfilesystem.h" +#include "osregistry.h" + +// ---------------------------------------------------------------------------------------------------- +// PXO DEFINES/VARS +// + +// button definitions +#define MULTI_PXO_NUM_BUTTONS 15 +#define MULTI_PXO_PLIST_UP 0 +#define MULTI_PXO_PLIST_DOWN 1 +#define MULTI_PXO_RANKINGS 2 +#define MULTI_PXO_PINFO 3 +#define MULTI_PXO_FIND 4 +#define MULTI_PXO_MOTD 5 +#define MULTI_PXO_JOIN 6 +#define MULTI_PXO_JOIN_PRIV 7 +#define MULTI_PXO_CHAN_UP 8 +#define MULTI_PXO_CHAN_DOWN 9 +#define MULTI_PXO_TEXT_UP 10 +#define MULTI_PXO_TEXT_DOWN 11 +#define MULTI_PXO_EXIT 12 +#define MULTI_PXO_HELP 13 +#define MULTI_PXO_GAMES 14 + + +ui_button_info Multi_pxo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_BUTTONS] = { + { // GR_640 + ui_button_info( "PXB_00", 1, 104, -1, -1, 0 ), // scroll player list up + ui_button_info( "PXB_01", 1, 334, -1, -1, 1 ), // scroll player list down + ui_button_info( "PXB_02", 18, 385, -1, -1, 2 ), // rankings webpage + ui_button_info( "PXB_03", 71, 385, -1, -1, 3 ), // pilot info + ui_button_info( "PXB_04", 115, 385, -1, -1, 4 ), // find player + ui_button_info( "PXB_05", 1, 443, -1, -1, 5 ), // motd + ui_button_info( "PXB_06", 330, 96, -1, -1, 6 ), // join channel + ui_button_info( "PXB_07", 330, 131, -1, -1, 7 ), // join private channel + ui_button_info( "PXB_08", 618, 92, -1, -1, 8 ), // scroll channels up + ui_button_info( "PXB_09", 618, 128, -1, -1, 9 ), // scroll channels down + ui_button_info( "PXB_10", 615, 171, -1, -1, 10 ), // scroll text up + ui_button_info( "PXB_11", 615, 355, -1, -1, 11 ), // scroll text down + ui_button_info( "PXB_12", 482, 435, -1, -1, 12 ), // exit + ui_button_info( "PXB_13", 533, 432, -1, -1, 13 ), // help + ui_button_info( "PXB_14", 573, 432, -1, -1, 14 ), // games list + }, + { // GR_1024 + ui_button_info( "2_PXB_00", 2, 166, -1, -1, 0 ), // scroll player list up + ui_button_info( "2_PXB_01", 2, 534, -1, -1, 1 ), // scroll player list down + ui_button_info( "2_PXB_02", 29, 616, -1, -1, 2 ), // rankings webpage + ui_button_info( "2_PXB_03", 114, 616, -1, -1, 3 ), // pilot info + ui_button_info( "2_PXB_04", 184, 616, -1, -1, 4 ), // find player + ui_button_info( "2_PXB_05", 2, 709, -1, -1, 5 ), // motd + ui_button_info( "2_PXB_06", 528, 119, -1, -1, 6 ), // join channel + ui_button_info( "2_PXB_07", 528, 175, -1, -1, 7 ), // join private channel + ui_button_info( "2_PXB_08", 989, 112, -1, -1, 8 ), // scroll channels up + ui_button_info( "2_PXB_09", 989, 170, -1, -1, 9 ), // scroll channels down + ui_button_info( "2_PXB_10", 984, 240, -1, -1, 10 ), // scroll text up + ui_button_info( "2_PXB_11", 984, 568, -1, -1, 11 ), // scroll text down + ui_button_info( "2_PXB_12", 771, 696, -1, -1, 12 ), // exit + ui_button_info( "2_PXB_13", 853, 691, -1, -1, 13 ), // help + ui_button_info( "2_PXB_14", 917, 691, -1, -1, 14 ), // games list + }, +}; + +// define MULTI_PXO_NUM_TEXT 18 +#define MULTI_PXO_NUM_TEXT 16 +UI_XSTR Multi_pxo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_TEXT] = { + { // GR_640 + {"Web", 1313, 20, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button}, + {"Ranking", 1314, 6, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button}, + {"Pilot", 1310, 68, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button}, + {"Info", 1311, 72, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button}, + {"Find", 1315, 119, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_FIND].button}, + {"Motd", 1316, 36, 456, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_MOTD].button}, + {"Join", 1505, 291, 100, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button}, + {"Channel", 1317, 266, 112, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button}, + {"Join", 1506, 291, 134, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button}, + {"Private", 1318, 273, 146, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button}, + {"Exit", 1416, 493, 424, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_EXIT].button}, + {"Help", 928, 535, 416, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_HELP].button}, + {"Games", 1319, 579, 416, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_GAMES].button}, + {"Players", 1269, 29, 102, UI_XSTR_COLOR_GREEN, -1, NULL}, + // {"Squad", 1320, 110, 101, UI_XSTR_COLOR_GREEN, -1, NULL}, + // {"Channels", 1321, 369, 76, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"Players", 1269, 507, 90, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"Games", 1319, 568, 90, UI_XSTR_COLOR_GREEN, -1, NULL} + }, + { // GR_1024 + {"Web", 1313, 32, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button}, + {"Ranking", 1314, 9, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button}, + {"Pilot", 1310, 109, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button}, + {"Info", 1311, 115, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button}, + {"Find", 1315, 190, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_FIND].button}, + {"Motd", 1316, 58, 729, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_MOTD].button}, + {"Join", 1505, 488, 129, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button}, + {"Channel", 1317, 461, 139, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button}, + {"Join", 1506, 487, 184, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button}, + {"Private", 1318, 467, 194, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button}, + {"Exit", 1416, 789, 678, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_EXIT].button}, + {"Help", 928, 857, 667, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_HELP].button}, + {"Games", 1319, 917, 667, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_GAMES].button}, + {"Players", 1269, 47, 163, UI_XSTR_COLOR_GREEN, -1, NULL}, + // {"Squad", 1320, 176, 163, UI_XSTR_COLOR_GREEN, -1, NULL}, + // {"Channels", 1321, 591, 86, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"Players", 1269, 852, 109, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"Games", 1319, 926, 109, UI_XSTR_COLOR_GREEN, -1, NULL} + } +}; + +char Multi_pxo_bitmap_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "PXOChat", + "2_PXOChat" +}; +char Multi_pxo_mask_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "PXOChat-M", + "2_PXOChat-M" +}; + +UI_WINDOW Multi_pxo_window; +int Multi_pxo_bitmap = -1; +int Multi_pxo_palette = -1; + + +// pxo animation +#define MULTI_PXO_ANIM_FNAME "pxologo" +#define MULTI_PXO_ANIM_X 0 +#define MULTI_PXO_ANIM_Y 4 +anim *Multi_pxo_anim = NULL; +anim_instance *Multi_pxo_anim_instance = NULL; + +// rankings last clicked time +#define MULTI_PXO_RANK_TIME (5.0f) +float Multi_pxo_ranking_last = -1.0f; + +// chat api vars +int Multi_pxo_must_connect = 0; // if we still need to connect +int Multi_pxo_connected = 0; // if we are connected +int Multi_pxo_must_validate = 0; // if we need to validate on the tracker +int Multi_pxo_must_autojoin = 1; // still need to autojoin a channel +int Multi_pxo_must_verify_version = 1; // only do it once per instance of freespace + +// mode +#define MULTI_PXO_MODE_NORMAL 0 // normal mode +#define MULTI_PXO_MODE_PRIVATE 1 // private channel popup +#define MULTI_PXO_MODE_FIND 2 // find player popup +int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL; + +// our nick for this session +int x = NAME_LENGTH; +char Multi_pxo_nick[NAME_LENGTH+1]; + +// check for button presses +void multi_pxo_check_buttons(); + +// handle a button press +void multi_pxo_button_pressed(int n); + +// condition function for popup_do_with_condition for connected to Parallax Online +// return 10 : on successful connect +int multi_pxo_connect_do(); + +// attempt to connect to Parallax Online, return success or fail +int multi_pxo_connect(); + +// run the networking functions for the PXO API +void multi_pxo_api_process(); + +// process a "nick" change event +void multi_pxo_process_nick_change(char *data); + +// run normally (no popups) +void multi_pxo_do_normal(); + +// blit everything on the "normal" screen +void multi_pxo_blit_all(); + +// process common stuff +void multi_pxo_process_common(); + +// get selected player information +void multi_pxo_get_data(char *name); + +// handle being kicked +void multi_pxo_handle_kick(); + +// handle being disconnected +void multi_pxo_handle_disconnect(); + +// return string2, which is the first substring of string 1 without a space +// it is safe to pass the same pointer for both parameters +void multi_pxo_strip_space(char *string1,char *string2); + +// fire up the given URL +void multi_pxo_url(char *url); + +// load/set the palette +void multi_pxo_load_palette(); + +// unload the palette +void multi_pxo_unload_palette(); + +// if we're currently on a private channel +int multi_pxo_on_private_channel(); + +// convert string 1 into string 2, substituting underscores for spaces +void multi_pxo_underscore_nick(char *string1,char *string2); + +// if the command is a potential "nick" command +int multi_pxo_is_nick_command(char *msg); + + +// status bar stuff ----------------------------------------------- +int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 95, 467, 354, 12 + }, + { // GR_1024 + 152, 750, 570, 12 + }, +}; + +// the status text itself +char Multi_pxo_status_text[255]; + +// set the status text +void multi_pxo_set_status_text(const char *txt); + +// blit the status text +void multi_pxo_blit_status_text(); + + +// channel related stuff ------------------------------------------- +#define MAX_CHANNEL_NAME_LEN 32 +#define MAX_CHANNEL_DESCRIPT_LEN 120 + +// some convenient macros +#define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1) +#define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1) + +typedef struct pxo_channel { + pxo_channel *next,*prev; // next and previous items in the list + char name[MAX_CHANNEL_NAME_LEN+1]; // name + char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description + short num_users; // # users, or -1 if not in use + short num_servers; // the # of servers registered on this channel +} pxo_channel; + +// last channel we were on before going to the game list screen +char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = ""; +int Multi_pxo_use_last_channel = 0; + +// all channels which are prefixed with this are "lobby" channels +#define MULTI_PXO_AUTOJOIN_PREFIX "#lobby" + +// join this channel to get put in an appropriate lobby channel +#define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect" + +int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 369, 101, 241, 60 + }, + { // GR_1024 + 593, 124, 386, 100 + }, +}; + +// this is the offset from the RIGHT side of the channel box +#define CHAN_PLAYERS_COLUMN 0 +#define CHAN_GAMES_COLUMN 1 +static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = { + { 81, 26 }, + { 103, 35 } +}; + +#define CHANNEL_REFRESH_TIME (75.0f) +float Multi_pxo_channel_last_refresh = -1.0f; + +#define CHANNEL_SERVER_REFRESH_TIME (35.0f) +float Multi_pxo_channel_server_refresh = -1.0f; + +int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = { + 6, // GR_640 + 10 // GR_1024 +}; + +UI_BUTTON Multi_pxo_channel_button; + +// head of the list of available (displayed) channels +pxo_channel *Multi_pxo_channels = NULL; +int Multi_pxo_channel_count = 0; + +// item we're going to start displaying at +pxo_channel *Multi_pxo_channel_start = NULL; +int Multi_pxo_channel_start_index = -1; + +// items we've currently got selected +pxo_channel *Multi_pxo_channel_select = NULL; + +// channel we're currently connected to, num_users == -1, if we're not connected +pxo_channel Multi_pxo_channel_current; + +// channel we're currently trying to change to, num_users == -1, if we're not trying to change channels +pxo_channel Multi_pxo_channel_switch; + +// get a list of channels on the server (clear any old list as well) +void multi_pxo_get_channels(); + +// clear the old channel list +void multi_pxo_clear_channels(); + +// parse the input string and make a list of new channels +void multi_pxo_make_channels(char *chan_str); + +// create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail +pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list); + +// lookup a channel with the specified name +pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list); + +// process the channel list (select, etc) +void multi_pxo_process_channels(); + +// display the channel list +void multi_pxo_blit_channels(); + +// scroll channel list up +void multi_pxo_scroll_channels_up(); + +// scroll channel list down +void multi_pxo_scroll_channels_down(); + +// attempt to join a channel +void multi_pxo_join_channel(pxo_channel *chan); + +// handle any processing details if we're currently trying to join a channel +void multi_pxo_handle_channel_change(); + +// autojoin an appropriate channel +void multi_pxo_autojoin(); + +// does the string match the "autojoin" prefic +int multi_pxo_is_autojoin(char *name); + +// send a request to refresh our channel server counts +void multi_pxo_channel_refresh_servers(); + +// refresh current channel server count +void multi_pxo_channel_refresh_current(); + + +// player related stuff ------------------------------------------- +#define MAX_PLAYER_NAME_LEN 32 + +typedef struct player_list { + player_list *next,*prev; + char name[MAX_PLAYER_NAME_LEN+1]; +} player_list; + +// channel list region +int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 27, 121, 141, 261 + }, + { // GR_1024 + 43, 194, 154, 417 + }, +}; + +int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = { + 25, // GR_640 + 41 // GR_1024 +}; +UI_BUTTON Multi_pxo_player_button; + +// UI_SLIDER2 Multi_pxo_player_slider; + +// slider coords +int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 1, 139, 21, 192 + }, + { // GR_1024 + 2, 219, 33, 314 + } +}; +const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = { + "slider", // GR_640 + "2_slider" // GR_1024 +}; + +// head of the list of players in this channel +player_list *Multi_pxo_players = NULL; +int Multi_pxo_player_count = 0; + +// item we're going to start displaying at +player_list *Multi_pxo_player_start = NULL; +// int Multi_pxo_player_start_index = -1; + +// items we've currently got selected +player_list *Multi_pxo_player_select = NULL; + +// clear the old player list +void multi_pxo_clear_players(); + +// create a new player with the given name and place it on the player list, return a pointer or NULL on fail +player_list *multi_pxo_add_player(char *name); + +// remove a player with the given name +void multi_pxo_del_player(char *name); + +// try and find a player with the given name, return a pointer to his entry (or NULL) +player_list *multi_pxo_find_player(char *name); + +// process the player list (select, etc) +void multi_pxo_process_players(); + +// display the player list +void multi_pxo_blit_players(); + +// scroll player list up +void multi_pxo_scroll_players_up(); + +// scroll player list down +void multi_pxo_scroll_players_down(); + +// get the absolute index of the displayed items which our currently selected one is +int multi_pxo_get_select_index(); + +DCF(players, "") +{ + char name[512] = ""; + + // add a bunch of bogus players + dc_get_arg(ARG_INT); + for(int idx=0; idx 0) { + Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap); + } + + // create the channel list select button and hide it + Multi_pxo_channel_button.create(&Multi_pxo_window, "", Multi_pxo_chan_coords[gr_screen.res][0], Multi_pxo_chan_coords[gr_screen.res][1], Multi_pxo_chan_coords[gr_screen.res][2], Multi_pxo_chan_coords[gr_screen.res][3], 0, 1); + Multi_pxo_channel_button.hide(); + + // create the player list select button and hide it + Multi_pxo_player_button.create(&Multi_pxo_window, "", Multi_pxo_player_coords[gr_screen.res][0], Multi_pxo_player_coords[gr_screen.res][1], Multi_pxo_player_coords[gr_screen.res][2], Multi_pxo_player_coords[gr_screen.res][3], 0, 1); + Multi_pxo_player_button.hide(); + + // create the chat input box + Multi_pxo_chat_input.create(&Multi_pxo_window, Multi_pxo_input_coords[gr_screen.res][0], Multi_pxo_input_coords[gr_screen.res][1], Multi_pxo_input_coords[gr_screen.res][2], MAX_CHAT_LINE_LEN + 1, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED); + Multi_pxo_chat_input.set_focus(); + + // create the banner button and hide it + Multi_pxo_ban_button.create(&Multi_pxo_window, "", Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1], Pxo_ban_coords[gr_screen.res][2], Pxo_ban_coords[gr_screen.res][3], 0, 1); + Multi_pxo_ban_button.hide(); + + // create the player list slider + // Multi_pxo_player_slider.create(&Multi_pxo_window, Multi_pxo_player_slider_coords[gr_screen.res][0], Multi_pxo_player_slider_coords[gr_screen.res][1], Multi_pxo_player_slider_coords[gr_screen.res][2], Multi_pxo_player_slider_coords[gr_screen.res][3], 0, Multi_pxo_player_slider_name[gr_screen.res], multi_pxo_scroll_players_up, multi_pxo_scroll_players_down, NULL); + + // create the chat slider + Multi_pxo_chat_slider.create(&Multi_pxo_window, Multi_pxo_chat_slider_coords[gr_screen.res][0], Multi_pxo_chat_slider_coords[gr_screen.res][1], Multi_pxo_chat_slider_coords[gr_screen.res][2], Multi_pxo_chat_slider_coords[gr_screen.res][3], 0, Multi_pxo_chat_slider_name[gr_screen.res], multi_pxo_scroll_chat_up, multi_pxo_scroll_chat_down, NULL); + + // set our connection status so that we do the right stuff next frame + Multi_pxo_must_validate = 1; + Multi_pxo_must_connect = 0; + Multi_pxo_connected = 0; + + // channel we're currently connected to + memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel)); + Multi_pxo_channel_current.num_users = -1; + + // channel we're currently trying to change to, or NULL if nont + memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel)); + Multi_pxo_channel_switch.num_users = -1; + + // last time clicked the url button (so we don't have repeats) + Multi_pxo_ranking_last = -1.0f; + + // channel switching extra time delay stamp + Multi_pxo_switch_delay = -1; + + // our nick for this session + multi_pxo_underscore_nick(Player->callsign,Multi_pxo_nick); + + // clear the channel list + multi_pxo_clear_channels(); + + // clear the player list + multi_pxo_clear_players(); + + // initialize the chat system + multi_pxo_chat_init(); + + // initialize http + multi_pxo_ban_init(); + + // load the animation up + if (gr_screen.res == GR_1024) { + char anim_filename[32] = "2_"; + strcat(anim_filename, MULTI_PXO_ANIM_FNAME); + Multi_pxo_anim = anim_load(anim_filename); + + // if hi-res is not there, fallback to low + if (Multi_pxo_anim == NULL) { + Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME); + } + } else { + Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME); + } + + // clear the status text + multi_pxo_set_status_text(""); + + // last refresh time + Multi_pxo_channel_last_refresh = -1.0f; + + // server count last refresh time + Multi_pxo_channel_server_refresh = -1.0f; + + // set our mode + Multi_pxo_mode = MULTI_PXO_MODE_NORMAL; + + // init motd + multi_pxo_motd_init(); + + // make sure we autojoin + Multi_pxo_must_autojoin = 1; + + // clear all tracker channel related strings + memset(Multi_fs_tracker_channel,0,255); + memset(Multi_fs_tracker_filter,0,255); +} + +// do frame for the PXO screen +void multi_pxo_do() +{ + pxo_channel priv_chan; + + // run api stuff + if(Multi_pxo_connected) { + multi_pxo_api_process(); + } + + // process common stuff + multi_pxo_process_common(); + + switch(Multi_pxo_mode){ + // private channel join mode + case MULTI_PXO_MODE_PRIVATE: + switch(multi_pxo_priv_popup()){ + // still running + case 0: + break; + + // user hit "cancel" + case -1: + // return to normal mode + Multi_pxo_mode = MULTI_PXO_MODE_NORMAL; + break; + + // user hit "ok" + case 1 : + // setup some information + memset(&priv_chan,0,sizeof(pxo_channel)); + priv_chan.num_users = 0; + strcpy(priv_chan.name,Multi_pxo_priv_chan); + + // see if we know about this channel already + multi_pxo_join_channel(&priv_chan); + + // return to normal mode + Multi_pxo_mode = MULTI_PXO_MODE_NORMAL; + break; + } + break; + + // find player mode + case MULTI_PXO_MODE_FIND: + switch(multi_pxo_find_popup()){ + // still running + case 0: + break; + + // user hit "cancel" + case -1: + // return to normal mode + Multi_pxo_mode = MULTI_PXO_MODE_NORMAL; + break; + + // user hit "ok" + case 1 : + // return to normal mode + Multi_pxo_mode = MULTI_PXO_MODE_NORMAL; + + // if there is a valid channel name try and join it + if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){ + pxo_channel join; + + // setup the info + memset(&join,0,sizeof(pxo_channel)); + join.num_users = 0; + strcpy(join.name,Multi_pxo_find_channel); + + // try and join + multi_pxo_join_channel(&join); + } + break; + } + break; + // normal mode + case MULTI_PXO_MODE_NORMAL: + multi_pxo_do_normal(); + break; + } +} +//XSTR:ON +// close the PXO screen +void multi_pxo_close() +{ + // unload any bitmaps + bm_unload(Multi_pxo_bitmap); + bm_unload(Multi_pxo_com_bitmap); + + // record the last channel we were on, if any + memset(Multi_fs_tracker_channel,0,255); + memset(Multi_fs_tracker_filter,0,255); + if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){ + // channel name + strcpy(Multi_fs_tracker_channel,Multi_pxo_channel_current.name); + + // filter name + strcpy(Multi_fs_tracker_filter,Multi_pxo_channel_current.name); + } + + // disconnect from the server + DisconnectFromChatServer(); + Multi_pxo_connected = 0; + + // unload the animation + anim_release_all_instances(GS_STATE_PXO); + Multi_pxo_anim_instance = NULL; + if(Multi_pxo_anim != NULL){ + anim_free(Multi_pxo_anim); + Multi_pxo_anim = NULL; + } + + // unload the palette for this screen + multi_pxo_unload_palette(); + + // destroy the UI_WINDOW + Multi_pxo_window.destroy(); + + // clear the channel list + multi_pxo_clear_channels(); + + // close the chat system + multi_pxo_chat_free(); + + // close http stuff + multi_pxo_ban_close(); +} + +// run normally (no popups) +void multi_pxo_do_normal() +{ + int validate_code; + int k = Multi_pxo_window.process(); + + // if the animation isn't playing, start it up + if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){ + anim_play_struct aps; + + // fire up the animation + anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y); + aps.screen_id = GS_STATE_PXO; + aps.framerate_independent = 1; + aps.looped = 1; + Multi_pxo_anim_instance = anim_play(&aps); + } + + // process any keypresses + switch(k){ + case SDLK_ESCAPE: + gamesnd_play_iface(SND_USER_SELECT); + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + } + + // check for button presses + multi_pxo_check_buttons(); + + // if we're not in a chatroom, disable and hide the chat input box + if(!ON_CHANNEL()){ + Multi_pxo_chat_input.hide(); + Multi_pxo_chat_input.disable(); + } else { + Multi_pxo_chat_input.enable(); + Multi_pxo_chat_input.unhide(); + } + + // blit everything + multi_pxo_blit_all(); + + // flip the page + gr_flip(); + + // verify version # now (only once per Freespace instance) + if(Multi_pxo_must_verify_version){ + switch(multi_update_gobaby()){ + // everything is cool. Move along + case MULTI_UPDATE_CONTINUE: + Multi_pxo_must_verify_version = 0; + break; + + // go back to the main menu + case MULTI_UPDATE_MAIN_MENU: + gameseq_post_event(GS_EVENT_MAIN_MENU); + + // unset these so we don't do anything else PXO related + Multi_pxo_must_validate = 0; + Multi_pxo_must_connect = 0; + break; + + // freespace will be shutting down shortly + case MULTI_UPDATE_SHUTTING_DOWN: + return; + } + } + + // if we need to get tracker info for ourselves, do so + if(Multi_pxo_must_validate){ + // initialize the master tracker API for Freespace + multi_fs_tracker_init(); + + // validate the current player with the master tracker (will create the pilot on the MT if necessary) + validate_code = multi_fs_tracker_validate(0); + if(validate_code != 1){ + // show an error popup if it failed (not cancelled by the user) + if (validate_code == 0) { + switch (popup(PF_USE_AFFIRMATIVE_ICON | PF_WEB_CURSOR_1 | PF_WEB_CURSOR_2, 3, POPUP_CANCEL,XSTR("&Create Acct",936), XSTR("&Verify Acct",937), XSTR("PXO Login not accepted. You may visit the Parallax Online website to create or verify your login. Or you may click Cancel to play without using the Parallax Online service. (You may switch back to Parallax Online from the Options Menu under the Multi tab.)",938))) { + case 0: + nprintf(("Network","PXO CANCEL\n")); + + // flip his "pxo" bit temporarily and push him to the join game screen + Multi_options_g.pxo = 0; + // Net_game_tcp_mode = NET_TCP; + gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME); + break; + + case 1: + nprintf(("Network","PXO CREATE\n")); + // fire up the given URL + multi_pxo_url(Multi_options_g.pxo_create_url); + break; + + case 2: + nprintf(("Network","PXO VERIFY\n")); + // fire up the given URL + multi_pxo_url(Multi_options_g.pxo_verify_url); + break; + } + } + + // go back to the main hall + gameseq_post_event(GS_EVENT_MAIN_MENU); + + Multi_pxo_must_connect = 0; + Multi_pxo_must_validate = 0; + } + // now we have to conenct to PXO + else { + Multi_pxo_must_connect = 1; + Multi_pxo_must_validate = 0; + } + } + + // if we need to connect, do so now + if(Multi_pxo_must_connect){ + // for now, just try once + Multi_pxo_connected = multi_pxo_connect(); + + // if we successfully connected, send a request for a list of channels on the server + if(Multi_pxo_connected){ + multi_pxo_get_channels(); + + // set our status + multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939)); + } else { + // set our status + multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940)); + } + + // no longer need to connect + Multi_pxo_must_connect = 0; + } +} + +// blit everything on the "normal" screen +void multi_pxo_blit_all() +{ + // draw the background, etc + gr_reset_clip(); + // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap); + int bmap = Multi_pxo_bitmap; + do { + int bmw = -1; + int bmh = -1; + if(bmap != -1){ + bm_get_info( bmap, &bmw, &bmh); + if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){ + gr_clear(); + } + } else { + gr_clear(); + } + } while(0); + if(Multi_pxo_bitmap != -1){ + gr_set_bitmap(Multi_pxo_bitmap); + gr_bitmap(0,0); + } + Multi_pxo_window.draw(); + + // display the channel list + multi_pxo_blit_channels(); + + // display the player list + multi_pxo_blit_players(); + + // blit the chat text + multi_pxo_chat_blit(); + + // blit the status text + multi_pxo_blit_status_text(); + + // blit and process the notification string + multi_pxo_notify_blit(); + + // any bitmap or info or whatever + multi_pxo_ban_draw(); + + // draw any motd stuff + multi_pxo_motd_maybe_blit(); + + // if we have a valid animation handle, play it + if(Multi_pxo_anim_instance != NULL){ + anim_render_all(GS_STATE_PXO,flFrametime); + } +} + +// process common stuff +void multi_pxo_process_common() +{ + // process the channel list (select, etc) + multi_pxo_process_channels(); + + // process the player list (select, etc) + multi_pxo_process_players(); + + // process chat controls + multi_pxo_chat_process(); + + // process http download details + multi_pxo_ban_process(); +} + +// get selected player information +void multi_pxo_get_data(char *name) +{ +} + +// handle being kicked +void multi_pxo_handle_kick() +{ + // remove ourselves from the room + memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel)); + Multi_pxo_channel_current.num_users = -1; + + // clear text + multi_pxo_chat_clear(); + + // clear the old player list + multi_pxo_clear_players(); + + // add a notification string + multi_pxo_notify_add(XSTR("You have been kicked",941)); +} + +// handle being disconnected +void multi_pxo_handle_disconnect() +{ + popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942)); + gameseq_post_event(GS_EVENT_MAIN_MENU); +} + +// return string2, which is the first substring of string 1 without a space +// it is safe to pass the same pointer for both parameters +void multi_pxo_strip_space(char *string1,char *string2) +{ + char midway[255]; + char *tok; + + // copy the original + strcpy(midway,string1); + tok = strtok(midway," "); + if(tok != NULL){ + strcpy(string2,tok); + } else { + strcpy(string2,""); + } +} + +// fire up the given URL +void multi_pxo_url(char *url) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + // execute the shell command + int r = (int) ShellExecute(NULL, NOX("open"), url, NULL, NULL, SW_SHOW); + if (r < 32) { + switch (r) { + case 0: + case ERROR_BAD_FORMAT: + case SE_ERR_ACCESSDENIED: + case SE_ERR_ASSOCINCOMPLETE: + case SE_ERR_DDEBUSY: + case SE_ERR_DDEFAIL: + case SE_ERR_DDETIMEOUT: + case SE_ERR_DLLNOTFOUND: + case SE_ERR_OOM: + case SE_ERR_SHARE: + case SE_ERR_NOASSOC: + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943)); + break; + } + } +#endif +} + +// load/set the palette +void multi_pxo_load_palette() +{ + // use the palette +#ifndef HARDWARE_ONLY + palette_use_bm_palette(Multi_pxo_palette); +#endif +} + +// unload the palette +void multi_pxo_unload_palette() +{ + // unload the palette if it exists + if(Multi_pxo_palette != -1){ + bm_release(Multi_pxo_palette); + Multi_pxo_palette = -1; + } +} + +// if we're currently on a private channel +int multi_pxo_on_private_channel() +{ + // if we're connected to a channel with the "+" symbol on front + if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){ + return 1; + } + + // otherwise return falos + return 0; +} + +// convert string 1 into string 2, substituting underscores for spaces +void multi_pxo_underscore_nick(char *string1,char *string2) +{ + char nick_temp[512]; + char *tok; + + // don't do anything if we have bogus string + if((string1 == NULL) || (string2 == NULL)){ + return; + } + + // copy the nickname + memset(nick_temp,0,512); + strcpy(nick_temp,string1); + + // get the first token + tok = strtok(nick_temp," "); + if(tok != NULL){ + strcpy(string2,tok); + + // get the next token + tok = strtok(NULL," "); + while(tok != NULL){ + if(tok != NULL){ + strcat(string2,"_"); + strcat(string2,tok); + } + + tok = strtok(NULL," "); + } + } else { + strcpy(string2,string1); + } +} + +// if the command is a potential "nick" command +int multi_pxo_is_nick_command(char *msg) +{ + char *tok; + char tmp[512]; + + // get the first token in the message + memset(tmp,0,512); + strcpy(tmp,msg); + tok = strtok(tmp," "); + if(tok == NULL){ + // can't be a nick message + return 0; + } + + return !SDL_strcasecmp(tok,NOX("/nick")); +} + +// check for button presses +void multi_pxo_check_buttons() +{ + int idx; + + // go through all buttons + for(idx=0;idxname)){ + // show the stats + multi_pxo_pinfo_show(); + } + // if we didn't get stats for this guy. + else { + memset(stats,0,255); + sprintf(stats,XSTR("Could not get stats for %s\n(May not be a registered pilot)",946),Multi_pxo_player_select->name); + popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats); + } + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case MULTI_PXO_RANKINGS: + // make sure he doesn't click it too many times + if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){ + gamesnd_play_iface(SND_USER_SELECT); + + // fire up the url + multi_pxo_url(Multi_options_g.pxo_rank_url); + + // mark the time down + Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds()); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case MULTI_PXO_MOTD: + // maybe fire up the pxo motd dialog + multi_pxo_motd_dialog(); + break; + } +} + +// condition function for popup_do_with_condition for connected to Parallax Online +int mpxo_failed = 0; +int multi_pxo_connect_do() +{ + int ret_code; + char id_string[255] = ""; + char ip_string[255] = ""; + + // if we already tried and failed, sit around until the user presses cancel + if(!mpxo_failed){ + // try and connect to the server + SDL_assert(Player); + + // build the tracker id string + memset(id_string, 0, 255); + sprintf(id_string, "%s %s", Multi_tracker_id_string, Player->callsign); + + // build the ip string + memset(ip_string, 0, 255); + sprintf(ip_string, "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT); + + // connect to the server + ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string); + + // give some time to the pxo api. + multi_pxo_api_process(); + + switch(ret_code){ + // already connected, return success + case -2: + return 10; + + // failed to connect, return fail + case -1 : + mpxo_failed = 1; + popup_change_text(XSTR("Failed to connect to Parallax Online!", 947)); + return 0; + + // connected, return success + case 1 : + return 10; + + // still connecting + case 0 : + return 0; + } + } + + return 0; +} + +// popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete +int multi_pxo_autojoin_do() +{ + pxo_channel last_channel; + + // if we need to autojoin, do so now + if(Multi_pxo_must_autojoin){ + Multi_pxo_must_autojoin = 0; + + // if we're supposed to be using a (valid) "last" channel, do so + if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){ + // setup the data + memset(&last_channel, 0, sizeof(pxo_channel)); + last_channel.num_users = 0; + strcpy(last_channel.name, Multi_pxo_channel_last); + + // join the channel + multi_pxo_join_channel(&last_channel); + + nprintf(("Network","PXO : using last channel\n")); + } else { + multi_pxo_autojoin(); + + nprintf(("Network","PXO : using autojoin channel\n")); + } + multi_pxo_get_channels(); + } + + // give some time to the pxo api. + multi_pxo_api_process(); + multi_pxo_process_common(); + + // next value is not -1 when actually switching channels, so keep processing by returning 0. + if ( SWITCHING_CHANNELS() ){ + return 0; + } + + // couldn't switch channel for some reason. bail out with -1 + if ( !ON_CHANNEL() ){ + return -1; + } + + // return success + return 1; +} + +// attempt to connect to Parallax Online, return success or fail +int multi_pxo_connect() +{ + char join_str[256]; + char join_fail_str[256]; + + // intiialize chat api + ChatInit(); + + // set us to "must autojoin" + Multi_pxo_must_autojoin = 1; + + // run the connect dialog/popup + mpxo_failed = 0; + if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){ + int rval; + + memset(join_str,0,256); + memset(join_fail_str,0,256); + // if we're going to use the "last" channel + if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){ + strcpy(join_str, XSTR("Joining last channel (",982)); + strcat(join_str, Multi_pxo_channel_last + 1); + strcat(join_str, ")"); + + strcpy(join_fail_str, XSTR("Unable to join last channel", 983)); + } else { + strcpy(join_str, XSTR("Autojoining public channel", 984)); + strcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985)); + } + + // once connected, we should do an autojoin before allowing the guy to continue. + rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str ); + if ( rval == 1 ) { + return 1; + } + + popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str); + } + + // otherwise disconnect just to be safe + DisconnectFromChatServer(); + gameseq_post_event(GS_EVENT_MAIN_MENU); + + // did not successfully connect + return 0; +} + +// run the networking functions for the PXO API +void multi_pxo_api_process() +{ + char *p; + char msg_str[512]; + Chat_command *cmd; + pxo_channel *lookup; + + // give some time to psnet + PSNET_TOP_LAYER_PROCESS(); + + // give some time to the game tracker API + IdleGameTracker(); + + // give some time to the user tracker API + PollPTrackNet(); + + // get any incoming text + do + { + p = GetChatText(); + if(p) + { + // process the chat line + multi_pxo_chat_process_incoming(p); + } + } while(p); + + // get any incoming channel list stuff + p = GetChannelList(); + if(p) + { + // nprintf(("Network","%s\n",p)); + multi_pxo_make_channels(p); + } + + // process any chat commands + cmd = GetChatCommand(); + while(cmd) + { + switch(cmd->command) + { + case CC_USER_JOINING: + // add a user, if he doesn't already exist + if(multi_pxo_find_player(cmd->data) == NULL){ + multi_pxo_add_player(cmd->data); + } + + // increase the player count + if(ON_CHANNEL()){ + lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels); + if(lookup != NULL){ + lookup->num_users++; + } + } + break; + + case CC_USER_LEAVING: + // delete a user + multi_pxo_del_player(cmd->data); + + // add a text message + memset(msg_str,0,512); + sprintf(msg_str, XSTR("*** %s has left", 950), cmd->data); + multi_pxo_chat_process_incoming(msg_str); + + // decrease the player count + if(ON_CHANNEL()){ + lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels); + if(lookup != NULL){ + lookup->num_users--; + } + } + break; + + case CC_DISCONNECTED: + multi_pxo_handle_disconnect(); + break; + + case CC_KICKED: + multi_pxo_handle_kick(); + break; + + case CC_NICKCHANGED: + // process a nick change + multi_pxo_process_nick_change(cmd->data); + break; + + case CC_YOURCHANNEL: + // copy the current channel info, and unset the switching status + memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel)); + Multi_pxo_channel_switch.num_users = -1; + + SetNewChatChannel(NULL); + + strcpy(Multi_pxo_channel_current.name,cmd->data); + + // if we don't already have this guy on the list, add him + pxo_channel *lookup; + lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels); + if(lookup == NULL){ + // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail + lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels); + } + + // set the user count to be 0 + if(lookup != NULL){ + lookup->num_users = 0; + } + + // set our "last" channel to be this one + strcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name); + + // refresh current channel server count + multi_pxo_channel_refresh_current(); + + // clear the chat area + // multi_pxo_chat_clear(); + break; + + default: + Int3(); + } + + cmd = GetChatCommand(); + } + + // handle any processing details if we're currently trying to join a channel + multi_pxo_handle_channel_change(); +} + +// process a "nick" change event +void multi_pxo_process_nick_change(char *data) +{ + char *from, *to; + player_list *lookup; + + // get the new string + from = strtok(data," "); + to = strtok(NULL,""); + if((from != NULL) && (to != NULL)){ + lookup = multi_pxo_find_player(from); + if(lookup != NULL){ + strcpy(lookup->name,to); + + // if this is also my nick, change it + if(!SDL_strcasecmp(Multi_pxo_nick,from)){ + strcpy(Multi_pxo_nick,to); + } + } + } +} + +// autojoin an appropriate channel +void multi_pxo_autojoin() +{ + pxo_channel sw; + + memset(&sw,0,sizeof(pxo_channel)); + sw.num_users = 0; + strcpy(sw.name,MULTI_PXO_AUTOJOIN_CHANNEL); + + // if we found a valid room, attempt to join it + multi_pxo_join_channel(&sw); +} + +// does the string match the "autojoin" prefic +int multi_pxo_is_autojoin(char *name) +{ + // check to see if the name is long enough + if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){ + return 0; + } + + // check to see if the first n chars match + return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX)); +} + +// called from the game tracker API - server count update for a channel +void multi_pxo_channel_count_update(char *name,int count) +{ + pxo_channel *lookup; + + // lookup the channel name on the normal list + lookup = NULL; + lookup = multi_pxo_find_channel(name,Multi_pxo_channels); + if(lookup != NULL){ + lookup->num_servers = (ushort)count; + + nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count)); + } +} + +// status bar stuff ----------------------------------------------- + +// set the status text +void multi_pxo_set_status_text(const char *txt) +{ + // copy in the text + memset(Multi_pxo_status_text, 0, 255); + strncpy(Multi_pxo_status_text, txt, 254); + + // make sure it fits properly + gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]); +} + +// blit the status text +void multi_pxo_blit_status_text() +{ + int w; + + // center and draw the text + if(strlen(Multi_pxo_status_text)) { + gr_set_color_fast(&Color_bright); + gr_get_string_size(&w, NULL, Multi_pxo_status_text); + gr_string(Multi_pxo_status_coords[gr_screen.res][0] + ((Multi_pxo_status_coords[gr_screen.res][2] - w)/2), Multi_pxo_status_coords[gr_screen.res][1], Multi_pxo_status_text); + } +} + + +// channel related stuff ------------------------------------------- + +// get a list of channels on the server +void multi_pxo_get_channels() +{ + SendChatString(NOX("/list")); +} + +// clear the old channel list +void multi_pxo_clear_channels() +{ + pxo_channel *moveup,*backup; + + // only clear a non-null list + if(Multi_pxo_channels != NULL){ + // otherwise + moveup = Multi_pxo_channels; + backup = NULL; + if(moveup != NULL){ + do { + backup = moveup; + moveup = moveup->next; + + // free the struct itself + free(backup); + backup = NULL; + } while(moveup != Multi_pxo_channels); + Multi_pxo_channels = NULL; + } + + // head of the list of available channels + Multi_pxo_channels = NULL; + Multi_pxo_channel_count = 0; + + // item we're going to start displaying at + Multi_pxo_channel_start = NULL; + Multi_pxo_channel_start_index = -1; + + // items we've currently got selected + Multi_pxo_channel_select = NULL; + } +} + +// parse the input string and make a list of new channels +void multi_pxo_make_channels(char *chan_str) +{ + char *name_tok,*user_tok,*desc_tok; + pxo_channel *res; + pxo_channel *lookup; + int num_users; + + nprintf(("Network","Making some channels!\n")); + + // clear the channel list + // multi_pxo_clear_channels(); + + // set the last get time + Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds()); + + name_tok = strtok(chan_str," "); + if(name_tok == NULL){ + return; + } + name_tok += 1; + do { + // parse the user count token + user_tok = strtok(NULL," "); + + // parse the channel description token + desc_tok = strtok(NULL,"$"); + + // something invalid in the data, return here..... + if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){ + return; + } + + // get the # of users + num_users = 0; + num_users = (ubyte)atoi(user_tok); + + // if the # of users is > 0, or its not an autojoin, place it on the display list + if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){ + // see if it exists already, and if so, just update the user count + lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels); + + if(lookup != NULL){ + lookup->num_users = (short)num_users; + } + // add the channel + else { + res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels); + if(res != NULL){ + //Multi_pxo_channel_count++; + res->num_users = (short)num_users; + strcpy(res->desc,desc_tok); + } + } + } + + // get the next name token + name_tok = strtok(NULL," "); + } while(name_tok != NULL); + + // if we need to autojoin, do so now + //if(Multi_pxo_must_autojoin){ + // Multi_pxo_must_autojoin = 0; + // + // multi_pxo_autojoin(); + //} + + // refresh channels + multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951)); + + // if we haven't refreshed server counts yet, do it now + if(Multi_pxo_channel_server_refresh < 0.0f){ + multi_pxo_channel_refresh_servers(); + } + + // if we don't already have this guy on the list, add him + if(ON_CHANNEL()){ + pxo_channel *lookup; + lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels); + if(lookup == NULL){ + // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail + multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels); + } + } +} + +// create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail +pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list) +{ + pxo_channel *new_channel; + + // try and allocate a new pxo_channel struct + new_channel = (pxo_channel *)malloc(sizeof(pxo_channel)); + if ( new_channel == NULL ) { + nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n")); + return NULL; + } + memset(new_channel,0,sizeof(pxo_channel)); + // try and allocate a string for the channel name + strncpy(new_channel->name,name,MAX_CHANNEL_NAME_LEN); + + // insert it on the list + if ( *list != NULL ) { + new_channel->next = (*list)->next; + new_channel->next->prev = new_channel; + (*list)->next = new_channel; + new_channel->prev = *list; + } else { + *list = new_channel; + (*list)->next = (*list)->prev = *list; + } + + Multi_pxo_channel_count++; + return new_channel; +} + +// lookup a channel with the specified name +pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list) +{ + pxo_channel *moveup; + + // look the sucker up + moveup = list; + if(moveup == NULL){ + return NULL; + } + do { + if(!SDL_strcasecmp(name,moveup->name)){ + return moveup; + } + + moveup = moveup->next; + } while((moveup != list) && (moveup != NULL)); + + return NULL; +} + +// process the channel list (select, etc) +void multi_pxo_process_channels() +{ + int item_index,my; + int idx; + + // if we don't have a start item, but the list is non-null + if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){ + Multi_pxo_channel_start = Multi_pxo_channels; + Multi_pxo_channel_start_index = 0; + } + + // if we don't have a selected item, but the list is non-null + if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){ + Multi_pxo_channel_select = Multi_pxo_channels; + + // set the text + multi_pxo_set_status_text(Multi_pxo_channel_select->desc); + } + + // if the "switch" delay timestamp is set, see if it has expired + if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){ + Multi_pxo_switch_delay = -1; + } + + // see if we have a mouse click on the channel region + if(Multi_pxo_channel_button.pressed()){ + Multi_pxo_channel_button.get_mouse_pos(NULL,&my); + + // index from the top + item_index = my / 10; + + // select the item if possible + if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){ + Multi_pxo_channel_select = Multi_pxo_channel_start; + for(idx=0;idxnext; + } + + // set the text + multi_pxo_set_status_text(Multi_pxo_channel_select->desc); + } + } + + // last refresh time + if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){ + // refresh channels + multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952)); + + // get a list of channels on the server (clear any old list as well) + multi_pxo_get_channels(); + + // refresh + Multi_pxo_channel_last_refresh = -1.0f; + + nprintf(("Network","Refreshing channels\n")); + } + + // if we haven't updated our server channel counts in a while, do so again + // last refresh time + if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){ + // refresh server counts + // multi_pxo_set_status_text("Refreshing Public Channel Server Counts"); + + // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW! + multi_pxo_channel_refresh_servers(); + } +} + +// send a request to refresh our channel server counts +void multi_pxo_channel_refresh_servers() +{ + pxo_channel *lookup; + filter_game_list_struct filter; + + // traverse the list of existing channels we know about and query the game tracker about them + lookup = Multi_pxo_channels; + if(lookup == NULL){ + return; + } + do { + if(strlen(lookup->name)){ + // copy in the info + memset(&filter,0,sizeof(filter_game_list_struct)); + strcpy(filter.channel,lookup->name); + + // send the request + RequestGameCountWithFilter(&filter); + } + + // next item + lookup = lookup->next; + } while((lookup != NULL) && (lookup != Multi_pxo_channels)); + + // record the time + Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds()); +} + +// refresh current channel server count +void multi_pxo_channel_refresh_current() +{ + // send a request for a server count on this channel + if(strlen(Multi_pxo_channel_current.name)){ + // fill in the data + filter_game_list_struct filter; + memset(&filter,0,sizeof(filter_game_list_struct)); + strcpy(filter.channel,Multi_pxo_channel_current.name); + + // send the request + RequestGameCountWithFilter(&filter); + } +} + +// display the channel list +void multi_pxo_blit_channels() +{ + pxo_channel *moveup; + char chan_name[255]; + char chan_users[15]; + char chan_servers[15]; + int user_w,server_w; + int disp_count,y_start; + + // blit as many channels as we can + disp_count = 0; + y_start = Multi_pxo_chan_coords[gr_screen.res][1]; + moveup = Multi_pxo_channel_start; + if(moveup == NULL){ + return; + } + do { + // if this is the currently selected item, highlight it + if(moveup == Multi_pxo_channel_select){ + gr_set_color_fast(&Color_bright); + } + // otherwise draw it normally + else { + gr_set_color_fast(&Color_normal); + } + + // get the # of users on the channel + memset(chan_users, 0, 15); + sprintf(chan_users, "%d", moveup->num_users); + + // get the width of the user count string + gr_get_string_size(&user_w, NULL, chan_users); + + // get the # of servers on the channel + memset(chan_servers,0,15); + sprintf(chan_servers, "%d", moveup->num_servers); + + // get the width of the user count string + gr_get_string_size(&server_w, NULL, chan_servers); + + // make sure the name fits + memset(chan_name, 0, 10); + SDL_assert(moveup->name); + strcpy(chan_name,moveup->name); + gr_force_fit_string(chan_name, 254, Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_PLAYERS_COLUMN]); + + // blit the strings + gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1); + gr_string(Multi_pxo_chan_coords[gr_screen.res][0] + Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_PLAYERS_COLUMN], y_start, chan_users); + gr_set_color_fast(&Color_bright); + gr_string(Multi_pxo_chan_coords[gr_screen.res][0] + Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_GAMES_COLUMN], y_start, chan_servers); + + // increment the displayed count + disp_count++; + y_start += 10; + + // next item + moveup = moveup->next; + } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res])); +} + +// scroll channel list up +void multi_pxo_scroll_channels_up() +{ + // if we're already at the head of the list, do nothing + if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){ + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // otherwise move up one + Multi_pxo_channel_start = Multi_pxo_channel_start->prev; + Multi_pxo_channel_start_index--; + gamesnd_play_iface(SND_USER_SELECT); +} + +// scroll channel list down +void multi_pxo_scroll_channels_down() +{ + // if we're already at the tail of the list, do nothing + if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){ + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // if we can't scroll further without going past the end of the viewable list, don't + if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){ + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // otherwise move down one + Multi_pxo_channel_start = Multi_pxo_channel_start->next; + Multi_pxo_channel_start_index++; + gamesnd_play_iface(SND_USER_SELECT); +} + +// attempt to join a channel +void multi_pxo_join_channel(pxo_channel *chan) +{ + char switch_msg[256]; + + // if we're already on this channel, do nothing + if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){ + return; + } + + // if we're already trying to join a channel, do nothing + if(SWITCHING_CHANNELS()){ + return; + } + + // try and join the channel + switch(SetNewChatChannel(chan->name)){ + case -1 : + Int3(); + break; + + case 0 : + // decrement the count of our current channel + pxo_channel *lookup; + lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels); + if(lookup != NULL){ + lookup->num_users--; + } + + // set our current channel as none + memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel)); + Multi_pxo_channel_current.num_users = -1; + + multi_pxo_set_status_text(XSTR("Switching channels",953)); + + // copy the channel + memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel)); + + // clear the player list + multi_pxo_clear_players(); + + // display a line of text indicating that we're switching channels + memset(switch_msg,0,256); + + if(strlen(Multi_pxo_channel_switch.name) > 1){ + sprintf(switch_msg, "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1); + } else { + sprintf(switch_msg, "[Switching to channel %s]", Multi_pxo_channel_switch.name); + } + multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH); + break; + + case 1 : + Int3(); + } +} + +// handle any processing details if we're currently trying to join a channel +void multi_pxo_handle_channel_change() +{ + // if we're not switching channels, do nothing + if(!SWITCHING_CHANNELS()){ + return; + } + + // if we are, check the status + switch(SetNewChatChannel(NULL)){ + // failed to switch + case -1 : + // unset our switching struct + memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel)); + Multi_pxo_channel_switch.num_users = -1; + + // notify of error + multi_pxo_set_status_text(XSTR("No channel (error while switching)",954)); + break; + + // still switching + case 0: + break; + + // successfully changed + case 1: + // copy the current channel info, and unset the switching status + memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel)); + Multi_pxo_channel_switch.num_users = -1; + + // set our "last" channel + strcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name); + + // notify the user + multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951)); + + // if we don't already have this guy on the list, add him + pxo_channel *lookup; + lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels); + if(lookup == NULL){ + // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail + lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels); + } + + // set the user count to be 1 (just me) + if(lookup != NULL){ + lookup->num_users = 1; + } + + // clear the chat area + // multi_pxo_chat_clear(); + + // set the "switch" delay timestamp + Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME); + + // refresh current channel server count + multi_pxo_channel_refresh_current(); + break; + } +} + + +// player related stuff ------------------------------------------- + +// clear the old player list +void multi_pxo_clear_players() +{ + player_list *moveup,*backup; + + // if the list is null, don't free it up + if(Multi_pxo_players != NULL){ + // otherwise + moveup = Multi_pxo_players; + backup = NULL; + if(moveup != NULL){ + do { + backup = moveup; + moveup = moveup->next; + + // free the struct itself + free(backup); + backup = NULL; + } while(moveup != Multi_pxo_players); + Multi_pxo_players = NULL; + } + } + + Multi_pxo_player_start = NULL; + // Multi_pxo_player_start_index = -1; + Multi_pxo_player_select = NULL; + + // reset the slider + // Multi_pxo_player_slider.set_numberItems(0); + + // add a bunch of bogus players + /* + char str[50]; + for(int idx=0;idx<30;idx++){ + sprintf(str,"player%d",idx); + multi_pxo_add_player(str); + } + */ +} + +// create a new player with the given name and place it on the player list, return a pointer or NULL on fail +player_list *multi_pxo_add_player(char *name) +{ + player_list *new_player; + + // try and allocate a new player_list struct + new_player = (player_list *)malloc(sizeof(player_list)); + if ( new_player == NULL ) { + nprintf(("Network", "Cannot allocate space for new player_list structure\n")); + return NULL; + } + // try and allocate a string for the channel name + strncpy(new_player->name, name, MAX_PLAYER_NAME_LEN); + + // insert it on the list + if ( Multi_pxo_players != NULL ) { + new_player->next = Multi_pxo_players->next; + new_player->next->prev = new_player; + Multi_pxo_players->next = new_player; + new_player->prev = Multi_pxo_players; + } else { + Multi_pxo_players = new_player; + Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players; + } + + // increment the start position + if(Multi_pxo_player_start != NULL){ + // Multi_pxo_player_start_index++; + } + + // new player + Multi_pxo_player_count++; + + // update the count on the slider + // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count); + + return new_player; +} + +// remove a player with the given name +void multi_pxo_del_player(char *name) +{ + player_list *lookup; + + // try and find this guy + lookup = Multi_pxo_players; + if(lookup == NULL){ + return; + } + do { + // if we found a match, delete it + if(!SDL_strcasecmp(name,lookup->name)){ + // if this is the only item on the list, free stuff up + if(lookup->next == lookup){ + SDL_assert(lookup == Multi_pxo_players); + free(lookup); + Multi_pxo_players = NULL; + multi_pxo_clear_players(); + } + // otherwise, just delete it + else { + lookup->next->prev = lookup->prev; + lookup->prev->next = lookup->next; + + // if this was our selected item, unselect it + if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){ + Multi_pxo_player_select = Multi_pxo_player_select->next; + } + + // if this was our point to start viewing from, select another + if(lookup == Multi_pxo_player_start){ + // if this is the head of the list, move up one + if(Multi_pxo_players == lookup){ + Multi_pxo_player_start = Multi_pxo_player_start->next; + // Multi_pxo_player_start_index = 0; + } + // otherwise move back + else { + Multi_pxo_player_start = Multi_pxo_player_start->prev; + // Multi_pxo_player_start_index++; + } + } + + // if this is the head of the list, move it up + if(lookup == Multi_pxo_players){ + Multi_pxo_players = Multi_pxo_players->next; + } + + // free the item up + lookup->next = NULL; + lookup->prev = NULL; + free(lookup); + } + + // new player + Multi_pxo_player_count--; + SDL_assert(Multi_pxo_player_count >= 0); + + // update the count on the slider + // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count); + // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index); + + // we're done now + return; + } + + // next item + lookup = lookup->next; + } while((lookup != NULL) && (lookup != Multi_pxo_players)); +} + +// try and find a player with the given name, return a pointer to his entry (or NULL) +player_list *multi_pxo_find_player(char *name) +{ + player_list *lookup; + + // look through all players + lookup = Multi_pxo_players; + if(lookup == NULL){ + return NULL; + } + do { + if(!SDL_strcasecmp(name,lookup->name)){ + return lookup; + } + + lookup = lookup->next; + } while((lookup != NULL) && (lookup != Multi_pxo_players)); + + // return NULL + return NULL; +} + +// process the player list (select, etc) +void multi_pxo_process_players() +{ + int item_index,my; + player_list *lookup; + + // if we don't have a start item, but the list is non-null + if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){ + Multi_pxo_player_start = Multi_pxo_players; + // Multi_pxo_player_start_index = 0; + + // update the slider + // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index); + } + + // if we don't have a selected item, but the list is non-null + if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){ + Multi_pxo_player_select = Multi_pxo_players; + } + + // see if we have a mouse click on the channel region + if(Multi_pxo_player_button.pressed()){ + Multi_pxo_player_button.get_mouse_pos(NULL,&my); + + // index from the top + item_index = my / 10; + + // select the item if possible + lookup = Multi_pxo_player_start; + if(lookup == NULL){ + return; + } + // select item 0 + if(item_index == 0){ + Multi_pxo_player_select = Multi_pxo_player_start; + return; + } + do { + // move to the next item + lookup = lookup->next; + item_index--; + + // if this item is our guy + if((item_index == 0) && (lookup != Multi_pxo_players)){ + Multi_pxo_player_select = lookup; + return; + } + } while((lookup != Multi_pxo_players) && (item_index > 0)); + } +} + +// display the player list +void multi_pxo_blit_players() +{ + player_list *moveup; + char player_name[255]; + int disp_count,y_start; + + // blit as many channels as we can + disp_count = 0; + y_start = Multi_pxo_player_coords[gr_screen.res][1]; + moveup = Multi_pxo_player_start; + if(moveup == NULL){ + return; + } + do { + // if this is the currently selected item, highlight it + if(moveup == Multi_pxo_player_select){ + gr_set_color_fast(&Color_bright); + } + // otherwise draw it normally + else { + gr_set_color_fast(&Color_normal); + } + + // make sure the string fits + strcpy(player_name,moveup->name); + gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]); + + // blit the string + gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name); + + // increment the displayed count + disp_count++; + y_start += 10; + + // next item + moveup = moveup->next; + } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res])); +} + +// scroll player list up +void multi_pxo_scroll_players_up() +{ + // if we're already at the head of the list, do nothing + if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){ + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // otherwise move up one + Multi_pxo_player_start = Multi_pxo_player_start->prev; + // Multi_pxo_player_start_index--; + // SDL_assert(Multi_pxo_player_start_index >= 0); + + gamesnd_play_iface(SND_USER_SELECT); +} + +// scroll player list down +void multi_pxo_scroll_players_down() +{ + player_list *lookup; + int count = 0; + + // see if its okay to scroll down + lookup = Multi_pxo_player_start; + if(lookup == NULL ){ + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + count = 0; + while(lookup->next != Multi_pxo_players){ + lookup = lookup->next; + count++; + } + + // if we can move down + if(count >= Multi_pxo_max_player_display[gr_screen.res]){ + Multi_pxo_player_start = Multi_pxo_player_start->next; + + // Multi_pxo_player_start_index++; + + gamesnd_play_iface(SND_USER_SELECT); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + + +// chat text stuff ----------------------------------------- + +// initialize and create the chat text linked list +void multi_pxo_chat_init() +{ + int idx; + chat_line *new_line; + + // no chat lines + Multi_pxo_chat = NULL; + Multi_pxo_chat_add = NULL; + Multi_pxo_chat_start = NULL; + Multi_pxo_chat_start_index = -1; + + // create the lines in a non-circular doubly linked list + for(idx=0;idxprev = NULL; + new_line->next = NULL; + + // insert it into the (empty) list + if(Multi_pxo_chat == NULL){ + Multi_pxo_chat = new_line; + } + // insert it onto the (non-empty) list + else { + Multi_pxo_chat->prev = new_line; + new_line->next = Multi_pxo_chat; + Multi_pxo_chat = new_line; + } + } + + // start adding chat lines at the beginning of the list + Multi_pxo_chat_add = Multi_pxo_chat; +} + +// free up all chat list stuff +void multi_pxo_chat_free() +{ + chat_line *moveup, *backup; + + // free all items up + moveup = Multi_pxo_chat; + while(moveup != NULL){ + backup = moveup; + moveup = moveup->next; + + free(backup); + } + + // no chat lines + Multi_pxo_chat = NULL; + Multi_pxo_chat_add = NULL; + Multi_pxo_chat_start = NULL; + Multi_pxo_chat_start_index = -1; + Multi_pxo_chat_count = 0; + Multi_pxo_chat_slider.set_numberItems(0); +} + +// clear all lines of chat text in the chat area +void multi_pxo_chat_clear() +{ + chat_line *moveup; + + // clear the text in all the lines + moveup = Multi_pxo_chat; + while(moveup != NULL){ + memset(moveup->text,0,MAX_CHAT_LINE_LEN+1); + moveup = moveup->next; + } + + // how many chat lines we have + Multi_pxo_chat_count = 0; + + // start adding chat lines at the beginning of the list + Multi_pxo_chat_add = Multi_pxo_chat; +} + +// add a line of text +void multi_pxo_chat_add_line(char *txt, int mode) +{ + chat_line *temp; + + // copy in the text + SDL_assert(Multi_pxo_chat_add != NULL); + strncpy(Multi_pxo_chat_add->text, txt, MAX_CHAT_LINE_LEN); + Multi_pxo_chat_add->mode = mode; + + // if we're at the end of the list, move the front item down + if(Multi_pxo_chat_add->next == NULL) { + // store the new "head" of the list + temp = Multi_pxo_chat->next; + + // move the current head to the end of the list + Multi_pxo_chat_add->next = Multi_pxo_chat; + temp->prev = NULL; + Multi_pxo_chat->prev = Multi_pxo_chat_add; + Multi_pxo_chat->next = NULL; + + // reset the head of the list + Multi_pxo_chat = temp; + + // set the new add line + Multi_pxo_chat_add = Multi_pxo_chat_add->next; + memset(Multi_pxo_chat_add->text, 0, MAX_CHAT_LINE_LEN+1); + Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL; + } + // if we're not at the end of the list, just move up by one + else { + // set the new add line + Multi_pxo_chat_add = Multi_pxo_chat_add->next; + } + + // if we've reached max chat lines, don't increment + if(Multi_pxo_chat_count < MAX_CHAT_LINES) { + Multi_pxo_chat_count++; + } + + // set the count + Multi_pxo_chat_slider.set_numberItems(Multi_pxo_chat_count > Multi_pxo_max_chat_display[gr_screen.res] ? Multi_pxo_chat_count - Multi_pxo_max_chat_display[gr_screen.res] : 0, 0); // the 0 means don't reset + + // force the position, in case we arent at the bottom of the list + + + // move to the bottom of the chat area + /* + if(from_self){ + multi_pxo_goto_bottom(); + } + else { + // if we have more than the # of lines displayable + if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){ + } + multi_pxo_goto_bottom(); + } + */ + multi_pxo_goto_bottom(); +} + +// process an incoming line of text +void multi_pxo_chat_process_incoming(const char *txt,int mode) +{ + char msg_total[512],line[512]; + int n_lines,idx; + int n_chars[20]; + char *p_str[20]; // the initial line (unindented) + const char *priv_ptr = NULL; + + // filter out "has left" channel messages, when switching channels + if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) && + multi_pxo_chat_is_left_message(txt)){ + return; + } + + // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL + priv_ptr = multi_pxo_chat_is_private(txt); + if(priv_ptr != NULL){ + strcpy(msg_total, priv_ptr); + } else { + strcpy(msg_total, txt); + } + + // determine what mode to display this text in + + // if this is private chat + if(priv_ptr != NULL){ + mode = CHAT_MODE_PRIVATE; + } + // all other chat + else { + // if this is a server message + if(multi_pxo_is_server_text(txt)){ + mode = CHAT_MODE_SERVER; + } + // if this is a MOTD + else if(multi_pxo_is_motd_text(txt)){ + // mode = CHAT_MODE_MOTD; + // stuff the motd + multi_pxo_motd_add_text(txt); + return; + } + // if this is the end of motd text + else if(multi_pxo_is_end_of_motd_text(txt)){ + multi_pxo_set_end_of_motd(); + return; + } + } + + // split the text up into as many lines as necessary + n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3); + SDL_assert((n_lines != -1) && (n_lines <= 20)); + if((n_lines < 0) || (n_lines > 20)) { + return; + } + + // if the string fits on one line + if(n_lines == 1) { + multi_pxo_chat_add_line(msg_total,mode); + + // don't pad with extra spaces if its from the server + /* + if(mode != CHAT_MODE_SERVER){ + multi_pxo_chat_add_line("",CHAT_MODE_NORMAL); + } + */ + } + // if the string was split into multiple lines + else { + // add the first line + memcpy(line,p_str[0],n_chars[0]); + line[n_chars[0]] = '\0'; + multi_pxo_chat_add_line(line,mode); + + // copy the rest of the lines + for(idx=1; idx 1){ + sprintf(title, XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ on ]] + } else { + sprintf(title, XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ on ]] + } + } else { + strcpy(title,XSTR("Parallax Online - No Channel", 956)); + } + gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10); + gr_get_string_size(&token_width,NULL,title); + gr_set_color_fast(&Color_normal); + gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + ((Multi_pxo_chat_coords[gr_screen.res][2] - token_width)/2), Multi_pxo_chat_title_y[gr_screen.res], title); + + // blit all active lines of text + moveup = Multi_pxo_chat_start; + disp_count = 0; + y_start = Multi_pxo_chat_coords[gr_screen.res][1]; + while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){ + switch(moveup->mode){ + // if this is text from the server, display it all "bright" + case CHAT_MODE_SERVER: + gr_set_color_fast(&Color_bright); + gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text); + break; + + // if this is motd, display it all "bright" + case CHAT_MODE_MOTD: + gr_set_color_fast(&Color_bright_white); + gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text); + break; + + // normal mode, just highlight the server + case CHAT_MODE_PRIVATE: + case CHAT_MODE_NORMAL: + strcpy(piece,moveup->text); + tok = strtok(piece," "); + if(tok != NULL){ + // get the width of just the first "piece" + gr_get_string_size(&token_width, NULL, tok); + + // draw it brightly + gr_set_color_fast(&Color_bright); + gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok); + + // draw the rest of the string normally + tok = strtok(NULL,""); + if(tok != NULL){ + gr_set_color_fast(&Color_normal); + gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok); + } + } + break; + + // carry mode, display with no highlight + case CHAT_MODE_CARRY: + gr_set_color_fast(&Color_normal); + gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text); + break; + + // "switching channels mode", display it bright + case CHAT_MODE_CHANNEL_SWITCH: + gr_set_color_fast(&Color_bright); + gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text); + break; + } + + // next chat line + moveup = moveup->next; + disp_count++; + y_start += 10; + } + + if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) { + Can_scroll_down = 1; + } else { + Can_scroll_down = 0; + } +} + +// scroll to the very bottom of the chat area +void multi_pxo_goto_bottom() +{ + chat_line *backup; + int idx; + + if (Multi_pxo_chat == NULL) { + return; + } + + // if we have less than the displayable amount of lines, do nothing + if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){ + Multi_pxo_chat_start = Multi_pxo_chat; + + // nothing to do for the slider + Multi_pxo_chat_slider.set_numberItems(0); + return; + } + + if (!Can_scroll_down) + { + // otherwise move back the right # of items + backup = Multi_pxo_chat_add; + for(idx=0; idxprev != NULL); + backup = backup->prev; + } + + Multi_pxo_chat_start = backup; + + // fixup the start index + multi_pxo_chat_adjust_start(); + } +} + +// scroll the text up +void multi_pxo_scroll_chat_up() +{ + // if we're already at the top of the list, don't do anything + if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // otherwise move up one + Multi_pxo_chat_start = Multi_pxo_chat_start->prev; + + multi_pxo_chat_adjust_start(); + + gamesnd_play_iface(SND_USER_SELECT); +} + +// returns 1 if we can scroll down, 0 otherwise +int multi_pxo_can_scroll_down() +{ + chat_line *lookup; + int count = 0; + + // see if its okay to scroll down + lookup = Multi_pxo_chat_start; + if (lookup == NULL) { + // gamesnd_play_iface(SND_GENERAL_FAIL); + return 0; + } + count = 0; + while (lookup != Multi_pxo_chat_add) { + lookup = lookup->next; + count++; + } + + // check if we can move down, return accordingly + if (count > Multi_pxo_max_chat_display[gr_screen.res]) { + return 1; + } else { + return 0; + } +} + +// scroll the text down +void multi_pxo_scroll_chat_down() +{ + // if we can move down + if (multi_pxo_can_scroll_down()) { + Multi_pxo_chat_start = Multi_pxo_chat_start->next; + multi_pxo_chat_adjust_start(); + gamesnd_play_iface(SND_USER_SELECT); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// process chat controls +void multi_pxo_chat_process() +{ + char *remainder = NULL; + const char *result = NULL; + char msg[512]; + int msg_pixel_width; + + // if the chat line is getting too long, fire off the message, putting the last + // word on the next input line. + memset(msg, 0, 512); + Multi_pxo_chat_input.get_text(msg); + + // determine if the width of the string in pixels is > than the inputbox width -- if so, + // then send the message + gr_get_string_size(&msg_pixel_width, NULL, msg); + // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) { + if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) { + remainder = strrchr(msg, ' '); + if ( remainder ) { + *remainder = '\0'; + remainder++; + } + + // if we're connected to a channel, send the chat to the server + if(ON_CHANNEL()){ + result = SendChatString(msg,1); + if(result != NULL){ + multi_pxo_chat_process_incoming(result); + } + + // display any remainder of text on the next line + Multi_pxo_chat_input.set_text( remainder ? remainder : "" ); + } else { + Multi_pxo_chat_input.set_text(""); + } + } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) { + // tack on the null terminator in the boundary case + int x = strlen(msg); + if(x >= MAX_CHAT_LINE_LEN){ + msg[MAX_CHAT_LINE_LEN-1] = '\0'; + } + + // ignore "/nick" commands + if(multi_pxo_is_nick_command(msg)){ + Multi_pxo_chat_input.set_text(""); + return; + } + + // send the chat to the server + // if we're connected to a channel, send the chat to the server + if(ON_CHANNEL()){ + result = SendChatString(msg,1); + if(result != NULL){ + multi_pxo_chat_process_incoming(result); + } + + // display any remainder of text on the next line + Multi_pxo_chat_input.set_text( remainder ? remainder : "" ); + } else { + Multi_pxo_chat_input.set_text(""); + } + } +} + +// if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL +//XSTR:OFF + +// NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST +// PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!! +#define PMSG_FROM "private message from " +#define PMSG_TO "private message to " +const char *multi_pxo_chat_is_private(const char *txt) +{ + // quick check + if( strlen(txt) > strlen( PMSG_FROM ) ){ + // otherwise do a comparison + if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){ + return &txt[strlen( PMSG_FROM )]; + } + } + + // quick check + if(strlen(txt) > strlen( PMSG_TO )){ + // otherwise do a comparison + if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){ + return &txt[strlen(PMSG_TO)]; + } + } + + return NULL; +} +//XSTR:ON + +// if the text came from the server +int multi_pxo_is_server_text(const char *txt) +{ + // if the message is prefaced by a *** + if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){ + return 1; + } + + return 0; +} + +// if the text is message of the day text +int multi_pxo_is_motd_text(const char *txt) +{ + // if we're not on a channel, and this is not a channel switching message assume its coming from a server + if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){ + return 1; + } + + return 0; +} + +// if the text is the end of motd text +int multi_pxo_is_end_of_motd_text(const char *txt) +{ + // if we're not on a channel, and this is not a channel switching message assume its coming from a server + if((strlen(txt) >= strlen(PXO_CHAT_END_OF_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_END_OF_MOTD_PREFIX, strlen(PXO_CHAT_END_OF_MOTD_PREFIX))){ + return 1; + } + + return 0; +} + +// if the text is a "has left message" from the server +int multi_pxo_chat_is_left_message(const char *txt) +{ + char last_portion[100]; + + // if the text is not server text + if(!multi_pxo_is_server_text(txt)){ + return 0; + } + + // check to see if the last portion is the correct wording + memset(last_portion, 0, 100); + if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){ + return 1; + } + + // check the end of the line + return 0; +} + +// recalculate the chat start index, and adjust the slider properly +void multi_pxo_chat_adjust_start() +{ + chat_line *moveup; + + // if we have no chat + if (Multi_pxo_chat == NULL) { + Multi_pxo_chat_start_index = -1; + return; + } + + // traverse + Multi_pxo_chat_start_index = 0; + moveup = Multi_pxo_chat; + while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){ + Multi_pxo_chat_start_index++; + moveup = moveup->next; + } + + // set the slider index + Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index); +} + +// motd stuff --------------------------------------------------------- + +// initialize motd when going into this screen +void multi_pxo_motd_init() +{ + // zero the motd string + strcpy(Pxo_motd, ""); + + // haven't gotten it yet + Pxo_motd_end = 0; + + // haven't read it yet either + Pxo_motd_read = 0; +} + +// set the motd text +void multi_pxo_motd_add_text(const char *text) +{ + int cur_len = strlen(Pxo_motd); + int new_len; + + // sanity + if(text == NULL){ + return; + } + + // make sure its motd text + SDL_assert(multi_pxo_is_motd_text(text)); + if(!multi_pxo_is_motd_text(text)){ + return; + } + + // if its a 0 line motd + if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){ + return; + } + + // add text to the motd + new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1; + if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){ + strcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1); + strcat(Pxo_motd, "\n"); + mprintf(("MOTD ADD : %s\n", Pxo_motd)); + } +} + +// set end of motd +void multi_pxo_set_end_of_motd() +{ + int blink = 1; + + Pxo_motd_end = 1; + mprintf(("MOTD ALL : %s\n", Pxo_motd)); + + Pxo_motd_read = 0; + + // do we have an old MOTD file laying around? If so, read it in and see if its the same + unsigned int old_chksum; + unsigned long new_chksum; + + // checksum the current motd + new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd)); + + // checksum the old motd if its lying around + CFILE *in = cfopen("oldmotd.txt", "rb"); + if(in != NULL){ + // read the old checksum + cfread(&old_chksum, sizeof(old_chksum), 1, in); + cfclose(in); + + // same checksum? no blink + if(new_chksum == old_chksum){ + blink = 0; + } + } + + // write out the motd for next time + if(strlen(Pxo_motd)){ + CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA); + if(out != NULL){ + // write all the text + cfwrite(&new_chksum, sizeof(new_chksum), 1, out); + + // close the outfile + cfclose(out); + } + } + + // set the blink stamp + Pxo_motd_blink_stamp = -1; + if(blink){ + Pxo_motd_blink_on = 0; + if(!Pxo_motd_blinked_already){ + Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME); + Pxo_motd_blink_on = 1; + } + } + + Pxo_motd_blinked_already = 1; +} + +// display the motd dialog +void multi_pxo_motd_dialog() +{ + // mark the motd as read + Pxo_motd_read = 1; + + // simple popup, with a slider + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd); +} + +// call to maybe blink the motd button +void multi_pxo_motd_maybe_blit() +{ + // if we got the end of the motd, and he hasn't read it yet + if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){ + // if the timestamp elapsed, flip the blink flag + if(timestamp_elapsed(Pxo_motd_blink_stamp)){ + Pxo_motd_blink_on = !Pxo_motd_blink_on; + Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME); + } + + // draw properly + if(Pxo_motd_blink_on){ + Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2); + } + } +} + + +// common dialog stuff ------------------------------------------------ + +int Multi_pxo_searching = 0; + +// initialize the common dialog with the passed max input length +void multi_pxo_com_init(int input_len) +{ + int idx; + + // create the interface window + Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0); + Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]); + + // create the interface buttons + for(idx=0; idx 0){ + gr_set_color_fast(&Color_bright); + gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_top_text_coords[gr_screen.res][1], Multi_pxo_com_top_text); + } + if(strlen(Multi_pxo_com_middle_text) > 0){ + gr_set_color_fast(&Color_bright); + gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_middle_text_y[gr_screen.res], Multi_pxo_com_middle_text); + } + if(strlen(Multi_pxo_com_bottom_text) > 0){ + gr_set_color_fast(&Color_bright); + gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_bottom_text_y[gr_screen.res], Multi_pxo_com_bottom_text); + } +} + +// set the top text, shortening as necessary +void multi_pxo_com_set_top_text(const char *txt) +{ + if((txt != NULL) && strlen(txt)){ + strcpy(Multi_pxo_com_top_text,txt); + gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]); + } +} + +// set the middle text, shortening as necessary +void multi_pxo_com_set_middle_text(const char *txt) +{ + if((txt != NULL) && strlen(txt)){ + strcpy(Multi_pxo_com_middle_text,txt); + gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]); + } +} + +// set the bottom text, shortening as necessary +void multi_pxo_com_set_bottom_text(const char *txt) +{ + if((txt != NULL) && strlen(txt)){ + strcpy(Multi_pxo_com_bottom_text,txt); + gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]); + } +} + + +// private channel join stuff ----------------------------------------- + +// initialize the popup +void multi_pxo_priv_init() +{ + SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE); + + // initialize the common dialog with the passed max input length + multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN); + + // initialize the return code + Multi_pxo_priv_return_code = -1; + + // mark us as running + Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE; + + // set some text + multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961)); +} + +// close down the popup +void multi_pxo_priv_close() +{ + // close down the common dialog + multi_pxo_com_close(); + + // mark us as not running any more + Multi_pxo_mode = MULTI_PXO_MODE_NORMAL; +} + +// run the popup, 0 if still running, -1 if cancel, 1 if ok +int multi_pxo_priv_popup() +{ + int k; + + // if we're not already running, initialize stuff + if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){ + // intialize + multi_pxo_priv_init(); + + // return "still running" + return 0; + } + + k = Multi_pxo_com_window.process(); + + // process keypresses + switch(k){ + // like hitting the cancel button + case SDLK_ESCAPE: + Multi_pxo_priv_return_code = 0; + break; + } + + // process button presses + multi_pxo_priv_process_buttons(); + + // process the inputbox + multi_pxo_priv_process_input(); + + // blit the background + multi_pxo_blit_all(); + + // blit my stuff + gr_reset_clip(); + gr_set_bitmap(Multi_pxo_com_bitmap); + gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]); + Multi_pxo_com_window.draw(); + + // blit all text lines, top, middle, bottoms + multi_pxo_com_blit_text(); + + gr_flip(); + + // check the return code + switch(Multi_pxo_priv_return_code){ + // still in progress + case -1 : + return 0; + + // user hit cancel + case 0 : + multi_pxo_priv_close(); + return -1; + + // user hit ok + case 1 : + multi_pxo_priv_close(); + return 1; + } + + return 0; +} + +// process button presses +void multi_pxo_priv_process_buttons() +{ + int idx; + + // check all buttons + for(idx=0;idx 0){ + pxo_channel *lookup; + lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels); + + // if we couldn't find it, don't join + if(lookup != NULL){ + multi_pxo_join_channel(lookup); + } + } + return 1; + } + + return 0; +} + +// process button presses +void multi_pxo_find_process_buttons() +{ + int idx; + + // check all buttons + for(idx=0;idx 0){ + char search_text[512]; + + // put us in search mode + Multi_pxo_searching = 1; + + // look for the guy + GetChannelByUser(name_lookup); + + // set the top text + memset(search_text,0,512); + sprintf(search_text,XSTR("Searching for %s",963),name_lookup); + multi_pxo_com_set_top_text(search_text); + } + // clear everything + else { + memset(Multi_pxo_com_top_text,0,255); + } + } + } +} + +// process search mode if applicable +void multi_pxo_find_search_process() +{ + char *channel; + + // if we're not searching for anything, return + if(!Multi_pxo_searching){ + return; + } + + // otherwise check to see if we've found him + channel = GetChannelByUser(NULL); + + // if we've got a result, let the user know + if(channel){ + // if he couldn't be found + if(channel == (char *)-1){ + multi_pxo_com_set_middle_text(XSTR("User not found",964)); + strcpy(Multi_pxo_find_channel,""); + } else { + if(channel[0] == '*'){ + multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965)); + strcpy(Multi_pxo_find_channel,""); + } else { + char p_text[512]; + memset(p_text,0,512); + + // if this guy is on a public channel, display which one + if(channel[0] == '#'){ + sprintf(p_text,XSTR("Found %s on :",966),name_lookup); + + // display the results + multi_pxo_com_set_middle_text(p_text); + multi_pxo_com_set_bottom_text(channel+1); + + // mark down the channel name so we know where to find him + strcpy(Multi_pxo_find_channel,channel); + // strip out trailing whitespace + if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){ + Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0'; + } + } + // if this is a private channel + else if(channel[0] == '+'){ + sprintf(p_text,XSTR("Found %s on a private channel",967),name_lookup); + multi_pxo_com_set_middle_text(p_text); + + strcpy(Multi_pxo_find_channel,""); + } + } + } + + // unset search mode + Multi_pxo_searching = 0; + + // clear the inputbox + Multi_pxo_com_input.set_text(""); + } +} + + +// player info stuff ----------------------------------------- + +// popup conditional functions, returns 10 on successful get of stats +int multi_pxo_pinfo_cond() +{ + char *ret_string; + char temp_string[255]; + char *tok; + + // process common stuff + multi_pxo_process_common(); + + // run the networking functions for the PXO API + multi_pxo_api_process(); + + // process depending on what mode we're in + switch(Multi_pxo_retrieve_mode){ + // getting his player tracker id + case 0: + // if the thing is non-null, do something + ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name); + if(ret_string != NULL){ + // user not-online/not found + if(ret_string == (char *)-1){ + return 1; + } + + // user not a tracker pilot + if(!SDL_strcasecmp(ret_string,"-1")){ + return 1; + } + + // otherwise parse into his id and callsign + strcpy(temp_string,ret_string); + tok = strtok(temp_string," "); + + // get tracker id + if(tok != NULL){ + strcpy(Multi_pxo_retrieve_id,tok); + + // get the callsign + tok = strtok(NULL,""); + if(tok != NULL){ + strcpy(Multi_pxo_retrieve_name,tok); + } + // failure + else { + return 1; + } + } + // failure of some kind or another + else { + return 1; + } + + Multi_pxo_retrieve_mode = 1; + return 0; + } + break; + + // initial call to get his stats + case 1: + // change the popup text + popup_change_text(XSTR("Getting player stats",968)); + + // fill in the data + memset(&Multi_pxo_pinfo, 0, sizeof(vmt_freespace2_struct)); + strcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name); + strncpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, TRACKER_ID_LEN); + + // make the initial call to the API + GetFSPilotData((vmt_freespace2_struct*)0xffffffff,NULL,NULL,0); + if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){ + return 2; + } + // if the call went through, set the mode to 2 + else { + Multi_pxo_retrieve_mode = 2; + } + break; + + // busy retrieving his stats + case 2: + switch(GetFSPilotData(NULL,NULL,NULL,0)){ + // timeout, fail, cancel + case -1: + case 3: + case 2: + return 2; + + // got the data + case 1: + return 10; + + // still busy + case 0: + break; + } + + break; + } + + // return not done yet + return 0; +} + +// return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise +int multi_pxo_pinfo_get(char *name) +{ + // run the popup + Multi_pxo_retrieve_mode = 0; + strcpy(Multi_pxo_retrieve_name,name); + switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){ + // success + case 10 : + return 1; + + // failed to get his tracker id + case 1 : + return 0; + + // failed to get his stats + case 2 : + return 0; + } + + // we didn't get the stats + return 0; +} + +// fire up the stats view popup +void multi_pxo_pinfo_show() +{ + // initialize the popup + multi_pxo_pinfo_init(); + + // run the popup + do { + game_set_frametime(GS_STATE_PXO); + } while(!multi_pxo_pinfo_do()); + + // close down the popup + multi_pxo_pinfo_close(); +} + +// build the stats labels values +void multi_pxo_pinfo_build_vals() +{ + vmt_freespace2_struct *fs = &Multi_pxo_pinfo; + + // pilot name + memset(Multi_pxo_pinfo_vals[0],0,50); + strcpy(Multi_pxo_pinfo_vals[0],fs->pilot_name); + gr_force_fit_string(Multi_pxo_pinfo_vals[0], 49, Multi_pxo_pinfo_coords[gr_screen.res][2] - (Multi_pxo_pinfo_val_x[gr_screen.res] - Multi_pxo_pinfo_coords[gr_screen.res][0])); + + // rank + memset(Multi_pxo_pinfo_vals[1],0,50); + multi_sg_rank_build_name(Ranks[fs->rank].name,Multi_pxo_pinfo_vals[1]); + gr_force_fit_string(Multi_pxo_pinfo_vals[1], 49, Multi_pxo_pinfo_coords[gr_screen.res][2] - (Multi_pxo_pinfo_val_x[gr_screen.res] - Multi_pxo_pinfo_coords[gr_screen.res][0])); + + // kills + memset(Multi_pxo_pinfo_vals[2],0,50); + sprintf(Multi_pxo_pinfo_vals[2],"%d",fs->kill_count); + + // assists + memset(Multi_pxo_pinfo_vals[3],0,50); + sprintf(Multi_pxo_pinfo_vals[3],"%d",fs->assists); + + // friendly kills + memset(Multi_pxo_pinfo_vals[4],0,50); + sprintf(Multi_pxo_pinfo_vals[4],"%d",fs->kill_count - fs->kill_count_ok); + + // missions flown + memset(Multi_pxo_pinfo_vals[5],0,50); + sprintf(Multi_pxo_pinfo_vals[5],"%d",(int)fs->missions_flown); + + // flight time + memset(Multi_pxo_pinfo_vals[6],0,50); + game_format_time(fl2f((float)fs->flight_time),Multi_pxo_pinfo_vals[6]); + + // last flown + memset(Multi_pxo_pinfo_vals[7],0,50); + if(fs->last_flown == 0){ + strcpy(Multi_pxo_pinfo_vals[7],XSTR("No missions flown",970)); + } else { + tm *tmr = gmtime((time_t*)&fs->last_flown); + if(tmr != NULL){ + strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr); + } else { + strcpy(Multi_pxo_pinfo_vals[7], ""); + } + } + + // primary shots fired + memset(Multi_pxo_pinfo_vals[8],0,50); + sprintf(Multi_pxo_pinfo_vals[8],"%d",(int)fs->p_shots_fired); + + // primary shots hit + memset(Multi_pxo_pinfo_vals[9],0,50); + sprintf(Multi_pxo_pinfo_vals[9],"%d",(int)fs->p_shots_hit); + + // primary hit pct + memset(Multi_pxo_pinfo_vals[10],0,50); + if(fs->p_shots_fired > 0){ + sprintf(Multi_pxo_pinfo_vals[10],"%d%%",(int)((float)fs->p_shots_hit / (float)fs->p_shots_fired * 100.0f)); + } else { + strcpy(Multi_pxo_pinfo_vals[10],"0%"); + } + + // secondary shots fired + memset(Multi_pxo_pinfo_vals[11],0,50); + sprintf(Multi_pxo_pinfo_vals[11],"%d",(int)fs->s_shots_fired); + + // secondary shots hit + memset(Multi_pxo_pinfo_vals[12],0,50); + sprintf(Multi_pxo_pinfo_vals[12],"%d",(int)fs->s_shots_hit); + + // secondary hit pct + memset(Multi_pxo_pinfo_vals[13],0,50); + if(fs->s_shots_fired > 0){ + sprintf(Multi_pxo_pinfo_vals[13],"%d%%",(int)((float)fs->s_shots_hit / (float)fs->s_shots_fired * 100.0f)); + } else { + strcpy(Multi_pxo_pinfo_vals[13],"0%"); + } + + // primary friendly hits + memset(Multi_pxo_pinfo_vals[14],0,50); + sprintf(Multi_pxo_pinfo_vals[14],"%d",fs->p_bonehead_hits); + + // primary friendly hit % + memset(Multi_pxo_pinfo_vals[15],0,50); + if(fs->p_shots_hit > 0){ + sprintf(Multi_pxo_pinfo_vals[15],"%d%%",(int)((float)100.0f*((float)fs->p_bonehead_hits/(float)fs->p_shots_fired))); + } else { + strcpy(Multi_pxo_pinfo_vals[15],"0%"); + } + + // secondary friendly hits + memset(Multi_pxo_pinfo_vals[16],0,50); + sprintf(Multi_pxo_pinfo_vals[16],"%d",fs->s_bonehead_hits); + + // secondary friendly hit % + memset(Multi_pxo_pinfo_vals[17],0,50); + if(fs->s_shots_hit > 0){ + sprintf(Multi_pxo_pinfo_vals[17],"%d%%",(int)((float)100.0f*((float)fs->s_bonehead_hits/(float)fs->s_shots_fired))); + } else { + strcpy(Multi_pxo_pinfo_vals[17],"0%"); + } +} + +// initialize the popup +void multi_pxo_pinfo_init() +{ + int idx; + + // create the interface window + Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0); + Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]); + + Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]); + SDL_assert(Multi_pxo_pinfo_bitmap != -1); + + // create the interface buttons + for(idx=0; idxtext[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]); + if(cp->text[cp->num_lines] == NULL){ + break; + } + + // read in the next line + cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in); + + // skip to the next page if necessary + if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){ + Multi_pxo_help_num_pages++; + SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES); + if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){ + Multi_pxo_help_num_pages--; + break; + } + cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages]; + } + } + + // close the file + cfclose(in); + + // mark the help as having been loaded + // Multi_pxo_help_loaded = 1; +} + +// blit the current page +void multi_pxo_help_blit_page() +{ + int idx; + int start_pos; + int y_start; + help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur]; + + // blit each line + y_start = Multi_pxo_help_coords[gr_screen.res][1]; + for(idx=0;idxnum_lines;idx++){ + // if the first symbol is "@", highlight the line + if(cp->text[idx][0] == '@'){ + gr_set_color_fast(&Color_bright); + start_pos = 1; + } else { + gr_set_color_fast(&Color_normal); + start_pos = 0; + } + + // blit the line + gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos); + + // increment the y location + y_start += 10; + } +} + +// process button presses +void multi_pxo_help_process_buttons() +{ + int idx; + + // process all buttons + for(idx=0;idxIsFileError()){ + delete Multi_pxo_ban_get; + Multi_pxo_ban_get = NULL; + Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE; + break; + } + + // connecting, receiving + if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){ + break; + } + + // done! + if(Multi_pxo_ban_get->IsFileReceived()){ + delete Multi_pxo_ban_get; + Multi_pxo_ban_get = NULL; + Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP; + } + break; + + // start downloading files + case PXO_BAN_MODE_IMAGES_STARTUP: + // first thing - parse the banners file and pick a file + multi_pxo_ban_parse_banner_file(0); + + // if we have no active file, we're done + if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){ + Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE; + break; + } + + // if the file already exists, we're done + if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){ + Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE; + break; + } + + // otherwise try and download it + cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file); + // try creating the file get object + Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file); + + // bad + if(Multi_pxo_ban_get == NULL){ + Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE; + } + + // go to the downloading images mode + Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES; + break; + + // downloading files + case PXO_BAN_MODE_IMAGES: + // error + if(Multi_pxo_ban_get->IsFileError()){ + delete Multi_pxo_ban_get; + Multi_pxo_ban_get = NULL; + Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE; + break; + } + + // connecting, receiving + if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){ + break; + } + + // done! + if(Multi_pxo_ban_get->IsFileReceived()){ + delete Multi_pxo_ban_get; + Multi_pxo_ban_get = NULL; + Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE; + } + break; + + // done downloading - maybe load an image + case PXO_BAN_MODE_IMAGES_DONE: + // make sure we have a valid filename + // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0); + if(strlen(Multi_pxo_banner.ban_file) > 0){ + Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file); + } + + // now we're idle + Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE; + break; + + // idle (done with EVERYTHING) + case PXO_BAN_MODE_IDLE: + // if the banner button was clicked + if(Multi_pxo_ban_button.pressed()){ + multi_pxo_ban_clicked(); + } + break; + + case PXO_BAN_MODE_CHOOSE_RANDOM: + // first thing - parse the banners file and pick a file + multi_pxo_ban_parse_banner_file(1); + + Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE; + break; + } +} + +// close +void multi_pxo_ban_close() +{ + // if we have a currently active transfer + if(Multi_pxo_ban_get != NULL){ + Multi_pxo_ban_get->AbortGet(); + delete Multi_pxo_ban_get; + Multi_pxo_ban_get = NULL; + } + + // if we have a loaded bitmap, unload it + if(Multi_pxo_banner.ban_bitmap != -1){ + bm_unload(Multi_pxo_banner.ban_bitmap); + Multi_pxo_banner.ban_bitmap = -1; + } +} + +// parse the banners file and maybe fill in Multi_pxo_dl_file +void multi_pxo_ban_parse_banner_file(int choose_existing) +{ + char file_url[512] = ""; + char banners[10][512]; + char urls[10][512]; + int exists[10]; + int exist_count; + int num_banners, idx; + CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE); + + Multi_pxo_banner.ban_bitmap = -1; + strcpy(Multi_pxo_banner.ban_file, ""); + strcpy(Multi_pxo_banner.ban_file_url, ""); + strcpy(Multi_pxo_banner.ban_url, ""); + + // bad + if(in == NULL){ + return; + } + + // clear all strings + for(idx=0; idx<10; idx++){ + strcpy(banners[idx], ""); + strcpy(urls[idx], ""); + } + + // get the global banner url + if(cfgets(file_url, 254, in) == NULL){ + cfclose(in); + cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE); + return; + } + drop_leading_white_space(file_url); + drop_trailing_white_space(file_url); + + // otherwise read in + num_banners = 0; + while(num_banners < 10){ + // try and get the pcx + if(cfgets(banners[num_banners], 254, in) == NULL){ + break; + } + // try and get the url + if(cfgets(urls[num_banners], 254, in) == NULL){ + break; + } + + // strip off trailing and leading whitespace + drop_leading_white_space(banners[num_banners]); + drop_trailing_white_space(banners[num_banners]); + drop_leading_white_space(urls[num_banners]); + drop_trailing_white_space(urls[num_banners]); + + // got one + num_banners++; + } + + // close the file + cfclose(in); + + // no banners + if(num_banners <= 0){ + return; + } + + // if we're only selecting files which already exist (previously downloaded) + if(choose_existing){ + // non exist + for(idx=0; idx<10; idx++){ + exists[idx] = 0; + } + + // build a list of existing files + exist_count = 0; + for(idx=0; idx= exist_count){ + select = exist_count - 1; + } + if(select < 0){ + select = 0; + } + for(idx=0; idx= num_banners){ + idx = num_banners - 1; + } + if(idx < 0){ + idx = 0; + } + + // base filename + strncpy(Multi_pxo_banner.ban_file, banners[idx], MAX_FILENAME_LEN); + + // get the full file url + strncpy(Multi_pxo_banner.ban_file_url, file_url, MULTI_OPTIONS_STRING_LEN); + strncat(Multi_pxo_banner.ban_file_url, banners[idx], MULTI_OPTIONS_STRING_LEN); + + // url of where to go to when clicked + strncpy(Multi_pxo_banner.ban_url, urls[idx], MULTI_OPTIONS_STRING_LEN); + } + + // delete the banner config file + // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE); +} + +// any bitmap or info or whatever +void multi_pxo_ban_draw() +{ + // if we have a valid bitmap + if(Multi_pxo_banner.ban_bitmap >= 0){ + // if the mouse is over the banner button, highlight with a rectangle + if(Multi_pxo_ban_button.is_mouse_on()){ + gr_set_color_fast(&Color_bright_blue); + gr_rect(Pxo_ban_coords[gr_screen.res][0] - 1, Pxo_ban_coords[gr_screen.res][1] - 1, Pxo_ban_coords[gr_screen.res][2] + 2, Pxo_ban_coords[gr_screen.res][3] + 2); + } + + // draw the bitmap itself + gr_set_bitmap(Multi_pxo_banner.ban_bitmap); + gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]); + } +} + +// called when the URL button is clicked +void multi_pxo_ban_clicked() +{ + // if we have a valid bitmap and URL, launch the URL + if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){ + multi_pxo_url(Multi_pxo_banner.ban_url); + } +} + diff --git a/src/network/multi_sw.cpp b/src/network/multi_sw.cpp new file mode 100644 index 0000000..aef25c2 --- /dev/null +++ b/src/network/multi_sw.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +/* + * $Logfile: /Freespace2/code/Network/multi_sw.cpp $ + * $Revision: 10 $ + * $Date: 9/13/99 11:30a $ + * $Author: Dave $ + * + * $Log: /Freespace2/code/Network/multi_sw.cpp $ + * + * 10 9/13/99 11:30a Dave + * Added checkboxes and functionality for disabling PXO banners as well as + * disabling d3d zbuffer biasing. + * + * 9 9/04/99 8:00p Dave + * Fixed up 1024 and 32 bit movie support. + * + * 8 8/25/99 4:38p Dave + * Updated PXO stuff. Make squad war report stuff much more nicely. + * + * 7 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 6 2/24/99 2:25p Dave + * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn + * bug for dogfight more. + * + * 5 2/19/99 2:55p Dave + * Temporary checking to report the winner of a squad war match. + * + * 4 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 3 2/12/99 6:16p Dave + * Pre-mission Squad War code is 95% done. + * + * 2 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * + * $NoKeywords: $ + */ + + +#include "systemvars.h" +#include "localize.h" +#include "multi.h" +#include "popup.h" +#include "ptrack.h" +#include "multi_fstracker.h" +#include "multimsgs.h" +#include "multi_team.h" +#include "multi_sw.h" +#include "multi_pmsg.h" +#include "multiutil.h" +#include "freespace.h" + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER SQUAD WAR DEFINES/VARS +// + +// global request info +squad_war_request Multi_sw_request; + +// global result info +squad_war_result Multi_sw_result; + +// set on the host in response to a standalone sw query, -1 == waiting, 0 == fail, 1 == success +int Multi_sw_std_query = -1; + +// match code +char Multi_sw_match_code[MATCH_CODE_LEN]; + +// reply from a standalone on a bad response +char Multi_sw_bad_reply[MAX_SQUAD_RESPONSE_LEN+1] = ""; + +// team scores +extern int Multi_team0_score; +extern int Multi_team1_score; + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER SQUAD WAR FORWARD DECLARATIONS +// + +// popup_till_condition do frame function for the host doing verification through the standalone +int multi_sw_query_tracker_A(); + +// condition do frame function for the standalone querying PXO +int multi_sw_query_tracker_B(); + +// stuff Multi_sw_request +void multi_sw_stuff_request(char *match_code); + +// stuff Multi_sw_result +void multi_sw_stuff_result(); + +// verify that we have the proper # of players +int multi_sw_verify_squad_counts(); + + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER SQUAD WAR FUNCTIONS +// + +// call before loading level - mission sync phase. only the server need do this +void multi_sw_level_init() +{ +} + +// determine if everything is ok to move forward for a squad war match +int multi_sw_ok_to_commit() +{ + char *ret; + char match_code[MATCH_CODE_LEN] = ""; + char bad_response[MAX_SQUAD_RESPONSE_LEN+1]; + + memset(bad_response, 0, MAX_SQUAD_RESPONSE_LEN+1); + + // make sure we have enough players per team + if(!multi_sw_verify_squad_counts()){ + return 0; + } + + // prompt the host for the match code + ret = popup_input(0, XSTR("Please enter the match code", 1076), 32); + if(ret == NULL){ + return 0; + } + strcpy(match_code, ret); + + strcpy(Multi_sw_match_code, ""); + + // if we're the server, do the nice case + if(MULTIPLAYER_MASTER){ + // stuff match request + multi_sw_stuff_request(match_code); + + // return an MSW_STATUS_* value + if(multi_fs_tracker_validate_sw(&Multi_sw_request, bad_response) == MSW_STATUS_VALID){ + // store the match code + strncpy(Multi_sw_match_code, match_code, MATCH_CODE_LEN); + + // success + return 1; + } + // do - didn't check out properly + else { + if(strlen(bad_response) > 0){ + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "Error validating Squad War match\n\n(%s)", bad_response); + } else { + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "Error validating Squad War match\n\n(%s)", "Unknown"); + } + } + } + // otherwise we have to do it through the standalone - ARRRGHH! + else { + // send the query packet and wait for a response + Multi_sw_std_query = -1; + memset(Multi_sw_bad_reply, 0, MAX_SQUAD_RESPONSE_LEN+1); + send_sw_query_packet(SW_STD_START, match_code); + if(popup_till_condition(multi_sw_query_tracker_A, XSTR("&Cancel", 667), XSTR("Validating squad war", 1075)) == 10){ + // success + return 1; + } else { + if(strlen(Multi_sw_bad_reply) > 0){ + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "Error validating Squad War match\n\n(%s)", Multi_sw_bad_reply); + } else { + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "Error validating Squad War match\n\n(%s)", "Unknown"); + } + } + } + + // hmm, should probably never get here + return 0; +} + +// query PXO on the standalone +void multi_sw_std_query(char *match_code) +{ + char bad_response[MAX_SQUAD_RESPONSE_LEN+1]; + + memset(bad_response, 0, MAX_SQUAD_RESPONSE_LEN+1); + + // stuff match request + multi_sw_stuff_request(match_code); + + strcpy(Multi_sw_match_code, ""); + + // return an MSW_STATUS_* value + if(multi_fs_tracker_validate_sw(&Multi_sw_request, bad_response) != MSW_STATUS_VALID){ + send_sw_query_packet(SW_STD_BAD, bad_response); + } else { + // store the match code + strncpy(Multi_sw_match_code, match_code, MATCH_CODE_LEN); + + send_sw_query_packet(SW_STD_OK, NULL); + } +} + +// call to update everything on the tracker +#define SEND_AND_DISPLAY(mesg) do { send_game_chat_packet(Net_player, mesg, MULTI_MSG_ALL, NULL, NULL, 1); multi_display_chat_msg(mesg, 0, 0); } while(0); +void multi_sw_report(int stats_saved) +{ + char bad_response[MAX_SQUAD_RESPONSE_LEN+1]; + memset(bad_response, 0, MAX_SQUAD_RESPONSE_LEN+1); + + // stuff Multi_sw_result + multi_sw_stuff_result(); + + // update on PXO + if(stats_saved){ + if(multi_fs_tracker_store_sw(&Multi_sw_result, bad_response)){ + SEND_AND_DISPLAY(XSTR("", 1079)); + } else { + SEND_AND_DISPLAY(XSTR("", 1080)); + if(strlen(bad_response) > 0){ + SEND_AND_DISPLAY(bad_response); + } + } + } + // doh. something was bogus + else { + SEND_AND_DISPLAY(XSTR("", 1478)); + } +} + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER SQUAD WAR FORWARD DEFINITIONS +// + +// condition do frame function for the standalone querying PXO +int multi_sw_query_tracker_A() +{ + switch(Multi_sw_std_query){ + // still waiting + case -1 : + return 0; + + // failure + case 0 : + return 1; + + // successs + case 1 : + return 10; + } + + // shouldn't get here + return 1; +} + +// stuff Multi_sw_request +void multi_sw_stuff_request(char *match_code) +{ + int idx; + squad_war_request *s = &Multi_sw_request; + + // stuff squad id #'s, counts, and squad names + s->squad_count1 = 0; + s->squad_count2 = 0; + for(idx=0; idxsquad_plr1[s->squad_count1++] = Net_players[idx].tracker_player_id; + } + // team 1 + else { + s->squad_plr2[s->squad_count2++] = Net_players[idx].tracker_player_id; + } + } + } + + // stuff match code + strncpy(s->match_code, match_code, MATCH_CODE_LEN); +} + +// verify that we have the proper # of players +int multi_sw_verify_squad_counts() +{ + int idx; + int team0_count = 0; + int team1_count = 0; + char err_string[255] = ""; + + for(idx=0; idxmatch_code, Multi_sw_match_code, MATCH_CODE_LEN); + + // determine what happened + switch(multi_team_winner()){ + // tie + case -1: + s->result = 0; + s->squad_count1 = 0; + s->squad_count2 = 0; + break; + + // team 0 won + case 0: + s->result = 1; + // stuff squad id #'s, counts, and squad names + s->squad_count1 = 0; + s->squad_count2 = 0; + for(idx=0; idxsquad_winners[s->squad_count1++] = Net_players[idx].tracker_player_id; + } + // team 1 + else { + s->squad_losers[s->squad_count2++] = Net_players[idx].tracker_player_id; + } + } + } + break; + + // team 1 won + case 1: + s->result = 1; + // stuff squad id #'s, counts, and squad names + s->squad_count1 = 0; + s->squad_count2 = 0; + for(idx=0; idxsquad_winners[s->squad_count1++] = Net_players[idx].tracker_player_id; + } + // team 0 + else { + s->squad_losers[s->squad_count2++] = Net_players[idx].tracker_player_id; + } + } + } + break; + } +} + diff --git a/src/network/ptrack.cpp b/src/network/ptrack.cpp new file mode 100644 index 0000000..73108c8 --- /dev/null +++ b/src/network/ptrack.cpp @@ -0,0 +1,637 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + +//Pilot tracker client code + +#ifdef PLAT_UNIX +#include +#include +#include +#include +#endif + +#include "pstypes.h" +#include "timer.h" +#include "multi.h" +#include "ptrack.h" +#include "psnet.h" + +//Variables + +// SOCKET pilotsock; + +SOCKADDR_IN ptrackaddr; + + +int FSWriteState; +int FSReadState; +unsigned int FSLastSentWrite; +unsigned int FSFirstSentWrite; +unsigned int FSLastSent; +unsigned int FSFirstSent; + +int SWWriteState; +unsigned int SWLastSentWrite; +unsigned int SWFirstSentWrite; + + +udp_packet_header fs_pilot_req, fs_pilot_write, sw_res_write; +pilot_request *fs_pr; +#ifdef MAKE_FS1 +vmt_freespace_struct *ReadFSPilot; +#else +vmt_freespace2_struct *ReadFSPilot; +#endif + +// squad war response +squad_war_response SquadWarWriteResponse; + +int InitPilotTrackerClient() +{ + SOCKADDR_IN sockaddr; + unsigned long iaddr; + + FSWriteState = STATE_IDLE; + FSReadState = STATE_IDLE; + SWWriteState = STATE_IDLE; + + ReadFSPilot = NULL; + + fs_pr = (pilot_request *)&fs_pilot_req.data; + + /* + pilotsock = socket(AF_INET,SOCK_DGRAM,0); + + if ( pilotsock == INVALID_SOCKET ) + { + printf("Unable to open a socket.\n"); + return 0; + } + */ + + memset( &sockaddr, 0, sizeof(SOCKADDR_IN) ); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; + sockaddr.sin_port = 0;//htons(REGPORT); + + /* + if (SOCKET_ERROR==bind(pilotsock, (SOCKADDR*)&sockaddr, sizeof (sockaddr))) + { + printf("Unable to bind a socket.\n"); + printf("WSAGetLastError() returned %d.\n",WSAGetLastError()); + return 0; + } + */ + + // iaddr = inet_addr ( Multi_user_tracker_ip_address ); + + // first try and resolve by name + iaddr = inet_addr( Multi_options_g.user_tracker_ip ); + if ( iaddr == INADDR_NONE ) { + HOSTENT *he; + he = gethostbyname( Multi_options_g.user_tracker_ip ); + if(!he) + return 0; + /* + { + // try and resolve by address + unsigned int n_order = inet_addr(Multi_user_tracker_ip_address); + he = gethostbyaddr((char*)&n_order,4,PF_INET); + + if(!he){ + return 0; + } + } + */ + memcpy(&iaddr, he->h_addr_list[0],4); + } + + memcpy(&ptrackaddr.sin_addr.s_addr, &iaddr, 4); + ptrackaddr.sin_family = AF_INET; + ptrackaddr.sin_port = htons(REGPORT); + + return 1; +} + +// Returns: +// -3 Error -- Called with NULL, but no request is waiting +// -2 Error -- Already sending data (hasn't timed out yet) +// -1 Timeout trying to send pilot data +// 0 Sending +// 1 Data succesfully sent +// 2 Send Cancelled (data may still have been written already, we just haven't been ACK'd yet) +// 3 Pilot not written (for some reason) + +// Call with NULL to poll +// Call with -1 to cancel send +// Call with valid pointer to a vmt_descent3_struct to initiate send +#ifdef MAKE_FS1 +int SendFSPilotData(vmt_freespace_struct *fs_pilot) +#else +int SendFSPilotData(vmt_freespace2_struct *fs_pilot) +#endif +{ + //First check the network + PollPTrackNet(); + + if(fs_pilot == NULL) + { + if(FSWriteState == STATE_IDLE) + { + return -3; + } + if(FSWriteState == STATE_SENDING_PILOT) + { + return 0; + } + if(FSWriteState == STATE_WROTE_PILOT) + { + //We wrote this pilot, and now we are about to inform the app, so back to idle + FSWriteState = STATE_IDLE; + return 1; + } + if(FSWriteState == STATE_TIMED_OUT) + { + //We gave up on sending this pilot, and now we are about to inform the app, so back to idle + FSWriteState = STATE_IDLE; + + return -1; + } + if(FSWriteState == STATE_WRITE_PILOT_FAILED) + { + //The tracker said this dude couldn't be written + FSWriteState = STATE_IDLE; + + return 3; + } + + } + else if(fs_pilot == (vmt_freespace2_struct*)0xffffffff) + { + if(FSWriteState == STATE_IDLE) + { + return -3; + } + else + { + //Cancel this baby + FSWriteState = STATE_IDLE; + + return 2; + } + + } + else if(FSWriteState == STATE_IDLE) + { + //New request, send out the req, and go for it. + + FSWriteState = STATE_SENDING_PILOT; + + FSLastSentWrite = 0; + FSFirstSentWrite = timer_get_milliseconds(); + + fs_pilot_write.type = UNT_PILOT_DATA_WRITE_NEW; +#ifdef MAKE_FS1 + fs_pilot_write.len = PACKED_HEADER_ONLY_SIZE+sizeof(vmt_freespace_struct); + fs_pilot_write.code = CMD_GAME_FREESPACE; + memcpy(&fs_pilot_write.data,fs_pilot,sizeof(vmt_freespace_struct)); +#else + fs_pilot_write.len = PACKED_HEADER_ONLY_SIZE+sizeof(vmt_freespace2_struct); + fs_pilot_write.code = CMD_GAME_FREESPACE2; + memcpy(&fs_pilot_write.data,fs_pilot,sizeof(vmt_freespace2_struct)); +#endif + + return 0; + } + return -2; +} + +// Returns: +// -3 Error -- Called with NULL, but no request is waiting +// -2 Error -- Already sending data (hasn't timed out yet) +// -1 Timeout trying to send pilot data +// 0 Sending +// 1 Data succesfully sent +// 2 Send Cancelled (data may still have been written already, we just haven't been ACK'd yet) +// 3 Pilot not written (for some reason) + +// Call with NULL to poll +// Call with -1 to cancel send +// Call with valid pointer to a vmt_descent3_struct to initiate send +int SendSWData(squad_war_result *sw_res, squad_war_response *sw_resp) +{ + //First check the network + PollPTrackNet(); + + if(sw_res == NULL){ + if(SWWriteState == STATE_IDLE){ + return -3; + } + if(SWWriteState == STATE_SENDING_PILOT){ + return 0; + } + + // fill in the response + if(SWWriteState == STATE_WROTE_PILOT){ + // We wrote this pilot, and now we are about to inform the app, so back to idle + SWWriteState = STATE_IDLE; + + if(sw_resp != NULL){ + memcpy(sw_resp, &SquadWarWriteResponse, sizeof(squad_war_response)); + } + return 1; + } + // fill in the response + if(SWWriteState == STATE_WRITE_PILOT_FAILED){ + // The tracker said this dude couldn't be written + SWWriteState = STATE_IDLE; + + if(sw_resp != NULL){ + memcpy(sw_resp, &SquadWarWriteResponse, sizeof(squad_war_response)); + } + return 3; + } + + if(SWWriteState == STATE_TIMED_OUT){ + // We gave up on sending this pilot, and now we are about to inform the app, so back to idle + SWWriteState = STATE_IDLE; + + return -1; + } + } else if(sw_res == (squad_war_result*)0xffffffff){ + if(SWWriteState == STATE_IDLE){ + return -3; + } else { + // Cancel this baby + SWWriteState = STATE_IDLE; + + return 2; + } + } else if(SWWriteState == STATE_IDLE) { + //New request, send out the req, and go for it. + + SWWriteState = STATE_SENDING_PILOT; + + SWLastSentWrite = 0; + SWFirstSentWrite = timer_get_milliseconds(); + + sw_res_write.len = PACKED_HEADER_ONLY_SIZE+sizeof(squad_war_result); + sw_res_write.type = UNT_SW_RESULT_WRITE; +#ifdef MAKE_FS1 + sw_res_write.code = CMD_GAME_FREESPACE; +#else + sw_res_write.code = CMD_GAME_FREESPACE2; +#endif + memcpy(&sw_res_write.data, sw_res, sizeof(squad_war_result)); + + return 0; + } + return -2; +} + + +// Returns: +// -3 Error -- Called with NULL, but no request is waiting +// -2 Error -- Already waiting on data (hasn't timed out yet) +// -1 Timeout waiting for pilot data +// 0 Waiting for data +// 1 Data received +// 2 Get Cancelled +// 3 Pilot not found + +// Call with NULL to poll +// Call with -1 to cancel wait +// Call with valid pointer to a vmt_descent3_struct to get a response +#ifdef MAKE_FS1 +int GetFSPilotData(vmt_freespace_struct *fs_pilot, const char *pilot_name, const char *tracker_id, int get_security) +#else +int GetFSPilotData(vmt_freespace2_struct *fs_pilot, const char *pilot_name, const char *tracker_id, int get_security) +#endif +{ + //First check the network + PollPTrackNet(); + + if(fs_pilot == NULL) + { + if(FSReadState == STATE_IDLE) + { + return -3; + } + if(FSReadState == STATE_READING_PILOT) + { + return 0; + } + if(FSReadState == STATE_RECEIVED_PILOT) + { + // We got this pilot, and now we are about to inform the app, so back to idle + FSReadState = STATE_IDLE; + ReadFSPilot = NULL; + return 1; + } + if(FSReadState == STATE_TIMED_OUT) + { + // We gave up on this pilot, and now we are about to inform the app, so back to idle + FSReadState = STATE_IDLE; + ReadFSPilot = NULL; + return -1; + } + if(FSReadState == STATE_PILOT_NOT_FOUND) + { + //The tracker said this dude is not found. + FSReadState = STATE_IDLE; + ReadFSPilot = NULL; + return 3; + } + + } + else if(fs_pilot == (vmt_freespace2_struct*)0xffffffff) + { + if(FSReadState == STATE_IDLE) + { + return -3; + } + else + { + //Cancel this baby + FSReadState = STATE_IDLE; + ReadFSPilot = NULL; + return 2; + } + + } + else if(FSReadState == STATE_IDLE) + { + //New request, send out the req, and go for it. + + FSReadState = STATE_READING_PILOT; + ReadFSPilot = fs_pilot; + FSLastSent = 0; + FSFirstSent = timer_get_milliseconds(); + + fs_pilot_req.len = PACKED_HEADER_ONLY_SIZE+sizeof(pilot_request); + + if(get_security){ + fs_pilot_req.type = UNT_PILOT_DATA_READ_NEW; + } else { + fs_pilot_req.type = UNT_PILOT_DATA_READ; + } + +#ifdef MAKE_FS1 + fs_pilot_req.code = CMD_GAME_FREESPACE; +#else + fs_pilot_req.code = CMD_GAME_FREESPACE2; +#endif + strcpy(fs_pr->pilot_name,pilot_name); + strncpy(fs_pr->tracker_id,tracker_id,TRACKER_ID_LEN); + + return 0; + } + return -2; + +} + +// Send an ACK to the server +void AckServer(unsigned int sig) +{ + udp_packet_header ack_pack; + + ack_pack.type = UNT_CONTROL; + ack_pack.sig = sig; + ack_pack.code = CMD_CLIENT_RECEIVED; + ack_pack.len = PACKED_HEADER_ONLY_SIZE; + + SENDTO(Unreliable_socket, (char *)&ack_pack,PACKED_HEADER_ONLY_SIZE,0,(SOCKADDR *)&ptrackaddr,sizeof(SOCKADDR_IN), PSNET_TYPE_USER_TRACKER); +} + +void IdlePTrack() +{ + PSNET_TOP_LAYER_PROCESS(); + + // reading pilot data + if(FSReadState == STATE_READING_PILOT){ + if((timer_get_milliseconds()-FSFirstSent)>=PILOT_REQ_TIMEOUT){ + FSReadState = STATE_TIMED_OUT; + } else if((timer_get_milliseconds()-FSLastSent)>=PILOT_REQ_RESEND_TIME){ + //Send 'da packet + SENDTO(Unreliable_socket, (char *)&fs_pilot_req,fs_pilot_req.len,0,(SOCKADDR *)&ptrackaddr,sizeof(SOCKADDR_IN), PSNET_TYPE_USER_TRACKER); + FSLastSent = timer_get_milliseconds(); + } + } + + // writing pilot data + if(FSWriteState == STATE_SENDING_PILOT){ + if((timer_get_milliseconds()-FSFirstSentWrite)>=PILOT_REQ_TIMEOUT){ + FSWriteState = STATE_TIMED_OUT; + + } else if((timer_get_milliseconds()-FSLastSentWrite)>=PILOT_REQ_RESEND_TIME){ + // Send 'da packet + SENDTO(Unreliable_socket, (char *)&fs_pilot_write,fs_pilot_write.len,0,(SOCKADDR *)&ptrackaddr,sizeof(SOCKADDR_IN), PSNET_TYPE_USER_TRACKER); + FSLastSentWrite = timer_get_milliseconds(); + } + } + + // writing squad war results + if(SWWriteState == STATE_SENDING_PILOT){ + if((timer_get_milliseconds()-SWFirstSentWrite) >= PILOT_REQ_TIMEOUT){ + SWWriteState = STATE_TIMED_OUT; + } else if((timer_get_milliseconds()-SWLastSentWrite) >= PILOT_REQ_RESEND_TIME){ + // Send 'da packet + SENDTO(Unreliable_socket, (char *)&sw_res_write, sw_res_write.len, 0, (SOCKADDR *)&ptrackaddr,sizeof(SOCKADDR_IN), PSNET_TYPE_USER_TRACKER); + SWLastSentWrite = timer_get_milliseconds(); + } + } +} + +void PollPTrackNet() +{ + fd_set read_fds; + TIMEVAL timeout; + + IdlePTrack(); + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + +#ifndef PLAT_UNIX + if(SELECT(0, &read_fds,NULL,NULL,&timeout, PSNET_TYPE_USER_TRACKER)){ +#else + if(SELECT(Unreliable_socket+1, &read_fds,NULL,NULL,&timeout, PSNET_TYPE_USER_TRACKER)){ +#endif + int bytesin; + int addrsize; + SOCKADDR_IN fromaddr; + + udp_packet_header inpacket; + addrsize = sizeof(SOCKADDR_IN); + + bytesin = RECVFROM(Unreliable_socket, (char *)&inpacket,sizeof(udp_packet_header),0,(SOCKADDR *)&fromaddr,&addrsize, PSNET_TYPE_USER_TRACKER); + if(bytesin==-1){ + int wserr=WSAGetLastError(); + printf("recvfrom() failure. WSAGetLastError() returned %d\n",wserr); + + } + + // decrease packet size by 1 + inpacket.len--; + + //Check to make sure the packets ok + if(bytesin==inpacket.len){ + switch(inpacket.type){ + case UNT_PILOT_DATA_RESPONSE: + if(inpacket.code == CMD_GAME_FREESPACE2){ +#ifdef MAKE_FS1 + Int3(); +#else + if(FSReadState == STATE_READING_PILOT){ + vmt_freespace2_struct *stats; + + // 9/17/98 MWA. Compare the tracker id of this packet with the tracker id of + // what we are expecting. Only set our state to something different when + // the tracker id's match. This fixes possible multiple packets for a single pilto + // accidentally getting set for the wrong pilot + + stats = (vmt_freespace2_struct *)(&inpacket.data); + if ( !SDL_strncasecmp(stats->tracker_id, fs_pr->tracker_id,TRACKER_ID_LEN) ) { + //Copy the data + memcpy(ReadFSPilot,&inpacket.data,sizeof(vmt_freespace2_struct)); + //Set the state + FSReadState = STATE_RECEIVED_PILOT; + } + } +#endif + } else if(inpacket.code == CMD_GAME_FREESPACE){ +#ifndef MAKE_FS1 + Int3(); +#else + if(FSReadState == STATE_READING_PILOT){ + vmt_freespace_struct *stats; + + // 9/17/98 MWA. Compare the tracker id of this packet with the tracker id of + // what we are expecting. Only set our state to something different when + // the tracker id's match. This fixes possible multiple packets for a single pilto + // accidentally getting set for the wrong pilot + + stats = (vmt_freespace_struct *)(&inpacket.data); + if ( !SDL_strncasecmp(stats->tracker_id, fs_pr->tracker_id,TRACKER_ID_LEN) ) { + //Copy the data + memcpy(ReadFSPilot,&inpacket.data,sizeof(vmt_freespace_struct)); + //Set the state + FSReadState = STATE_RECEIVED_PILOT; + } + } +#endif + } else { + Int3(); + } + break; + + case UNT_PILOT_READ_FAILED: + if(inpacket.code == CMD_GAME_FREESPACE2){ +#ifdef MAKE_FS1 + Int3(); +#else + if(FSReadState == STATE_READING_PILOT){ + FSReadState = STATE_PILOT_NOT_FOUND; + } +#endif + } else if(inpacket.code == CMD_GAME_FREESPACE){ +#ifndef MAKE_FS1 + Int3(); +#else + if(FSReadState == STATE_READING_PILOT){ + FSReadState = STATE_PILOT_NOT_FOUND; + } +#endif + } else { + Int3(); + } + break; + + case UNT_PILOT_WRITE_SUCCESS: + if(inpacket.code == CMD_GAME_FREESPACE2){ +#ifdef MAKE_FS1 + Int3(); +#else + if(FSWriteState == STATE_SENDING_PILOT){ + FSWriteState = STATE_WROTE_PILOT; + } +#endif + } else if(inpacket.code == CMD_GAME_FREESPACE){ +#ifndef MAKE_FS1 + Int3(); +#else + if(FSWriteState == STATE_SENDING_PILOT){ + FSWriteState = STATE_WROTE_PILOT; + } +#endif + } else { + Int3(); + } + break; + + case UNT_PILOT_WRITE_FAILED: + if(inpacket.code == CMD_GAME_FREESPACE2){ +#ifdef MAKE_FS1 + Int3(); +#else + if(FSWriteState == STATE_SENDING_PILOT){ + FSWriteState = STATE_WRITE_PILOT_FAILED; + } +#endif + } else if(inpacket.code == CMD_GAME_FREESPACE){ +#ifndef MAKE_FS1 + Int3(); +#else + if(FSWriteState == STATE_SENDING_PILOT){ + FSWriteState = STATE_WRITE_PILOT_FAILED; + } +#endif + } else { + Int3(); + } + break; + + case UNT_SW_RESULT_RESPONSE: + if(SWWriteState == STATE_SENDING_PILOT){ + // copy the data + SDL_assert((bytesin - PACKED_HEADER_ONLY_SIZE) == sizeof(squad_war_response)); + if((bytesin - PACKED_HEADER_ONLY_SIZE) == sizeof(squad_war_response)){ + memset(&SquadWarWriteResponse, 0, sizeof(squad_war_response)); + memcpy(&SquadWarWriteResponse, inpacket.data, sizeof(squad_war_response)); + + // now check to see if we're good + if(SquadWarWriteResponse.accepted){ + SWWriteState = STATE_WROTE_PILOT; + } else { + SWWriteState = STATE_WRITE_PILOT_FAILED; + } + } else { + SWWriteState = STATE_WRITE_PILOT_FAILED; + } + } + break; + + case UNT_CONTROL: + Int3(); + break; + + case UNT_CONTROL_VALIDATION: + Int3(); + break; + + default: + break; + } + AckServer(inpacket.sig); + } + } +} diff --git a/src/network/valid.cpp b/src/network/valid.cpp new file mode 100644 index 0000000..b359d66 --- /dev/null +++ b/src/network/valid.cpp @@ -0,0 +1,503 @@ +/* + * Copyright (C) Volition, Inc. 2005. All rights reserved. + * + * All source code herein is the property of Volition, Inc. You may not sell + * or otherwise commercially exploit the source or things you created based on the + * source. + * +*/ + + +//Validate tracker user class + + +#ifdef PLAT_UNIX +#include +#include +#include +#include +#endif + +#include "multi.h" +#include "ptrack.h" +#include "valid.h" +#include "psnet.h" +#include "timer.h" + + +// Variables +udp_packet_header PacketHeader; +validate_id_request *ValidIDReq; + +int ValidState; + +// SOCKET validsock; +SOCKADDR_IN rtrackaddr; + +int ValidFirstSent; +int ValidLastSent; + +char *Psztracker_id; + +// mission validation +int MissionValidState; +int MissionValidFirstSent; +int MissionValidLastSent; + +// squad war validation +int SquadWarValidState; +int SquadWarFirstSent; +int SquadWarLastSent; + +// squad war response +squad_war_response SquadWarValidateResponse; + +int InitValidateClient(void) +{ + SOCKADDR_IN sockaddr; + unsigned long iaddr; + ValidFirstSent = 0; + ValidLastSent = 0; + ValidState = VALID_STATE_IDLE; + + MissionValidFirstSent = 0; + MissionValidLastSent = 0; + MissionValidState = VALID_STATE_IDLE; + + SquadWarFirstSent = 0; + SquadWarLastSent = 0; + SquadWarValidState = VALID_STATE_IDLE; + + /* + validsock = socket(AF_INET,SOCK_DGRAM,0); + if ( validsock == INVALID_SOCKET ) + { + printf("Unable to open a socket.\n"); + return 0; + } + */ + + memset( &sockaddr, 0, sizeof(SOCKADDR_IN) ); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; + sockaddr.sin_port = 0; + + /* + if (SOCKET_ERROR==bind(validsock, (SOCKADDR*)&sockaddr, sizeof (sockaddr))) + { + printf("Unable to bind a socket.\n"); + printf("WSAGetLastError() returned %d.\n",WSAGetLastError()); + return 0; + } + */ + + rtrackaddr.sin_family = AF_INET; + iaddr = inet_addr( Multi_options_g.user_tracker_ip ); + if ( iaddr == INADDR_NONE ) { + HOSTENT *he; + he = gethostbyname( Multi_options_g.user_tracker_ip ); + if(!he) + return 0; + /* + { + // try and resolve by address + unsigned int n_order = inet_addr(Multi_user_tracker_ip_address); + he = gethostbyaddr((char*)&n_order,4,PF_INET); + + if(!he){ + return 0; + } + } + */ + memcpy(&iaddr, he->h_addr_list[0],4); + } + + memcpy(&rtrackaddr.sin_addr.s_addr, &iaddr, 4); + rtrackaddr.sin_port = htons(REGPORT); + + return 1; + +} + +//Call with a valid struct to validate a user +//Call with NULL to poll + +//Return codes: +// -3 Still waiting (returned if we were waiting for a tracker response and ValidateUser was called with a non-NULL value +// -2 Timeout waiting for tracker to respond +// -1 User invalid +// 0 Still waiting for response from tracker/Idle +// 1 User valid +int ValidateUser(validate_id_request *valid_id, char *trackerid) +{ + ValidIdle(); + if(valid_id==NULL) + { + switch(ValidState) + { + case VALID_STATE_IDLE: + return 0; + break; + case VALID_STATE_WAITING: + return 0; + break; + case VALID_STATE_VALID: + ValidState = VALID_STATE_IDLE; + return 1; + break; + case VALID_STATE_INVALID: + ValidState = VALID_STATE_IDLE; + return -1; + case VALID_STATE_TIMEOUT: + ValidState = VALID_STATE_IDLE; + return -2; + } + return 0; + } + else + { + if(ValidState==VALID_STATE_IDLE) + { + //First, flush the input buffer for the socket + fd_set read_fds; + TIMEVAL timeout; + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + +#ifndef PLAT_UNIX + while(SELECT(0,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_VALIDATION)) +#else + while(SELECT(Unreliable_socket+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_VALIDATION)) +#endif + { + int addrsize; + SOCKADDR_IN fromaddr; + + udp_packet_header inpacket; + addrsize = sizeof(SOCKADDR_IN); + RECVFROM(Unreliable_socket, (char *)&inpacket,sizeof(udp_packet_header),0,(SOCKADDR *)&fromaddr,&addrsize, PSNET_TYPE_VALIDATION); + } + Psztracker_id = trackerid; + + //Build the request packet + PacketHeader.type = UNT_LOGIN_AUTH_REQUEST; + PacketHeader.len = PACKED_HEADER_ONLY_SIZE+sizeof(validate_id_request); + ValidIDReq=(validate_id_request *)&PacketHeader.data; + strcpy(ValidIDReq->login,valid_id->login); + strcpy(ValidIDReq->password,valid_id->password); + + SENDTO(Unreliable_socket, (char *)&PacketHeader,PacketHeader.len,0,(SOCKADDR *)&rtrackaddr,sizeof(SOCKADDR), PSNET_TYPE_VALIDATION); + ValidState = VALID_STATE_WAITING; + ValidFirstSent = timer_get_milliseconds(); + ValidLastSent = timer_get_milliseconds(); + return 0; + } + else + { + return -3; + } + } +} + + +void ValidIdle() +{ + fd_set read_fds; + TIMEVAL timeout; + + PSNET_TOP_LAYER_PROCESS(); + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + +#ifndef PLAT_UNIX + if(SELECT(0,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_VALIDATION)){ +#else + if(SELECT(Unreliable_socket+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_VALIDATION)){ +#endif + int bytesin; + int addrsize; + SOCKADDR_IN fromaddr; + + udp_packet_header inpacket; + addrsize = sizeof(SOCKADDR_IN); + + bytesin = RECVFROM(Unreliable_socket, (char *)&inpacket, sizeof(udp_packet_header),0,(SOCKADDR *)&fromaddr,&addrsize, PSNET_TYPE_VALIDATION); + if(bytesin==-1){ + int wserr=WSAGetLastError(); + printf("recvfrom() failure. WSAGetLastError() returned %d\n",wserr); + + } + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + + // decrease packet size by 1 + inpacket.len--; + + //Check to make sure the packets ok + if(bytesin==inpacket.len){ + switch(inpacket.type) + { + case UNT_LOGIN_NO_AUTH: + if(ValidState == VALID_STATE_WAITING) + { + ValidState = VALID_STATE_INVALID; + } + break; + case UNT_LOGIN_AUTHENTICATED: + if(ValidState == VALID_STATE_WAITING) + { + ValidState = VALID_STATE_VALID; + strncpy(Psztracker_id, (const char *)&inpacket.data, TRACKER_ID_LEN); + } + break; + // old - this is a Freespace 1 packet type + case UNT_VALID_FS_MSN_RSP: + Int3(); + break; + + // fs2 mission validation response + case UNT_VALID_FS2_MSN_RSP: + if(MissionValidState == VALID_STATE_WAITING){ + if(inpacket.code==2){ + MissionValidState = VALID_STATE_VALID; + } else { + MissionValidState = VALID_STATE_INVALID; + } + } + break; + + // fs2 squad war validation response + case UNT_VALID_SW_MSN_RSP: + if(SquadWarValidState == VALID_STATE_WAITING){ + // copy the data + SDL_assert((bytesin - PACKED_HEADER_ONLY_SIZE) == sizeof(squad_war_response)); + if((bytesin - PACKED_HEADER_ONLY_SIZE) == sizeof(squad_war_response)){ + memset(&SquadWarValidateResponse, 0, sizeof(squad_war_response)); + memcpy(&SquadWarValidateResponse, inpacket.data, sizeof(squad_war_response)); + + // now check to see if we're good + if(SquadWarValidateResponse.accepted){ + SquadWarValidState = VALID_STATE_VALID; + } else { + SquadWarValidState = VALID_STATE_INVALID; + } + } else { + SquadWarValidState = VALID_STATE_INVALID; + } + } + break; + + case UNT_CONTROL_VALIDATION: + Int3(); + break; + + case UNT_CONTROL: + Int3(); + break; + } + AckValidServer(inpacket.sig); + } + } + + if(ValidState == VALID_STATE_WAITING) + { + if((timer_get_milliseconds()-ValidFirstSent)>=PILOT_REQ_TIMEOUT) + { + ValidState = VALID_STATE_TIMEOUT; + + } + else if((timer_get_milliseconds()-ValidLastSent)>=PILOT_REQ_RESEND_TIME) + { + //Send 'da packet + SENDTO(Unreliable_socket, (char *)&PacketHeader, PacketHeader.len, 0, (SOCKADDR *)&rtrackaddr, sizeof(SOCKADDR), PSNET_TYPE_VALIDATION); + ValidLastSent = timer_get_milliseconds(); + } + } +} + + +//Send an ACK to the server +void AckValidServer(unsigned int sig) +{ + udp_packet_header ack_pack; + + ack_pack.type = UNT_CONTROL; + ack_pack.sig = sig; + ack_pack.code = CMD_CLIENT_RECEIVED; + ack_pack.len = PACKED_HEADER_ONLY_SIZE; + + SENDTO(Unreliable_socket, (char *)&ack_pack,PACKED_HEADER_ONLY_SIZE,0,(SOCKADDR *)&rtrackaddr,sizeof(SOCKADDR_IN), PSNET_TYPE_VALIDATION); +} + +// call with a valid struct to validate a mission +// call with NULL to poll + +// Return codes: +// -3 Still waiting (returned if we were waiting for a tracker response and ValidateMission was called with a non-NULL value +// -2 Timeout waiting for tracker to respond +// -1 User invalid +// 0 Still waiting for response from tracker/Idle +// 1 User valid +int ValidateMission(vmt_validate_mission_req_struct *valid_msn) +{ + ValidIdle(); + if(valid_msn==NULL) + { + switch(MissionValidState) + { + case VALID_STATE_IDLE: + return 0; + break; + case VALID_STATE_WAITING: + return 0; + break; + case VALID_STATE_VALID: + MissionValidState = VALID_STATE_IDLE; + return 1; + break; + case VALID_STATE_INVALID: + MissionValidState = VALID_STATE_IDLE; + return -1; + case VALID_STATE_TIMEOUT: + MissionValidState = VALID_STATE_IDLE; + return -2; + } + return 0; + } + else + { + if(MissionValidState==VALID_STATE_IDLE) + { + //First, flush the input buffer for the socket + fd_set read_fds; + TIMEVAL timeout; + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + +#ifndef PLAT_UNIX + while(SELECT(0,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_VALIDATION)) +#else + while(SELECT(Unreliable_socket+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_VALIDATION)) +#endif + { + int addrsize; + SOCKADDR_IN fromaddr; + + udp_packet_header inpacket; + addrsize = sizeof(SOCKADDR_IN); + RECVFROM(Unreliable_socket, (char *)&inpacket,sizeof(udp_packet_header),0,(SOCKADDR *)&fromaddr,&addrsize, PSNET_TYPE_VALIDATION); + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + } + //only send the header, the checksum and the string length plus the null + PacketHeader.type = UNT_VALID_FS2_MSN_REQ; + PacketHeader.len = (short)(PACKED_HEADER_ONLY_SIZE + sizeof(int)+1+strlen(valid_msn->file_name)); + memcpy(PacketHeader.data,valid_msn,PacketHeader.len-PACKED_HEADER_ONLY_SIZE); + SENDTO(Unreliable_socket, (char *)&PacketHeader,PacketHeader.len,0,(SOCKADDR *)&rtrackaddr,sizeof(SOCKADDR), PSNET_TYPE_VALIDATION); + MissionValidState = VALID_STATE_WAITING; + MissionValidFirstSent = timer_get_milliseconds(); + MissionValidLastSent = timer_get_milliseconds(); + return 0; + } + else + { + return -3; + } + } +} + +// query the usertracker to validate a squad war match +// call with a valid struct to validate a mission +// call with NULL to poll + +// Return codes: +// -3 Still waiting (returned if we were waiting for a tracker response and ValidateSquadWae was called with a non-NULL value +// -2 Timeout waiting for tracker to respond +// -1 match invalid +// 0 Still waiting for response from tracker/Idle +// 1 match valid +int ValidateSquadWar(squad_war_request *sw_req, squad_war_response *sw_resp) +{ + ValidIdle(); + if(sw_req==NULL){ + switch(SquadWarValidState){ + case VALID_STATE_IDLE: + return 0; + break; + case VALID_STATE_WAITING: + return 0; + break; + + // fill in the response + case VALID_STATE_VALID: + SquadWarValidState = VALID_STATE_IDLE; + if(sw_resp != NULL){ + memcpy(sw_resp, &SquadWarValidateResponse, sizeof(squad_war_response)); + } + return 1; + break; + // fill in the response + case VALID_STATE_INVALID: + SquadWarValidState = VALID_STATE_IDLE; + if(sw_resp != NULL){ + memcpy(sw_resp, &SquadWarValidateResponse, sizeof(squad_war_response)); + } + return -1; + + case VALID_STATE_TIMEOUT: + SquadWarValidState = VALID_STATE_IDLE; + return -2; + } + return 0; + } else { + if(SquadWarValidState==VALID_STATE_IDLE){ + // First, flush the input buffer for the socket + fd_set read_fds; + TIMEVAL timeout; + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + +#ifndef PLAT_UNIX + while(SELECT(0,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_VALIDATION)){ +#else + while(SELECT(Unreliable_socket+1,&read_fds,NULL,NULL,&timeout, PSNET_TYPE_VALIDATION)){ +#endif + int addrsize; + SOCKADDR_IN fromaddr; + + udp_packet_header inpacket; + addrsize = sizeof(SOCKADDR_IN); + RECVFROM(Unreliable_socket, (char *)&inpacket,sizeof(udp_packet_header),0,(SOCKADDR *)&fromaddr,&addrsize, PSNET_TYPE_VALIDATION); + FD_ZERO(&read_fds); + FD_SET(Unreliable_socket, &read_fds); + } + // only send the header, the checksum and the string length plus the null + PacketHeader.type = UNT_VALID_SW_MSN_REQ; + PacketHeader.len = (short)(PACKED_HEADER_ONLY_SIZE + sizeof(squad_war_request)); + memcpy(PacketHeader.data, sw_req, PacketHeader.len-PACKED_HEADER_ONLY_SIZE); + SENDTO(Unreliable_socket, (char *)&PacketHeader, PacketHeader.len, 0, (SOCKADDR *)&rtrackaddr, sizeof(SOCKADDR), PSNET_TYPE_VALIDATION); + SquadWarValidState = VALID_STATE_WAITING; + SquadWarFirstSent = timer_get_milliseconds(); + SquadWarLastSent = timer_get_milliseconds(); + return 0; + } else { + return -3; + } + } +} -- 2.39.2