File: multi_options.cpp

package info (click to toggle)
freespace2 24.2.0%2Brepack-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 43,716 kB
  • sloc: cpp: 595,001; ansic: 21,741; python: 1,174; sh: 457; makefile: 248; xml: 181
file content (888 lines) | stat: -rw-r--r-- 25,591 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
/*
 * Copyright (C) Volition, Inc. 1999.  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.
 *
*/




#include "cmdline/cmdline.h"
#include "osapi/osregistry.h"
#include "network/multi.h"
#include "network/multimsgs.h"
#include "network/multi_obj.h"
#include "freespace.h"
#include "network/stand_gui.h"
#include "network/multiutil.h"
#include "network/multi_voice.h"
#include "network/multi_options.h"
#include "network/multi_team.h"
#include "mission/missioncampaign.h"
#include "mission/missionparse.h"
#include "options/Option.h"
#include "parse/parselo.h"
#include "playerman/player.h"
#include "cfile/cfile.h"
#include "network/multi_fstracker.h"

#include <climits>



// ----------------------------------------------------------------------------------
// MULTI OPTIONS DEFINES/VARS
//

// packet codes
#define MULTI_OPTION_SERVER						0				// server update follows
#define MULTI_OPTION_LOCAL							1				// local netplayer options follow
#define MULTI_OPTION_START_GAME					2				// host's start game options on the standalone server
#define MULTI_OPTION_MISSION						3				// host's mission selection stuff on a standalone server

// global options
#define MULTI_CFG_FILE								NOX("multi.cfg")
multi_global_options Multi_options_g;

char Multi_options_proxy[512] = "";
ushort Multi_options_proxy_port = 0;
bool Multi_cfg_missing = true;

auto TogglePXOOption __UNUSED = options::OptionBuilder<bool>("Multi.TogglePXO",
									std::pair<const char*, int>{"PXO", 1383},
									std::pair<const char*, int>{"Whether or not to play games on the local network or on PXO", 1809})
									.category(std::make_pair("Multi", 1828))
									.level(options::ExpertLevel::Beginner)
									.default_val(true)
									.bind_to(&Multi_options_g.pxo)
									.importance(10)
									.flags({options::OptionFlags::RetailBuiltinOption})
									.finish();

// These next few options are a little messy because the values are stored in the player file
// rather than a global. The Change_Listener runs on game start and when the option is changed.
// The "initial" paramter is used to skip setting the player flag on game start because Player
// hasn't been initialized yet. On Player Load the static local globals are set based on the
// player flags and the options will read/save those as necessary. When an option is changed
// any time after startup the Change_Listener will set the player flags. We don't need to manually
// save the player flags because the player file already does that. So after the local globals
// and player flags are sync'd up at game start then we are good to go for the entire runtime.

static bool BroadcastGamesLocally = false;

static bool local_broadcast_change(bool val, bool initial)
{
	if (initial) {
		return false;
	} else {
		if (!val) {
			Player->m_local_options.flags &= ~(MLO_FLAG_LOCAL_BROADCAST);
		} else {
			Player->m_local_options.flags |= MLO_FLAG_LOCAL_BROADCAST;
		}
		BroadcastGamesLocally = (Player->m_local_options.flags & MLO_FLAG_LOCAL_BROADCAST) ? 1 : 0;
		return true;
	}
}

auto LocalBroadcastOption __UNUSED = options::OptionBuilder<bool>("Multi.LocalBroadcast",
									std::pair<const char*, int>{"Broadcast Locally", 1387},
									std::pair<const char*, int>{"Whether or not to broadcast games on the local network", 1808})
									.category(std::make_pair("Multi", 1828))
									.level(options::ExpertLevel::Beginner)
									.default_val(false)
                                    .bind_to(&BroadcastGamesLocally)
									.change_listener(local_broadcast_change)
									.importance(10)
									.flags({options::OptionFlags::RetailBuiltinOption})
									.finish();

static bool AlwaysFlushCache = false;

