2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Network/multi_options.cpp $
16 * Revision 1.4 2003/05/25 02:30:43 taylor
19 * Revision 1.3 2002/06/09 04:41:23 relnev
20 * added copyright header
22 * Revision 1.2 2002/05/26 20:22:48 theoddone33
23 * Most of network/ works
25 * Revision 1.1.1.1 2002/05/03 03:28:10 root
29 * 22 8/27/99 12:32a Dave
30 * Allow the user to specify a local port through the launcher.
32 * 21 8/22/99 1:19p Dave
33 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
34 * which d3d cards are detected.
36 * 20 8/04/99 6:01p Dave
37 * Oops. Make sure standalones log in.
39 * 19 7/09/99 9:51a Dave
40 * Added thick polyline code.
42 * 18 5/03/99 8:32p Dave
43 * New version of multi host options screen.
45 * 17 4/25/99 7:43p Dave
46 * Misc small bug fixes. Made sun draw properly.
48 * 16 4/20/99 6:39p Dave
49 * Almost done with artillery targeting. Added support for downloading
50 * images on the PXO screen.
52 * 15 3/10/99 6:50p Dave
53 * Changed the way we buffer packets for all clients. Optimized turret
54 * fired packets. Did some weapon firing optimizations.
56 * 14 3/09/99 6:24p Dave
57 * More work on object update revamping. Identified several sources of
58 * unnecessary bandwidth.
60 * 13 3/08/99 7:03p Dave
61 * First run of new object update system. Looks very promising.
63 * 12 2/21/99 6:01p Dave
64 * Fixed standalone WSS packets.
66 * 11 2/19/99 2:55p Dave
67 * Temporary checking to report the winner of a squad war match.
69 * 10 2/12/99 6:16p Dave
70 * Pre-mission Squad War code is 95% done.
72 * 9 2/11/99 3:08p Dave
73 * PXO refresh button. Very preliminary squad war support.
75 * 8 11/20/98 11:16a Dave
76 * Fixed up IPX support a bit. Making sure that switching modes and
77 * loading/saving pilot files maintains proper state.
79 * 7 11/19/98 4:19p Dave
80 * Put IPX sockets back in psnet. Consolidated all multiplayer config
83 * 6 11/19/98 8:03a Dave
84 * Full support for D3-style reliable sockets. Revamped packet lag/loss
85 * system, made it receiver side and at the lowest possible level.
87 * 5 11/17/98 11:12a Dave
88 * Removed player identification by address. Now assign explicit id #'s.
90 * 4 10/13/98 9:29a Dave
91 * Started neatening up freespace.h. Many variables renamed and
92 * reorganized. Added AlphaColors.[h,cpp]
94 * 3 10/07/98 6:27p Dave
95 * Globalized mission and campaign file extensions. Removed Silent Threat
96 * special code. Moved \cache \players and \multidata into the \data
99 * 2 10/07/98 10:53a Dave
102 * 1 10/07/98 10:50a Dave
104 * 20 9/10/98 1:17p Dave
105 * Put in code to flag missions and campaigns as being MD or not in Fred
106 * and Freespace. Put in multiplayer support for filtering out MD
107 * missions. Put in multiplayer popups for warning of non-valid missions.
109 * 19 7/07/98 2:49p Dave
112 * 18 6/04/98 11:04a Allender
113 * object update level stuff. Don't reset to high when becoming an
114 * observer of any type. default to low when guy is a dialup customer
116 * 17 5/24/98 3:45a Dave
117 * Minor object update fixes. Justify channel information on PXO. Add a
118 * bunch of configuration stuff for the standalone.
120 * 16 5/19/98 1:35a Dave
121 * Tweaked pxo interface. Added rankings url to pxo.cfg. Make netplayer
122 * local options update dynamically in netgames.
124 * 15 5/08/98 5:05p Dave
125 * Go to the join game screen when quitting multiplayer. Fixed mission
126 * text chat bugs. Put mission type symbols on the create game list.
127 * Started updating standalone gui controls.
129 * 14 5/06/98 12:36p Dave
130 * Make sure clients can leave the debrief screen easily at all times. Fix
131 * respawn count problem.
133 * 13 5/03/98 7:04p Dave
134 * Make team vs. team work mores smoothly with standalone. Change how host
135 * interacts with standalone for picking missions. Put in a time limit for
136 * ingame join ship select. Fix ingame join ship select screen for Vasudan
139 * 12 5/03/98 2:52p Dave
140 * Removed multiplayer furball mode.
142 * 11 4/23/98 6:18p Dave
143 * Store ETS values between respawns. Put kick feature in the text
144 * messaging system. Fixed text messaging system so that it doesn't
145 * process or trigger ship controls. Other UI fixes.
147 * 10 4/22/98 5:53p Dave
148 * Large reworking of endgame sequencing. Updated multi host options
149 * screen for new artwork. Put in checks for host or team captains leaving
152 * 9 4/16/98 11:39p Dave
153 * Put in first run of new multiplayer options screen. Still need to
154 * complete final tab.
156 * 8 4/13/98 4:50p Dave
157 * Maintain status of weapon bank/links through respawns. Put # players on
158 * create game mission list. Make observer not have engine sounds. Make
159 * oberver pivot point correct. Fixed respawn value getting reset every
160 * time host options screen started.
162 * 7 4/09/98 11:01p Dave
163 * Put in new multi host options screen. Tweaked multiplayer options a
166 * 6 4/09/98 5:43p Dave
167 * Remove all command line processing from the demo. Began work fixing up
168 * the new multi host options screen.
170 * 5 4/06/98 6:37p Dave
171 * Put in max_observers netgame server option. Make sure host is always
172 * defaulted to alpha 1 or zeta 1. Changed create game so that MAX_PLAYERS
173 * can always join but need to be kicked before commit can happen. Put in
174 * support for server ending a game and notifying clients of a special
177 * 4 4/04/98 4:22p Dave
178 * First rev of UDP reliable sockets is done. Seems to work well if not
181 * 3 4/03/98 1:03a Dave
182 * First pass at unreliable guaranteed delivery packets.
184 * 2 3/31/98 4:51p Dave
185 * Removed medals screen and multiplayer buttons from demo version. Put in
186 * new pilot popup screen. Make ships in mp team vs. team have proper team
187 * ids. Make mp respawns a permanent option saved in the player file.
189 * 1 3/30/98 6:24p Dave
199 #include "osregistry.h"
201 #include "multimsgs.h"
202 #include "freespace.h"
203 #include "stand_gui.h"
204 #include "multiutil.h"
205 #include "multi_voice.h"
206 #include "multi_options.h"
207 #include "multi_team.h"
209 // ----------------------------------------------------------------------------------
210 // MULTI OPTIONS DEFINES/VARS
214 #define MULTI_OPTION_SERVER 0 // server update follows
215 #define MULTI_OPTION_LOCAL 1 // local netplayer options follow
216 #define MULTI_OPTION_START_GAME 2 // host's start game options on the standalone server
217 #define MULTI_OPTION_MISSION 3 // host's mission selection stuff on a standalone server
220 #define MULTI_CFG_FILE NOX("multi.cfg")
221 multi_global_options Multi_options_g;
223 char Multi_options_proxy[512] = "";
224 ushort Multi_options_proxy_port = 0;
226 // ----------------------------------------------------------------------------------
227 // MULTI OPTIONS FUNCTIONS
230 // load in the config file
231 #define NEXT_TOKEN() do { tok = strtok(NULL, "\n"); if(tok != NULL){ drop_leading_white_space(tok); drop_trailing_white_space(tok); } } while(0);
232 #define SETTING(s) ( !stricmp(tok, s) )
233 void multi_options_read_config()
239 // set default value for the global multi options
240 memset(&Multi_options_g, 0, sizeof(multi_global_options));
241 Multi_options_g.protocol = NET_TCP;
243 // do we have a forced port via commandline or registry?
244 ushort forced_port = (ushort)os_config_read_uint(NULL, "ForcePort", 0);
245 Multi_options_g.port = (Cmdline_network_port >= 0) ? (ushort)Cmdline_network_port : forced_port == 0 ? (ushort)DEFAULT_GAME_PORT : forced_port;
247 Multi_options_g.log = (Cmdline_multi_log) ? 1 : 0;
248 Multi_options_g.datarate_cap = OO_HIGH_RATE_DEFAULT;
249 strcpy(Multi_options_g.user_tracker_ip, "");
250 strcpy(Multi_options_g.game_tracker_ip, "");
251 strcpy(Multi_options_g.pxo_ip, "");
252 strcpy(Multi_options_g.pxo_rank_url, "");
253 strcpy(Multi_options_g.pxo_create_url, "");
254 strcpy(Multi_options_g.pxo_verify_url, "");
255 strcpy(Multi_options_g.pxo_banner_url, "");
258 Multi_options_g.std_max_players = -1;
259 Multi_options_g.std_datarate = OBJ_UPDATE_HIGH;
260 Multi_options_g.std_voice = 1;
261 memset(Multi_options_g.std_passwd, 0, STD_PASSWD_LEN);
262 memset(Multi_options_g.std_pname, 0, STD_NAME_LEN);
263 Multi_options_g.std_framecap = 30;
265 // read in the config file
266 in = cfopen(MULTI_CFG_FILE, "rt", CFILE_NORMAL, CF_TYPE_DATA);
268 // if we failed to open the config file, user default settings
270 nprintf(("Network","Failed to open network config file, using default settings\n"));
275 // read in the game info
279 // parse the first line
280 tok = strtok(str," \t");
284 drop_leading_white_space(tok);
285 drop_trailing_white_space(tok);
290 // all possible options
291 // only standalone cares about the following options
300 if(SETTING("+name")){
301 // set the standalone server's permanent name
304 strncpy(Multi_options_g.std_pname, tok, STD_NAME_LEN);
307 if(SETTING("+no_voice")){
308 // standalone won't allow voice transmission
309 Multi_options_g.std_voice = 0;
311 if(SETTING("+max_players")){
312 // set the max # of players on the standalone
315 if(!((atoi(tok) < 1) || (atoi(tok) > MAX_PLAYERS))){
316 Multi_options_g.std_max_players = atoi(tok);
327 if(SETTING("+passwd")){
328 // set the standalone host password
331 strncpy(Multi_options_g.std_passwd, tok, STD_PASSWD_LEN);
337 extern HWND Multi_std_host_passwd;
338 SetWindowText(Multi_std_host_passwd, Multi_options_g.std_passwd);
342 if(SETTING("+low_update")){
343 // set standalone to low updates
344 Multi_options_g.std_datarate = OBJ_UPDATE_LOW;
346 if(SETTING("+med_update")){
347 // set standalone to medium updates
348 Multi_options_g.std_datarate = OBJ_UPDATE_MEDIUM;
350 if(SETTING("+high_update")){
351 // set standalone to high updates
352 Multi_options_g.std_datarate = OBJ_UPDATE_HIGH;
354 if(SETTING("+lan_update")){
355 // set standalone to high updates
356 Multi_options_g.std_datarate = OBJ_UPDATE_LAN;
360 // common to all modes
361 if(SETTING("+user_server")){
362 // ip addr of user tracker
365 strcpy(Multi_options_g.user_tracker_ip, tok);
368 if(SETTING("+game_server")){
369 // ip addr of game tracker
372 strcpy(Multi_options_g.game_tracker_ip, tok);
375 if(SETTING("+chat_server")){
376 // ip addr of pxo chat server
379 strcpy(Multi_options_g.pxo_ip, tok);
382 if(SETTING("+rank_url")){
383 // url of pilot rankings page
386 strcpy(Multi_options_g.pxo_rank_url, tok);
389 if(SETTING("+create_url")){
390 // url of pxo account create page
393 strcpy(Multi_options_g.pxo_create_url, tok);
396 if(SETTING("+verify_url")){
397 // url of pxo account verify page
400 strcpy(Multi_options_g.pxo_verify_url, tok);
403 if(SETTING("+banner_url")){
404 // url of pxo account verify page
407 strcpy(Multi_options_g.pxo_banner_url, tok);
410 if(SETTING("+datarate")){
411 // set the max datarate for high updates
414 if(atoi(tok) >= 4000){
415 Multi_options_g.datarate_cap = atoi(tok);
419 if(SETTING("+http_proxy")){
420 // get the proxy server
423 char *ip = strtok(tok, ":");
425 strcpy(Multi_options_proxy, ip);
427 ip = strtok(NULL, "");
429 Multi_options_proxy_port = (ushort)atoi(ip);
431 strcpy(Multi_options_proxy, "");
437 // close the config file
442 // set netgame defaults
443 // NOTE : should be used when creating a newpilot
444 void multi_options_set_netgame_defaults(multi_server_options *options)
446 // any player can do squadmate messaging
447 options->squad_set = MSO_SQUAD_ANY;
449 // only the host can end the game
450 options->endgame_set = MSO_END_HOST;
452 // allow ingame file xfer and custom pilot pix
453 options->flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
455 // set the default time limit to be -1 (no limit)
456 options->mission_time_limit = fl2f(-1.0f);
458 // set the default max kills for a mission
459 options->kill_limit = 9999;
461 // set the default # of respawns
462 options->respawn = 2;
464 // set the default # of max observers
465 options->max_observers = 2;
467 // set the default netgame qos
468 options->voice_qos = 10;
470 // set the default token timeout
471 options->voice_token_wait = 2000; // he must wait 2 seconds between voice gets
473 // set the default max voice record time
474 options->voice_record_time = 5000;
477 // set local netplayer defaults
478 // NOTE : should be used when creating a newpilot
479 void multi_options_set_local_defaults(multi_local_options *options)
481 // accept pix by default and broadcast on the local subnet
482 options->flags = (MLO_FLAG_ACCEPT_PIX | MLO_FLAG_LOCAL_BROADCAST);
484 // set the object update level based on the type of network connection specified by the user
485 // at install (or launcher) time.
486 if ( Psnet_connection == NETWORK_CONNECTION_DIALUP ) {
487 options->obj_update_level = OBJ_UPDATE_LOW;
489 options->obj_update_level = OBJ_UPDATE_HIGH;
493 // fill in the passed netgame options struct with the data from my player file data (only host/server should do this)
494 void multi_options_netgame_load(multi_server_options *options)
497 memcpy(options,&Player->m_server_options,sizeof(multi_server_options));
501 // fill in the passed local options struct with the data from my player file data (all machines except standalone should do this)
502 void multi_options_local_load(multi_local_options *options, net_player *pxo_pl)
505 memcpy(options,&Player->m_local_options,sizeof(multi_local_options));
508 // stuff pxo squad info
511 strcpy(pxo_pl->p_info.pxo_squad_name, Multi_tracker_squad_name);
516 // update everyone on the current netgame options
517 void multi_options_update_netgame()
519 ubyte data[MAX_PACKET_SIZE],code;
522 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
524 // build the header and add the opcode
525 BUILD_HEADER(OPTIONS_UPDATE);
526 code = MULTI_OPTION_SERVER;
529 // add the netgame options
530 ADD_DATA(Netgame.options);
533 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
534 multi_io_send_to_all_reliable(data, packet_size);
536 multi_io_send_reliable(Net_player, data, packet_size);
540 // update everyone with my local settings
541 void multi_options_update_local()
543 ubyte data[MAX_PACKET_SIZE],code;
546 // if i'm the server, don't do anything
547 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
551 // build the header and add the opcode
552 BUILD_HEADER(OPTIONS_UPDATE);
553 code = MULTI_OPTION_LOCAL;
556 // add the netgame options
557 ADD_DATA(Net_player->p_info.options);
560 multi_io_send_reliable(Net_player, data, packet_size);
563 // update the standalone with the settings I have picked at the "start game" screen
564 void multi_options_update_start_game(netgame_info *ng)
566 ubyte data[MAX_PACKET_SIZE],code;
569 // should be a host on a standalone
570 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
573 BUILD_HEADER(OPTIONS_UPDATE);
574 code = MULTI_OPTION_START_GAME;
577 // add the start game options
578 ADD_STRING(ng->name);
580 ADD_DATA(ng->security);
582 // add mode-specific data
584 case NG_MODE_PASSWORD:
585 ADD_STRING(ng->passwd);
587 case NG_MODE_RANK_ABOVE:
588 case NG_MODE_RANK_BELOW:
589 ADD_DATA(ng->rank_base);
593 // send to the standalone server
594 multi_io_send_reliable(Net_player, data, packet_size);
597 // update the standalone with the mission settings I have picked (mission filename, etc)
598 void multi_options_update_mission(netgame_info *ng, int campaign_mode)
600 ubyte data[MAX_PACKET_SIZE],code;
603 // should be a host on a standalone
604 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
607 BUILD_HEADER(OPTIONS_UPDATE);
608 code = MULTI_OPTION_MISSION;
611 // type (coop or team vs. team)
612 ADD_DATA(ng->type_flags);
615 ADD_DATA(ng->respawn);
617 // add the mission/campaign filename
618 code = (ubyte)campaign_mode;
621 ADD_STRING(ng->campaign_name);
623 ADD_STRING(ng->mission_name);
626 // send to the server
627 multi_io_send_reliable(Net_player, data, packet_size);
631 // ----------------------------------------------------------------------------------
632 // MULTI OPTIONS FUNCTIONS
635 // process an incoming multi options packet
636 void multi_options_process_packet(unsigned char *data, header *hinfo)
639 multi_local_options bogus;
640 int idx,player_index;
642 int offset = HEADER_LENGTH;
644 // find out who is sending this data
645 player_index = find_player_id(hinfo->id);
647 // get the packet code
650 // get the start game options
651 case MULTI_OPTION_START_GAME:
652 Assert(Game_mode & GM_STANDALONE_SERVER);
654 // get the netgame name
655 GET_STRING(Netgame.name);
657 // get the netgame mode
658 GET_DATA(Netgame.mode);
660 // get the security #
661 GET_DATA(Netgame.security);
663 // get mode specific data
664 switch(Netgame.mode){
665 case NG_MODE_PASSWORD:
666 GET_STRING(Netgame.passwd);
668 case NG_MODE_RANK_ABOVE:
669 case NG_MODE_RANK_BELOW:
670 GET_DATA(Netgame.rank_base);
674 // update standalone stuff
675 std_connect_set_gamename(Netgame.name);
676 std_multi_update_netgame_info_controls();
679 // get mission choice options
680 case MULTI_OPTION_MISSION:
682 char title[NAME_LENGTH+1];
683 int campaign_type,max_players;
685 memset(&ng,0,sizeof(netgame_info));
687 Assert(Game_mode & GM_STANDALONE_SERVER);
689 // coop or team vs. team mode
690 GET_DATA(ng.type_flags);
691 if((ng.type_flags & NG_TYPE_TEAM) && !(Netgame.type_flags & NG_TYPE_TEAM)){
694 // if squad war was switched on
695 if((ng.type_flags & NG_TYPE_SW) && !(Netgame.type_flags & NG_TYPE_SW)){
696 mprintf(("STANDALONE TURNED ON SQUAD WAR!!\n"));
698 Netgame.type_flags = ng.type_flags;
701 GET_DATA(Netgame.respawn);
709 GET_STRING(ng.campaign_name);
711 // set the netgame max players here if the filename has changed
712 if(strcmp(Netgame.campaign_name,ng.campaign_name)){
713 memset(title,0,NAME_LENGTH+1);
714 if(!mission_campaign_get_info(ng.campaign_name,title,&campaign_type,&max_players)){
715 Netgame.max_players = 0;
717 Netgame.max_players = max_players;
720 strcpy(Netgame.campaign_name,ng.campaign_name);
723 Netgame.campaign_mode = 1;
725 // put brackets around the campaign name
726 if(Game_mode & GM_STANDALONE_SERVER){
728 strcat(str,Netgame.campaign_name);
730 std_multi_set_standalone_mission_name(str);
735 GET_STRING(ng.mission_name);
737 if(strcmp(Netgame.mission_name,ng.mission_name)){
738 if(strlen(ng.mission_name)){
739 Netgame.max_players = mission_parse_get_multi_mission_info( ng.mission_name );
741 // setting this to -1 will prevent us from being seen on the network
742 Netgame.max_players = -1;
744 strcpy(Netgame.mission_name,ng.mission_name);
745 strcpy(Game_current_mission_filename,Netgame.mission_name);
748 Netgame.campaign_mode = 0;
750 // set the mission name
751 if(Game_mode & GM_STANDALONE_SERVER){
752 std_multi_set_standalone_mission_name(Netgame.mission_name);
756 send_netgame_update_packet();
759 // get the netgame options
760 case MULTI_OPTION_SERVER:
761 GET_DATA(Netgame.options);
763 // if we're a standalone set for no sound, do so here
764 if((Game_mode & GM_STANDALONE_SERVER) && !Multi_options_g.std_voice){
765 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
767 // maybe update the quality of sound
768 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
771 // set the skill level
772 Game_skill_level = Netgame.options.skill_level;
774 if((Game_mode & GM_STANDALONE_SERVER) && !(Game_mode & GM_CAMPAIGN_MODE)){
775 Netgame.respawn = Netgame.options.respawn;
778 // if we have the "temp closed" flag toggle
779 if(Netgame.options.flags & MLO_FLAG_TEMP_CLOSED){
780 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
782 Netgame.options.flags &= ~(MLO_FLAG_TEMP_CLOSED);
784 // if i'm the standalone server, I should rebroadcast to all other players
785 if(Game_mode & GM_STANDALONE_SERVER){
786 for(idx=0;idx<MAX_PLAYERS;idx++){
787 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && (&Net_players[idx] != &Net_players[player_index]) ){
788 multi_io_send_reliable(&Net_players[idx], data, offset);
792 send_netgame_update_packet();
796 // local netplayer options
797 case MULTI_OPTION_LOCAL:
798 if(player_index == -1){
801 GET_DATA(Net_players[player_index].p_info.options);