static bool flush_cache_change(bool val, bool initial)
{
	if (initial) {
		return false;
	} else {
		if (!val) {
			Player->m_local_options.flags &= ~(MLO_FLAG_FLUSH_CACHE);
		} else {
			Player->m_local_options.flags |= MLO_FLAG_FLUSH_CACHE;
		}
		AlwaysFlushCache = (Player->m_local_options.flags & MLO_FLAG_FLUSH_CACHE) ? 1 : 0;
		return true;
	}
}

static SCP_string flush_cache_display(bool mode) { return mode ? XSTR("Never", 1400) : XSTR("Before Game", 1401); }

auto FlushCacheOption __UNUSED = options::OptionBuilder<bool>("Multi.FlushCache",
									std::pair<const char*, int>{"Flush Cache", 1399},
									std::pair<const char*, int>{"Whether or not flush the multidata cache before games", 1810})
									.category(std::make_pair("Multi", 1828))
									.level(options::ExpertLevel::Beginner)
                                    .flags({options::OptionFlags::ForceMultiValueSelection, options::OptionFlags::RetailBuiltinOption})
                                    .display(flush_cache_display) 
									.default_val(false)
                                    .bind_to(&AlwaysFlushCache)
									.change_listener(flush_cache_change)
									.importance(10)
									.finish();

static bool CacheMissionsToMultidata = false;

static bool transfer_missions_change(bool val, bool initial)
{
	if (initial) {
		return false;
	} else {
		if (!val) {
			Player->m_local_options.flags &= ~(MLO_FLAG_XFER_MULTIDATA);
		} else {
			Player->m_local_options.flags |= MLO_FLAG_XFER_MULTIDATA;
		}
		CacheMissionsToMultidata = (Player->m_local_options.flags & MLO_FLAG_XFER_MULTIDATA) ? 1 : 0;
		return true;
	}
}

static SCP_string transfer_missions_display(bool mode) { return mode ? XSTR("/multidata", 1397) : XSTR("/missions", 1398); }

auto TransferMissionsOption __UNUSED = options::OptionBuilder<bool>("Multi.TransferMissions",
									std::pair<const char*, int>{"Transfer Missions", 1396},
									std::pair<const char*, int>{"What appdata folder to save missions to", 1811})
									.category(std::make_pair("Multi", 1828))
									.level(options::ExpertLevel::Beginner)
                                    .flags({options::OptionFlags::ForceMultiValueSelection, options::OptionFlags::RetailBuiltinOption})
                                    .display(transfer_missions_display) 
									.default_val(false)
                                    .bind_to(&CacheMissionsToMultidata)
									.change_listener(transfer_missions_change)
									.importance(10)
									.finish();

// ----------------------------------------------------------------------------------
// MULTI OPTIONS FUNCTIONS
//

// load in the config file
#define NEXT_TOKEN()						do { tok = strtok(NULL, "\n"); if(tok != NULL){ drop_leading_white_space(tok); drop_trailing_white_space(tok); } } while(false);
#define SETTING(s)						( !stricmp(tok, s) )
void multi_options_read_config()
{
	CFILE *in;
	char str[512];
	char *tok = NULL;

	// set default value for the global multi options
	Multi_options_g.reset();

	ushort forced_port = (ushort)os_config_read_uint(NULL, "ForcePort", 0);
	Multi_options_g.port = (Cmdline_network_port >= 0) ? (ushort)Cmdline_network_port : forced_port == 0 ? (ushort)DEFAULT_GAME_PORT : forced_port;
	Multi_options_g.log = (Cmdline_multi_log) ? 1 : 0;

	// read in the config file
	in = cfopen(MULTI_CFG_FILE, "rt", CFILE_NORMAL, CF_TYPE_DATA);
	
	// if we failed to open the config file, user default settings
	if (in == NULL) {
		nprintf(("Network","Failed to open network config file, using default settings\n"));		
	} else {
		Multi_cfg_missing = false;

		while ( !cfeof(in) ) {
			// read in the game info
			memset(str, 0, 512);
			cfgets(str, 512, in);

			// parse the first line
			tok = strtok(str, " \t");

			// check the token
			if (tok != NULL) {
				drop_leading_white_space(tok);
				drop_trailing_white_space(tok);			
			} else {
				continue;
			}		

			// all possible options

			// only standalone cares about the following options
			if (Is_standalone) {
				// setup PXO mode
				if ( SETTING("+pxo") ) {
					Multi_options_g.pxo = true;
					// force protocol to TCP
					Multi_options_g.protocol = NET_TCP;
					NEXT_TOKEN();
					if (tok != NULL) {
						strncpy(Multi_fs_tracker_channel, tok, MAX_PATH-1);
					}
				} else
				// set the standalone server's permanent name
				if	( SETTING("+name") ) {
					NEXT_TOKEN();
					if (tok != NULL) {
						strncpy(Multi_options_g.std_pname, tok, STD_NAME_LEN);
					}
				} else
				// standalone won't allow voice transmission
				if ( SETTING("+no_voice") ) {
					Multi_options_g.std_voice = 0;
				} else
				// set the max # of players on the standalone
				if ( SETTING("+max_players") ) {
					NEXT_TOKEN();
					if (tok != NULL) {
						if ( !((atoi(tok) < 1) || (atoi(tok) > MAX_PLAYERS)) ) {
							Multi_options_g.std_max_players = atoi(tok);
						}
					}
				} else
				// ban a player
				if ( SETTING("+ban") ) {
					NEXT_TOKEN();
					if (tok != NULL) {
						std_add_ban(tok);
					}
				} else
				// set the standalone host password
				if ( SETTING("+passwd") ) {
					NEXT_TOKEN();
					if (tok != NULL) {
						strncpy(Multi_options_g.std_passwd, tok, STD_PASSWD_LEN);
#ifdef _WIN32
						// yuck
						extern HWND Multi_std_host_passwd;
						SetWindowText(Multi_std_host_passwd, Multi_options_g.std_passwd);
#else
						// TODO: get password ?
						// argh, gonna have to figure out how to do this - mharris 07/07/2002
#endif
					}
				} else
				// set standalone to low updates
				if ( SETTING("+low_update") ) {
					Multi_options_g.std_datarate = OBJ_UPDATE_LOW;
				} else
				// set standalone to medium updates
				if ( SETTING("+med_update") ) {
					Multi_options_g.std_datarate = OBJ_UPDATE_MEDIUM;
				} else
				// set standalone to high updates
				if ( SETTING("+high_update") ) {
					Multi_options_g.std_datarate = OBJ_UPDATE_HIGH;
				} else
				// set standalone to high updates
				if ( SETTING("+lan_update") ) {
					Multi_options_g.std_datarate = OBJ_UPDATE_LAN;
				} else
				if ( SETTING("+webui_root") ) {
					NEXT_TOKEN();
					if (tok != NULL) {
						Multi_options_g.webuiRootDirectory = SCP_string(tok);
					}
				} else
				if ( SETTING("+webapi_username") ) {
					NEXT_TOKEN();
					if (tok != NULL) {
						Multi_options_g.webapiUsername = SCP_string(tok);
					}
				} else
				if ( SETTING("+webapi_password") ) {
					NEXT_TOKEN();
					if (tok != NULL) {
						Multi_options_g.webapiPassword = SCP_string(tok);
					}
				} else
				if ( SETTING("+webapi_server_port") ) {
					NEXT_TOKEN();
					if (tok != NULL) {
						long int result = strtol(tok, NULL, 10);
						if(result <= 0 || result > USHRT_MAX) {
							mprintf(("ERROR: Invalid or out of range webapi_server_port '%s' specified in multi.cfg, must be integer between 1024 and %i.\n", tok, USHRT_MAX));
						}
						else if(result < 1024) {
							mprintf(("ERROR: webapi_server_port '%ld' in multi.cfg is too low, must be between 1024 and %d.\n", result, USHRT_MAX));
						}
						else {
							mprintf(("Using webapi_server_port '%ld' from multi.cfg.\n", result));
							Multi_options_g.webapiPort = (ushort) result;
						}
					}
				}
			}

			// ... common to all modes ...

			// ip addr of user tracker
			if ( SETTING("+user_server") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					strcpy_s(Multi_options_g.user_tracker_ip, tok);
				}
			} else
			// ip addr of game tracker
			if ( SETTING("+game_server") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					strcpy_s(Multi_options_g.game_tracker_ip, tok);
				}
			} else
			// ip addr of pxo chat server
			if ( SETTING("+chat_server") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					strcpy_s(Multi_options_g.pxo_ip, tok);
				}
			} else
			// url of pilot rankings page
			if ( SETTING("+rank_url") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					strcpy_s(Multi_options_g.pxo_rank_url, tok);
				}
			} else
			// url of pxo account create page
			if ( SETTING("+create_url") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					strcpy_s(Multi_options_g.pxo_create_url, tok);
				}
			} else
			// url of pxo account verify page
			if ( SETTING("+verify_url") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					strcpy_s(Multi_options_g.pxo_verify_url, tok);
				}
			} else
			// url of pxo banners
			if ( SETTING("+banner_url") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					strcpy_s(Multi_options_g.pxo_banner_url, tok);
				}
			} else
			// set the max datarate for high updates
			if ( SETTING("+datarate") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					if ( atoi(tok) >= 4000 ) {
						Multi_options_g.datarate_cap = atoi(tok);
					}
				}			
			} else
			// get the proxy server
			if ( SETTING("+http_proxy") ) {
				NEXT_TOKEN();
				if (tok != NULL) {
					char *ip = strtok(tok, ":");

					if (ip != NULL) {
						strcpy_s(Multi_options_proxy, ip);
					}

					ip = strtok(NULL, "");

					if (ip != NULL) {
						Multi_options_proxy_port = (ushort)atoi(ip);
					} else {
						strcpy_s(Multi_options_proxy, "");
					}
				}
			}
		}

		// close the config file
		cfclose(in);
		in = NULL;
	}

	// sanitize config options for PXO
	multi_fs_tracker_verify_options();

#ifndef _WIN32
	if (Is_standalone) {
		std_configLoaded(&Multi_options_g);
	}
#endif
}

// set netgame defaults 
// NOTE : should be used when creating a newpilot
void multi_options_set_netgame_defaults(multi_server_options *options)
{
	// any player can do squadmate messaging
	options->squad_set = MSO_SQUAD_ANY;

	// only the host can end the game
	options->endgame_set = MSO_END_HOST;

	// allow ingame file xfer and custom pilot pix
	options->flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);

	// set the default time limit to be -1 (no limit)
	options->mission_time_limit = fl2f(-1.0f);

	// set the default max kills for a mission
	options->kill_limit = 9999;

	// set the default # of respawns
	options->respawn = 2;

	// set the default # of max observers
	options->max_observers = 2;

	// set the default netgame qos
	options->voice_qos = 10;

	// set the default token timeout
	options->voice_token_wait = 2000;				// he must wait 2 seconds between voice gets

	// set the default max voice record time
	options->voice_record_time = 5000;

	// Set default skill level to medium
	options->skill_level = NUM_SKILL_LEVELS / 2;
}

// set local netplayer defaults
// NOTE : should be used when creating a newpilot
void multi_options_set_local_defaults(multi_local_options *options)
{
	// accept pix by default and broadcast on the local subnet
	options->flags = (MLO_FLAG_ACCEPT_PIX | MLO_FLAG_LOCAL_BROADCAST);	

	// set the object update level based on the type of network connection specified by the user
	// at install (or launcher) time.
	if ( Psnet_connection == NETWORK_CONNECTION_DIALUP ) {
		options->obj_update_level = OBJ_UPDATE_LOW;
	} else {
		options->obj_update_level = Default_multi_object_update_level;
	}
}

// fill in the passed netgame options struct with the data from my player file data (only host/server should do this)
void multi_options_netgame_load(multi_server_options *options)
{
	if(options != NULL){
		memcpy(options,&Player->m_server_options,sizeof(multi_server_options));
	}	
}

// fill in the passed local options struct with the data from my player file data (all machines except standalone should do this)
void multi_options_local_load(multi_local_options *options, net_player *pxo_pl)
{
	if(options != NULL){
		memcpy(options,&Player->m_local_options,sizeof(multi_local_options));
	}

	// stuff pxo squad info
	if(pxo_pl != NULL){
		strcpy_s(pxo_pl->p_info.pxo_squad_name, Multi_tracker_squad_name);		
	}
}

// fill out the in-game options local globals using the player data
void multi_options_init_globals()
{
	BroadcastGamesLocally = (Player->m_local_options.flags & MLO_FLAG_LOCAL_BROADCAST) ? 1 : 0;
	AlwaysFlushCache = (Player->m_local_options.flags & MLO_FLAG_FLUSH_CACHE) ? 1 : 0;
	CacheMissionsToMultidata = (Player->m_local_options.flags & MLO_FLAG_XFER_MULTIDATA) ? 1 : 0;
}

// add data from a multi_server_options struct
void add_server_options(ubyte *data, int *size, const multi_server_options *mso)
{
	int packet_size = *size;

	// misc settings and flags
	ADD_DATA(mso->squad_set);
	ADD_DATA(mso->endgame_set);
	ADD_INT(mso->flags);

	// default respawn count
	ADD_UINT(mso->respawn);

	// default max # of observers
	ADD_DATA(mso->max_observers);

	// default skill level
	ADD_DATA(mso->skill_level);

	// voice settings
	ADD_DATA(mso->voice_qos);
	ADD_INT(mso->voice_token_wait);
	ADD_INT(mso->voice_record_time);

	// time limit
	ADD_INT(mso->mission_time_limit);

	// kill limit
	ADD_INT(mso->kill_limit);

	*size = packet_size;
}

// add data from a multi_local_options struct
void add_local_options(ubyte *data, int *size, const multi_local_options *mlo)
{
	int packet_size = *size;

	ADD_INT(mlo->flags);
	ADD_INT(mlo->obj_update_level);

	*size = packet_size;
}

// get data from multi_server_options struct
void get_server_options(ubyte *data, int *size, multi_server_options *mso)
{
	int offset = *size;

	// misc settings and flags
	GET_DATA(mso->squad_set);
	GET_DATA(mso->endgame_set);
	GET_INT(mso->flags);

	// default respawn count
	GET_UINT(mso->respawn);

	// default max # of observers
	GET_DATA(mso->max_observers);

	// default skill level
	GET_DATA(mso->skill_level);

	// voice settings
	GET_DATA(mso->voice_qos);
	GET_INT(mso->voice_token_wait);
	GET_INT(mso->voice_record_time);

	// time limit
	GET_INT(mso->mission_time_limit);

	// kill limit
	GET_INT(mso->kill_limit);

	*size = offset;
}

// get data from multi_local_options struct
void get_local_options(ubyte *data, int *size, multi_local_options *mlo)
{
	int offset = *size;

	GET_INT(mlo->flags);
	GET_INT(mlo->obj_update_level);

	*size = offset;
}

// update everyone on the current netgame options
void multi_options_update_netgame()
{
	ubyte data[MAX_PACKET_SIZE],code;
	int packet_size = 0;
	
	Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);

	// build the header and add the opcode
	BUILD_HEADER(OPTIONS_UPDATE);
	code = MULTI_OPTION_SERVER;
	ADD_DATA(code);

	// add the netgame options
	add_server_options(data, &packet_size, &Netgame.options);

	// send the packet
	if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
		multi_io_send_to_all_reliable(data, packet_size);
	} else {
		multi_io_send_reliable(Net_player, data, packet_size);
	}
}

// update everyone with my local settings
void multi_options_update_local()
{
	ubyte data[MAX_PACKET_SIZE],code;
	int packet_size = 0;
	
	// if i'm the server, don't do anything
	if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
		return;
	}

	// build the header and add the opcode
	BUILD_HEADER(OPTIONS_UPDATE);
	code = MULTI_OPTION_LOCAL;
	ADD_DATA(code);

	// add the netgame options
	add_local_options(data, &packet_size, &Net_player->p_info.options);

	// send the packet		
	multi_io_send_reliable(Net_player, data, packet_size);
}

// update the standalone with the settings I have picked at the "start game" screen
void multi_options_update_start_game(netgame_info *ng)
{
	ubyte data[MAX_PACKET_SIZE],code;
	int packet_size = 0;

	// should be a host on a standalone
	Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));

	// build the header
	BUILD_HEADER(OPTIONS_UPDATE);
	code = MULTI_OPTION_START_GAME;
	ADD_DATA(code);

	// add the start game options
	ADD_STRING(ng->name);
	ADD_INT(ng->mode);
	ADD_INT(ng->security);

	// add mode-specific data
	switch(ng->mode){
	case NG_MODE_PASSWORD:
		ADD_STRING(ng->passwd);
		break;
	case NG_MODE_RANK_ABOVE:
	case NG_MODE_RANK_BELOW:
		ADD_INT(ng->rank_base);
		break;
	}

	// send to the standalone server	
	multi_io_send_reliable(Net_player, data, packet_size);
}

// update the standalone with the mission settings I have picked (mission filename, etc)
void multi_options_update_mission(netgame_info *ng, int campaign_mode)
{
	ubyte data[MAX_PACKET_SIZE],code;
	int packet_size = 0;

	// should be a host on a standalone
	Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));

	// build the header
	BUILD_HEADER(OPTIONS_UPDATE);
	code = MULTI_OPTION_MISSION;
	ADD_DATA(code);

	// type (coop or team vs. team)
	ADD_INT(ng->type_flags);

	// respawns
	ADD_UINT(ng->respawn);

	// add the mission/campaign filename
	code = (ubyte)campaign_mode;
	ADD_DATA(code);
	if(campaign_mode){
		ADD_STRING(ng->campaign_name);
	} else {
		ADD_STRING(ng->mission_name);
	}

	// send to the server	
	multi_io_send_reliable(Net_player, data, packet_size);
}


// ----------------------------------------------------------------------------------
// MULTI OPTIONS FUNCTIONS
//

// process an incoming multi options packet
void multi_options_process_packet(unsigned char *data, header *hinfo)
{
	ubyte code;	
	multi_local_options bogus;
	int idx,player_index;
	char str[255];
	int offset = HEADER_LENGTH;

	// find out who is sending this data	
	player_index = find_player_index(hinfo->id);

	if (player_index < 0) {
		nprintf(("Network", "Received packet from unknown player!\n"));
		return;
	}

	// get the packet code
	GET_DATA(code);
	switch(code){
	// get the start game options
	case MULTI_OPTION_START_GAME:
		Assert(Game_mode & GM_STANDALONE_SERVER);

		// get the netgame name
		GET_STRING(Netgame.name);		

		// get the netgame mode
		GET_INT(Netgame.mode);

		// get the security #
		GET_INT(Netgame.security);

		// get mode specific data
		switch(Netgame.mode){
		case NG_MODE_PASSWORD:
			GET_STRING(Netgame.passwd);
			break;
		case NG_MODE_RANK_ABOVE:
		case NG_MODE_RANK_BELOW:
			GET_INT(Netgame.rank_base);
			break;
		}

		// update standalone stuff
		std_connect_set_gamename(Netgame.name);
		std_multi_update_netgame_info_controls();
		break;

	// get mission choice options
	case MULTI_OPTION_MISSION: {
		netgame_info ng;
		char title[NAME_LENGTH+1];
		int campaign_type,max_players;

		Assert(Game_mode & GM_STANDALONE_SERVER);

		// coop or team vs. team mode
		GET_INT(ng.type_flags);
		if((ng.type_flags & NG_TYPE_TEAM) && !(Netgame.type_flags & NG_TYPE_TEAM)){
			multi_team_reset();
		}
		// if squad war was switched on
		if((ng.type_flags & NG_TYPE_SW) && !(Netgame.type_flags & NG_TYPE_SW)){
			mprintf(("STANDALONE TURNED ON SQUAD WAR!!\n"));
		}
		Netgame.type_flags = ng.type_flags;

		// new respawn count
		GET_UINT(Netgame.respawn);

		// name string
		memset(str,0,255);

		GET_DATA(code);
		// campaign mode
		if(code){
			GET_STRING(ng.campaign_name);

			// set the netgame max players here if the filename has changed
			if(strcmp(Netgame.campaign_name,ng.campaign_name) != 0){				
				memset(title,0,NAME_LENGTH+1);			
				if(!mission_campaign_get_info(ng.campaign_name,title,&campaign_type,&max_players)){
					Netgame.max_players = 0;
				} else {
					Netgame.max_players = max_players;
				}

				strcpy_s(Netgame.campaign_name,ng.campaign_name);
			}

			Netgame.campaign_mode = 1;

			// put brackets around the campaign name
			if(Game_mode & GM_STANDALONE_SERVER){
				strcpy_s(str,"(");
				strcat_s(str,Netgame.campaign_name);
				strcat_s(str,")");
				std_multi_set_standalone_mission_name(str);
			}
		}
		// non-campaign mode
		else {
			GET_STRING(ng.mission_name);

			if(strcmp(Netgame.mission_name,ng.mission_name) != 0){
				if(strlen(ng.mission_name)){
					Netgame.max_players = mission_parse_get_multi_mission_info( ng.mission_name );
				} else {
					// setting this to -1 will prevent us from being seen on the network
					Netgame.max_players = -1;				
				}
				strcpy_s(Netgame.mission_name,ng.mission_name);
				strcpy_s(Game_current_mission_filename,Netgame.mission_name);				
			}			

			Netgame.campaign_mode = 0;
            
			// set the mission name
			if(Game_mode & GM_STANDALONE_SERVER){
				std_multi_set_standalone_mission_name(Netgame.mission_name);			
			}
		}

		send_netgame_update_packet();	   
		break;
	}

	// get the netgame options
	case MULTI_OPTION_SERVER:		
		get_server_options(data, &offset, &Netgame.options);

		// if we're a standalone set for no sound, do so here
		if((Game_mode & GM_STANDALONE_SERVER) && !Multi_options_g.std_voice){
			Netgame.options.flags |= MSO_FLAG_NO_VOICE;
		} else {
			// maybe update the quality of sound
			multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
		}

		// set the skill level
		Game_skill_level = Netgame.options.skill_level;		

		if((Game_mode & GM_STANDALONE_SERVER) && !(Game_mode & GM_CAMPAIGN_MODE)){
			Netgame.respawn = Netgame.options.respawn;
		}

		// if we have the "temp closed" flag toggle
		if(Netgame.options.flags & MLO_FLAG_TEMP_CLOSED){
			Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
		}
		Netgame.options.flags &= ~(MLO_FLAG_TEMP_CLOSED);

		// if i'm the standalone server, I should rebroadcast to all other players
		if(Game_mode & GM_STANDALONE_SERVER){
			for(idx=0;idx<MAX_PLAYERS;idx++){
				if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && (&Net_players[idx] != &Net_players[player_index]) ){
					multi_io_send_reliable(&Net_players[idx], data, offset);
				}
			}

			send_netgame_update_packet();
		}
		break;
	
	// local netplayer options
	case MULTI_OPTION_LOCAL:
		if(player_index == -1){
			get_local_options(data, &offset, &bogus);
		} else {
			get_local_options(data, &offset, &Net_players[player_index].p_info.options);

			//If the client has sent an object update higher than that which the server allows, reset it
			if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
				if (Net_players[player_index].p_info.options.obj_update_level > Cmdline_objupd) {
					Net_players[player_index].p_info.options.obj_update_level = Cmdline_objupd;
				}
			}
		}		
		break;
	}
	PACKET_SET_SIZE();
}