File: multi.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 (1907 lines) | stat: -rw-r--r-- 55,343 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
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
/*
 * 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 "network/multi.h"
#include "network/multiutil.h"
#include "network/multimsgs.h"
#include "ship/ship.h"
#include "io/timer.h"
#include "playerman/player.h"
#include "mission/missionparse.h"
#include "mission/missioncampaign.h"
#include "gamesequence/gamesequence.h"
#include "freespace.h"
#include "osapi/osapi.h"
#include "network/stand_gui.h"
#include "network/multi_xfer.h"
#include "network/multiui.h"
#include "network/multi_ingame.h"
#include "popup/popup.h"
#include "missionui/chatbox.h"
#include "model/modelreplace.h"
#include "network/multiteamselect.h"
#include "network/multi_data.h"
#include "network/multi_kick.h"
#include "network/multi_campaign.h"
#include "network/multi_voice.h"
#include "network/multi_team.h"
#include "network/multi_respawn.h"
#include "network/multi_pmsg.h"
#include "network/multi_endgame.h"
#include "missionui/missiondebrief.h"
#include "network/multi_pause.h"
#include "mission/missiongoals.h"
#include "network/multi_log.h"
#include "network/multi_rate.h"
#include "network/multi_lua.h"
#include "hud/hudescort.h"
#include "hud/hudmessage.h"
#include "globalincs/alphacolors.h"
#include "globalincs/pstypes.h"
#include "cfile/cfile.h"
#include "network/multi_fstracker.h"
#include "network/multi_sw.h"
#include "network/multi_portfwd.h"
#include "network/multi_turret_manager.h"
#include "pilotfile/pilotfile.h"
#include "debugconsole/console.h"
#include "network/psnet2.h"
#include "network/multi_mdns.h"
#include "cmdline/cmdline.h"

// Stupid windows workaround...
#ifdef MessageBox
#undef MessageBox
#endif



// ----------------------------------------------------------------------------------------
// Basic module scope defines
//
//

// timestamp defines
#define NETGAME_SEND_TIME								2						// time between sending netgame update packets
#define STATE_SEND_TIME									2						// time between sending netplayer state packets
#define GAMEINFO_SEND_TIME								3						// time between sending game information packets
#define PING_SEND_TIME									2						// time between player pings
#define BYTES_SENT_TIME									5						// every five seconds

// local network buffer stuff
#define MAX_NET_BUFFER									(1024 * 16)			// define and variable declaration for our local tcp buffer
#define NUM_REENTRANT_LEVELS							3

// time (in fixed seconds) to put up dialog about no contect from server
#define MULTI_SERVER_MAX_TIMEOUT						(F1_0 * 4)					// after this number of milliseoncds, stop client simulation
#define MULTI_SERVER_MAX_TIMEOUT_LARGE				(F1_0 * 40)					// done anytime not in mission
#define MULTI_SERVER_WAIT_TIME						(F1_0 * 60)					// wait 60 seconds to reconnect with the server
#define MULTI_SERVER_GONE								1
#define MULTI_SERVER_ALIVE								2

// define for when to show "slow network" icon
#define MULTI_SERVER_SLOW_PING_TIME					700					// when average ping time to server reaches this -- display hud icon

// update times for clients ships based on object update level
int Multi_client_update_intervals[MAX_OBJ_UPDATE_LEVELS]	= 
{
	333,				// Dialup, 3x a second
	166,				// Medium, 6x a second
	80,					// High, 12.5x a second
	30,					// LAN, 33x a second
};					

int Multi_display_netinfo = 1;

// ----------------------------------------------------------------------------------------
// Multiplayer vars
//
//

// net player vars		
net_player Net_players[MAX_PLAYERS];							// array of all netplayers in the game
net_player *Net_player;												// pointer to console's net_player entry

// netgame vars
netgame_info Netgame;												// netgame information
int Multi_mission_loaded = 0;										// flag, so that we don't load the mission more than once client side
int Ingame_join_net_signature = -1;								// signature for the player obj for use when joining ingame
int Multi_button_info_ok = 0;										// flag saying it is ok to apply critical button info on a client machine
int Multi_button_info_id = 0;										// identifier of the stored button info to be applying

// misc data
SCP_list<active_game> Active_games;							// list of active games displayed on the Join screen
CFILE* Multi_chat_stream;											// for streaming multiplayer chat strings to a file
int Multi_connection_speed;										// connection speed of this machine.
int Multi_num_players_at_start = 0;								// the # of players present (kept track of only on the server) at the very start of the mission
short Multi_id_num = 0;												// for assigning player id #'s

// permanent server list
server_item* Game_server_head;								// list of permanent game servers to be querying

// timestamp data
int Netgame_send_time = -1;							// timestamp used to send netgame info to players before misison starts
time_t State_send_time = -1;								// timestamp used to send state information to the host before a mission starts
int Gameinfo_send_time = -1;							// timestamp used by master to send game information to clients
time_t Next_ping_time = -1;								// when we should next ping all
int Multi_server_check_count = 0;					// var to keep track of reentrancy when checking server status
int Next_bytes_time = -1;								// bytes sent

// how often each player gets updated
UI_TIMESTAMP Multi_client_update_times[MAX_PLAYERS];	// client update packet timestamp

// local network buffer data
LOCAL ubyte net_buffer[NUM_REENTRANT_LEVELS][MAX_NET_BUFFER];
LOCAL ubyte Multi_read_count;

UI_TIMESTAMP Multi_restr_query_timestamp;
join_request Multi_restr_join_request;
net_addr Multi_restr_addr;
int Multi_join_restr_mode = -1;

LOCAL fix Multi_server_wait_start;				// variable to hold start time when waiting to reestablish with server

// non API master tracker vars
char Multi_tracker_login[MULTI_TRACKER_STRING_LEN+1] = "";
char Multi_tracker_passwd[MULTI_TRACKER_STRING_LEN+1] = "";
char Multi_tracker_squad_name[MULTI_TRACKER_STRING_LEN+1] = "";
int Multi_tracker_id = -1;
char Multi_tracker_id_string[255];

// current file checksum
ushort Multi_current_file_checksum = 0;
int Multi_current_file_length = -1;


// -------------------------------------------------------------------------------------------------
//	multi_init() is called only once, at game start-up.  Get player address + port, initialize the
// network players list.
//
//

void multi_init()
{
	int idx;

	// read in config file
	multi_options_read_config();

	Assert( Net_player == NULL );
	Multi_id_num = 0;

	// clear out all netplayers
	for(idx=0; idx<MAX_PLAYERS; idx++){
		Net_players[idx].init();
		Net_players[idx].reliable_socket = PSNET_INVALID_SOCKET;
	}

	// initialize the local netplayer
	Net_player = &Net_players[0];	
	Net_player->tracker_player_id = Multi_tracker_id;
	Net_player->m_player = Player;
	Net_player->flags = 0;	
	Net_player->s_info.xfer_handle = -1;
	Net_player->player_id = multi_get_new_id();
	Net_player->client_cinfo_seq = 0;
	Net_player->client_server_seq = 0;		

	// get our connection speed
	Multi_connection_speed = multi_get_connection_speed();			
	
	// initialize other stuff
	multi_log_init();

	// load up common multiplayer icons
	if (!Is_standalone)
		multi_load_common_icons();	

	// delete mvalid.cfg if it exists
	cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
}

// this is an important function which re-initializes any variables required in multiplayer games. 
// Always make sure globals you add are re-initialized here !!!!
void multi_vars_init()
{
	// initialize this variable right away.  Used in game_level_init for init'ing the player
	Next_ship_signature = SHIP_SIG_MIN;		
	Next_asteroid_signature = ASTEROID_SIG_MIN;
	Next_non_perm_signature = NPERM_SIG_MIN;   
	Next_debris_signature = DEBRIS_SIG_MIN;
	Next_waypoint_signature = WAYPOINT_SIG_MIN;
	
	// server-client critical stuff
	Multi_button_info_ok = 0;
	Multi_button_info_id = 0;

	// Ingame join stuff
	Ingame_join_net_signature = -1;

	// Netgame stuff
	Netgame.game_state = NETGAME_STATE_FORMING;	

	// team select stuff
	Multi_ts_inited = 0;	

	// load send stuff
	Multi_mission_loaded = 0;   // client side		

	// restricted game stuff
	Multi_restr_query_timestamp = UI_TIMESTAMP::invalid();

	// respawn stuff	
	Multi_server_check_count = 0;

	// reentrant variable
	Multi_read_count = 0;

	// current file checksum
	Multi_current_file_checksum = 0;
	Multi_current_file_length = -1;

	Active_games.clear();
	Game_server_head = NULL;

	// only the server should ever care about this
	Multi_id_num = 0;
}

// -------------------------------------------------------------------------------------------------
//	multi_level_init() is called whenever the player starts a multiplayer game
//
//

void multi_level_init() 
{
	int idx;

	// NETLOG
	ml_string(NOX("multi_level_init()"));

	// initialize the Net_players array
	for ( idx = 0; idx < MAX_PLAYERS; idx++) {
		// close all sockets down just for good measure
		psnet_rel_close_socket(Net_players[idx].reliable_socket);

		Net_players[idx].init();
		Net_players[idx].reliable_socket = PSNET_INVALID_SOCKET;

		Net_players[idx].s_info.xfer_handle = -1;
		Net_players[idx].p_info.team = 0;
	}

	// initialize the Players array
	for (idx=0;idx<MAX_PLAYERS;idx++) {
		if (Player == &Players[idx]) {
			continue;
		}
		Players[idx].reset();
	}

	multi_vars_init();	

	// initialize the fake lag/loss system
#ifdef MULTI_USE_LAG
	multi_lag_init();
#endif

	// initialize the kick system
	multi_kick_init();

	// initialize all file xfer stuff
	multi_xfer_init(multi_file_xfer_notify);

	// close the chatbox (if one exists)
	chatbox_close();	

	// reset the data xfer system
	multi_data_reset();

	// initialize the voice system
	multi_voice_init();

	// intialize the pause system
	multi_pause_reset();

	// initialize endgame stuff
	multi_endgame_init();

	// initialize respawning
	multi_respawn_init();

	// initialize all netgame timestamps
	multi_reset_timestamps();

	// flush psnet sockets
	psnet_flush();
}

// multi_check_listen() calls low level psnet routine to see if we have a connection from a client we
// should accept.
void multi_check_listen()
{
	int i;
	net_addr addr;
	PSNET_SOCKET_RELIABLE sock;

	// call psnet routine which calls select to see if we need to check for a connect from a client
	// by passing addr, we are telling check_for_listen to do the accept and return who it was from in
	// addr.  The
	sock = psnet_rel_check_for_listen(&addr);

	if (sock != PSNET_INVALID_SOCKET) {
		// be sure that my address and the server address are set correctly.
		if ( !psnet_same(&Psnet_my_addr, &Net_player->p_info.addr) ){
			Net_player->p_info.addr = Psnet_my_addr;
		}

		if ( !psnet_same(&Psnet_my_addr, &(Netgame.server_addr)) ){
			Netgame.server_addr = Psnet_my_addr;
		}

		// the connection was accepted in check_for_listen.  Find the netplayer whose address we connected
		// with and assign the socket descriptor.
		// Updated to utilize psnet_same() for address comparison so the port is also taken into account.
		// This allows multiple players using NAT to access a remote server simultneously.
		for (i = 0; i < MAX_PLAYERS; i++ ) {
			if ( (Net_players[i].flags & NETINFO_FLAG_CONNECTED) && (psnet_same(&addr, &(Net_players[i].p_info.addr))) ) {
				// mark this flag so we know he's "fully" connected
				Net_players[i].flags |= NETINFO_FLAG_RELIABLE_CONNECTED;
				Net_players[i].reliable_socket = sock;

				// send player information to the joiner
				send_accept_player_data( &Net_players[i], (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN)?1:0 );

				// send a netgame update so the new client has all the necessary settings
				send_netgame_update_packet();	

				// if this is a team vs. team game, send an update
				if(Netgame.type_flags & NG_TYPE_TEAM){
					multi_team_send_update();
				}

				// NETLOG
				ml_printf(NOX("Accepted TCP connection from %s"), Net_players[i].m_player == NULL ? NOX("Unknown") : Net_players[i].m_player->callsign);				
				break;
			}
		}

		// if we didn't find a player, close the socket
		if ( i == MAX_PLAYERS ) {
			nprintf(("Network", "Got accept on my listen socket, but unknown player.  Closing socket.\n"));
			psnet_rel_close_socket(sock);
		}
	}
}

// returns true is server hasn't been heard from in N seconds. false otherwise
int multi_client_server_dead()
{
	fix this_time, last_time, max;

	// get the last time we have heard from the server.  If greater than some default, then maybe
	// display some icon on the HUD indicating slow network connection.  if greater than some higher
	// max, stop simulating on the client side until we hear from the server again.
	this_time = timer_get_fixed_seconds();
	last_time = Netgame.server->last_heard_time;
	// check for wrap!  must return 0
	if ( last_time > this_time )
		return 0;

	this_time -= last_time;

	// if in mission, use the smaller timeout value.  Outside of mission, use a large one.
	if ( MULTI_IN_MISSION ){
		max = MULTI_SERVER_MAX_TIMEOUT;
	} else {
		max = MULTI_SERVER_MAX_TIMEOUT_LARGE;
	}

	if ( this_time > max){
		return 1;
	} else {
		return 0;
	}
}

void multi_process_incoming();		// prototype for function later in this module

// function to process network data in hopes of getting info back from server
int multi_client_wait_on_server()
{
	int is_dead;

	is_dead = multi_client_server_dead();

	// if the server is back alive, tell our popup
	if ( !is_dead ){
		return MULTI_SERVER_ALIVE;
	}

	// on release version -- keep popup active for 60 seconds, then bail
#ifdef NDEBUG
	fix this_time = timer_get_fixed_seconds();
	// if the timer wrapped:
	if ( this_time < Multi_server_wait_start ) {
		Multi_server_wait_start = timer_get_fixed_seconds();
		return FALSE;
	}
	// check to see if timeout expired
	this_time -= Multi_server_wait_start;
	if ( this_time > MULTI_SERVER_WAIT_TIME ){
		return MULTI_SERVER_GONE;
	}
#endif

	return FALSE;
}

// function called by multiplayer clients to stop simulating when they have not heard from the server
// in a while.
void multi_client_check_server()
{
	int rval;

	Assert( MULTIPLAYER_CLIENT );	

	// this function can get called while in the popup code below.  So we include this check as a
	// reentrancy check.
	if ( Multi_server_check_count )
		return;

	// make sure we have a valid server
	if(Netgame.server == NULL){
		return;
	}

	Multi_server_check_count++;
	if(multi_client_server_dead()){
		Netgame.flags |= NG_FLAG_SERVER_LOST;
	} else {
		Netgame.flags &= ~(NG_FLAG_SERVER_LOST);
	}

	if(Netgame.flags & NG_FLAG_SERVER_LOST) {
		if(!(Game_mode & GM_IN_MISSION) && !popup_active()){	
			// need to start a popup
			Multi_server_wait_start = timer_get_fixed_seconds();
			rval = popup_till_condition( multi_client_wait_on_server, XSTR("Cancel",641), XSTR("Contact lost with server.  Stopping simulation until contact reestablished.  Press Cancel to exit game.",642) );
			
			if ( !rval || (rval == MULTI_SERVER_GONE) ) {				
				multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONTACT_LOST);								
			}
			Netgame.flags &= ~(NG_FLAG_SERVER_LOST);
		}
	}

	Multi_server_check_count--;
}


// -------------------------------------------------------------------------------------------------
//	process_packet_normal() will determine what sort of packet it is, and send it to the appropriate spot.
//	Prelimiary verification of the magic number and checksum are done here.  
//

void process_packet_normal(ubyte* data, header *header_info, bool reliable)
{
	// this is for helping to diagnose misaligned packets.  The last sensible packet 
	// is usually the culprit that needs to be analyzed.
	if (Cmdline_dump_packet_type) {
		mprintf(("Game packet type of %d received.\n", data[0]));
	}

	switch ( data[0] ) {

		case JOIN:
			process_join_packet(data, header_info);
			break;

		case GAME_CHAT:
			process_game_chat_packet( data, header_info );
			break;

		case NOTIFY_NEW_PLAYER:
			process_new_player_packet(data, header_info);
			break;

		case HUD_MSG:
			process_hud_message(data, header_info);
			break;

		case MISSION_MESSAGE:
			process_mission_message_packet( data, header_info );
			break;

		case LEAVE_GAME:
			process_leave_game_packet(data, header_info);
			break;

		case GAME_QUERY:
			process_game_query(data, header_info);
			break;

		case GAME_ACTIVE:
			process_game_active_packet(data, header_info);
			break;

		case GAME_INFO:
			process_game_info_packet( data, header_info );
			break;		

		case SECONDARY_FIRED_AI:
			process_secondary_fired_packet(data, header_info, 0);
			break;		

		case SECONDARY_FIRED_PLR:
			process_secondary_fired_packet(data, header_info, 1);
			break;

		case COUNTERMEASURE_FIRED:
			process_countermeasure_fired_packet( data, header_info );
			break;		

		case FIRE_TURRET_WEAPON:
			process_turret_fired_packet( data, header_info );
			break;

		case GAME_UPDATE:
			process_netgame_update_packet( data, header_info );
			break;

		case UPDATE_DESCRIPT:
			process_netgame_descript_packet( data, header_info );
			break;

		case NETPLAYER_UPDATE:
			process_netplayer_update_packet( data, header_info );
			break;

		case ACCEPT :
			process_accept_packet(data, header_info);
			break;				

		case OBJECT_UPDATE:
			multi_oo_process_update(data, header_info);
			break;

		case SHIP_KILL:
			process_ship_kill_packet( data, header_info );
			break;

		case MISSILE_KILL:
			process_weapon_kill_packet(data, header_info);
			break;

		case WING_CREATE:
			process_wing_create_packet( data, header_info );
			break;
			
		case SHIP_CREATE:
			process_ship_create_packet( data, header_info );
			break;

		case SHIP_DEPART:
			process_ship_depart_packet( data, header_info );
			break;

		case MISSION_LOG_ENTRY:
			process_mission_log_packet( data, header_info );
			break;		

		case PING:
			process_ping_packet(data, header_info);
			break;

		case PONG:
			process_pong_packet(data, header_info);
			break;		

		case XFER_PACKET:
			Assert(header_info->id >= 0);
			int np_index;
			PSNET_SOCKET_RELIABLE sock;
			sock = PSNET_INVALID_SOCKET;

			// if I'm the server of the game, find out who this came from			
			if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
				np_index = find_player_index(header_info->id);
				if(np_index >= 0){
					sock = Net_players[np_index].reliable_socket;
				}
			}
			// otherwise always use my own socket
			else if(Net_player != NULL){
				sock = Net_player->reliable_socket;
			}
			
			header_info->bytes_processed = multi_xfer_process_packet(data + HEADER_LENGTH, sock) + HEADER_LENGTH;
			break;

		case MISSION_REQUEST:
			process_mission_request_packet(data,header_info);
			break;

		case MISSION_ITEM:
			process_mission_item_packet(data,header_info);
			break;		

		case MULTI_PAUSE_REQUEST:
			process_multi_pause_packet(data, header_info);
			break;		

		case INGAME_NAK:
			process_ingame_nak(data, header_info);
			break;

		case SHIPS_INGAME_PACKET:
			process_ingame_ships_packet(data, header_info);
			break;

		case WINGS_INGAME_PACKET:
			process_ingame_wings_packet(data, header_info);
			break;

		case MISSION_END:
			process_endgame_packet(data, header_info);
			break;

		case FORCE_MISSION_END:
			process_force_end_mission_packet(data, header_info);
			break;
		
		case OBSERVER_UPDATE:
			process_observer_update_packet(data, header_info);
			break;

		case NETPLAYER_SLOTS_P:
			process_netplayer_slot_packet(data, header_info);
			break;

		case SHIP_STATUS_CHANGE:
			process_ship_status_packet(data, header_info);
			break;

		case PLAYER_ORDER_PACKET:
			process_player_order_packet(data, header_info);
			break;

		case INGAME_SHIP_UPDATE:
			process_ingame_ship_update_packet(data, header_info);
			break;

		case INGAME_SHIP_REQUEST:
			process_ingame_ship_request_packet(data, header_info);
			break;
		
		case FILE_SIG_INFO:
			process_file_sig_packet(data, header_info);
			break;

		case RESPAWN_NOTICE:
			multi_respawn_process_packet(data,header_info);			
			break;

		case SUBSYSTEM_DESTROYED:
			process_subsystem_destroyed_packet( data, header_info );
			break;

		case LOAD_MISSION_NOW :
			process_netplayer_load_packet(data, header_info);
			break;

		case FILE_SIG_REQUEST :
			process_file_sig_request(data, header_info);
			break;

		case JUMP_INTO_GAME:
			process_jump_into_mission_packet(data, header_info);
			break;		

		case CLIENT_REPAIR_INFO:
			process_repair_info_packet(data,header_info);
			break;

		case MISSION_SYNC_DATA:
			process_mission_sync_packet(data,header_info);
			break;

		case STORE_MISSION_STATS:
			process_store_stats_packet(data, header_info);
			break;

		case DEBRIS_UPDATE:
			process_debris_update_packet(data, header_info);
			break;		

		case SHIP_WSTATE_CHANGE:
			process_ship_weapon_change( data, header_info );
			break;

		case WSS_UPDATE_PACKET:
			process_wss_update_packet(data, header_info);
			break;

		case WSS_REQUEST_PACKET:
			process_wss_request_packet( data, header_info );
			break;	

		case FIRING_INFO:
			process_firing_info_packet( data, header_info );
			break;		

		case CARGO_REVEALED:
			process_cargo_revealed_packet( data, header_info);
			break;		

		case CARGO_HIDDEN:
			process_cargo_hidden_packet( data, header_info);
			break;

		case SUBSYS_CARGO_REVEALED:
			process_subsystem_cargo_revealed_packet( data, header_info);
			break;		

		case SUBSYS_CARGO_HIDDEN:
			process_subsystem_cargo_hidden_packet( data, header_info);
			break;		

		case MISSION_GOAL_INFO:
			process_mission_goal_info_packet(data, header_info);
			break;

		case KICK_PLAYER:
			process_player_kick_packet(data, header_info);
			break;

		case PLAYER_SETTINGS:
			process_player_settings_packet(data, header_info);
			break;

		case DENY:
			process_deny_packet(data, header_info);
			break;

		case POST_SYNC_DATA:
			process_post_sync_data_packet(data, header_info);
			break;

		case WSS_SLOTS_DATA:
			process_wss_slots_data_packet(data,header_info);
			break;

		case SHIELD_EXPLOSION:
			process_shield_explosion_packet( data, header_info );
			break;

		case PLAYER_STATS:
			process_player_stats_block_packet(data, header_info);
			break;

		case SLOT_UPDATE:
			process_pslot_update_packet(data,header_info);
			break;

		case AI_INFO_UPDATE:
			process_ai_info_update_packet( data, header_info );
			break;		

		case CAMPAIGN_UPDATE :
			multi_campaign_process_update(data,header_info);
			break;

		case CAMPAIGN_UPDATE_INGAME:
			multi_campaign_process_ingame_start(data,header_info);
			break;

		case VOICE_PACKET :
			multi_voice_process_packet(data,header_info);
			break;

		case TEAM_UPDATE :
			multi_team_process_packet(data,header_info);
			break;

		case ASTEROID_INFO:
			process_asteroid_info(data, header_info);
			break;		

		case HOST_RESTR_QUERY:
			process_host_restr_packet(data, header_info);
			break;

		case OPTIONS_UPDATE:
			multi_options_process_packet(data,header_info);
			break;

		case SQUADMSG_PLAYER:
			multi_msg_process_squadmsg_packet(data,header_info);
			break;

		case NETGAME_END_ERROR:
			process_netgame_end_error_packet(data,header_info);
			break;

		case COUNTERMEASURE_SUCCESS:
			process_countermeasure_success_packet( data, header_info );
			break;

		case CLIENT_UPDATE:
			process_client_update_packet(data, header_info);
			break;

		case COUNTDOWN:
			process_countdown_packet(data, header_info);
			break;

		case DEBRIEF_INFO:
			process_debrief_info( data, header_info );
			break;

		case ACCEPT_PLAYER_DATA:
			process_accept_player_data( data, header_info );
			break;				

		case HOMING_WEAPON_UPDATE:
			process_homing_weapon_info( data, header_info );
			break;		

		case EMP_EFFECT:
			process_emp_effect(data, header_info);
			break;

		case REINFORCEMENT_AVAIL:
			process_reinforcement_avail( data, header_info );
			break;

		case PRIMARY_FIRED_NEW:
			process_NEW_primary_fired_packet(data, header_info);
			break;

		case LINEAR_WEAPON_FIRED:
			process_non_homing_fired_packet(data, header_info);
			break;

		case ANIMATION_TRIGGERED:
			process_animation_triggered_packet(data, header_info);
			break;

		case COUNTERMEASURE_NEW:
			process_NEW_countermeasure_fired_packet(data, header_info);
			break;

		case BEAM_FIRED:
			process_beam_fired_packet(data, header_info);
			break;		
			
		case SW_STD_QUERY:
			process_sw_query_packet(data, header_info);
			break;

		case EVENT_UPDATE:
			process_event_update_packet(data, header_info);
			break;

		case VARIABLE_UPDATE:
			process_variable_update_packet(data, header_info);
			break;

		case OBJECT_UPDATE_NEW:			
			multi_oo_process_update(data, header_info);
			break;

		case WEAPON_DET:
			process_weapon_detonate_packet(data, header_info);
			break;

		case FLAK_FIRED:
			process_flak_fired_packet(data, header_info);
			break;

		case NETPLAYER_PAIN:
			process_player_pain_packet(data, header_info);
			break;

		case LIGHTNING_PACKET:
			process_lightning_packet(data, header_info);
			break;

		case BYTES_SENT:
			process_bytes_recvd_packet(data, header_info);
			break;

		case TRANSFER_HOST:
			process_host_captain_change_packet(data, header_info);
			break;

		case SELF_DESTRUCT:
			process_self_destruct_packet(data, header_info);
			break;

		case SEXP:
			process_sexp_packet(data, header_info);
			break; 

		case TURRET_TRACK:
			process_turret_tracking_packet(data, header_info);
			break;

		case LUA_DATA_PACKET:
			process_lua_packet(data, header_info, reliable);
			break;

		default:
			mprintf(("Received packet with unknown type %d\n", data[0] ));
			header_info->bytes_processed = 10000;
			break;

	} // end switch

	// Let's also dump the amount of data that we've processed so far.
	if (Cmdline_dump_packet_type) {
		mprintf(("Game packet ended.  Total amount of data processed from packet is %d.\n", header_info->bytes_processed));
	}
}



// Takes a bunch of messages, check them for validity,
// and pass them to multi_process_data. 
//  --------------------^
// this should be process_packet() I think, or with the new code
// process_tracker_packet() as defined in MultiTracker.[h,cpp]
void multi_process_bigdata(ubyte *data, int len, net_addr *from_addr, int reliable)
{
	int bytes_processed;
	int player_num;
	header header_info;
	ubyte *buf;	

	// the only packets we will process from an unknown player are GAME_QUERY, GAME_INFO, JOIN, PING, PONG, ACCEPT, and GAME_ACTIVE packets
	player_num = find_player(from_addr);		

	// find the player who sent the message and mark the last_heard time for this player
	// check to see if netplayer is null (it may be in cases such as getting lists of games from the tracker)
	if(player_num >= 0){
		Net_players[player_num].last_heard_time = timer_get_fixed_seconds();
	}

	// store fields that were passed along in the message
	// store header information that was captured from the network-layer header
	memcpy(header_info.addr, from_addr->addr, sizeof(header_info.addr));
	header_info.port = from_addr->port;	
	if(player_num >= 0){
		header_info.id = Net_players[player_num].player_id;
	} else {
		header_info.id = -1;
	}   

	bytes_processed = 0;

	// start off logging of packets by writing how many bytes of data we should get through.
	if (Cmdline_dump_packet_type) {
		mprintf(("Network packet with %d bytes of data received. ", len));
	}

	while( (bytes_processed >= 0) && (bytes_processed < len) )  {

      buf = &(data[bytes_processed]);

      const ubyte type = buf[0];

		// if its coming from an unknown source, there are only certain packets we will actually process
		if((player_num == -1) && !multi_is_valid_unknown_packet((ubyte)type)){
			// So let's log it, because we should probably at least be vaguely aware of the buggy behavior.
			mprintf(("Receiving unknown source packet of type %d that should have a source, aborting packet processing!\n", type));
			return ;
		}		

		// perform any special processing checks here		
		process_packet_normal(buf,&header_info, reliable != 0);
		 
		// MWA -- magic number was removed from header on 8/4/97.  Replaced with bytes_processed
		// variable which gets stuffed whenever a packet is processed.
		bytes_processed += header_info.bytes_processed;
	}

	// if this is not reliable data and we have a valid player
	if(Net_player != NULL){
		if(!MULTIPLAYER_MASTER && !reliable && (Game_mode & GM_IN_MISSION)){
			Net_player->cl_bytes_recvd += len;
		}
	}
}

// process all reliable socket details
void multi_process_reliable_details()
{
	int idx;
	int sock_status;

	// run reliable sockets
	psnet_rel_work();

	
	// server operations
	if ( MULTIPLAYER_MASTER ){
		// listen for new reliable socket connections
		multi_check_listen();		

		// check for any broken sockets and delete any players
		for(idx=0; idx<MAX_PLAYERS; idx++){
			// players who _should_ be validly connected
			if((idx != MY_NET_PLAYER_NUM) && MULTI_CONNECTED(Net_players[idx])){				
				// if this guy's socket is broken or disconnected, kill him
				sock_status = psnet_rel_get_status(Net_players[idx].reliable_socket);
				if((sock_status == RNF_UNUSED) || (sock_status == RNF_BROKEN) || (sock_status == RNF_DISCONNECTED)){
					ml_string("Shutting down rel socket because of disconnect!");
					delete_player(idx);
				}

				// if we're still waiting for this guy to connect on his reliable socket and he's timed out, boot him
				if(Net_players[idx].s_info.reliable_connect_time != -1){
					// if he's connected
					if(Net_players[idx].reliable_socket != PSNET_INVALID_SOCKET){
						Net_players[idx].s_info.reliable_connect_time = -1;
					} 
					// if he's timed out
					else if(((time(nullptr) - Net_players[idx].s_info.reliable_connect_time) > MULTI_RELIABLE_CONNECT_WAIT) && (Net_players[idx].reliable_socket == PSNET_INVALID_SOCKET)){
						ml_string("Player timed out while connecting on reliable socket!");
						delete_player(idx);
					}
				}			
			}
		}
	}	
	// clients should detect broken sockets
	else {
		extern unsigned int Serverconn;
		if(Serverconn != 0xffffffff){
			int status = psnet_rel_get_status(Serverconn);
			if(status == RNF_BROKEN){
				mprintf(("CLIENT SOCKET DISCONNECTED\n"));

				// quit the game
				if(!multi_endgame_ending()){
					multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONTACT_LOST);
				}
			}
		}
	}
}

// multi_process_incoming reads incoming data off the unreliable and reliable ports and sends
// the data to process_big_data
void multi_process_incoming()
{
	int size;
	ubyte *data, *savep;
	net_addr from_addr;	

	Assert( Multi_read_count < NUM_REENTRANT_LEVELS );
	savep = net_buffer[Multi_read_count];

	Multi_read_count++;

	data = savep;

	// get the other net players data
	while( (size = psnet_get(data, &from_addr))>0 )	{
		// ingame joiners will ignore UDP packets until they are have picked a ship and are in the mission
		if( (Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Net_player->state != NETPLAYER_STATE_INGAME_SHIP_SELECT) ){
			nprintf(("Network","Tossing UDP like a good little ingame joiner...\n"));
		} 
		// otherwise process incoming data normally
		else {
			multi_process_bigdata(data, size, &from_addr, 0);
		}
	} // end while

	// read reliable sockets for data
	data = savep;
	int idx;

	if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
		for (idx=0;idx<MAX_PLAYERS;idx++) {
			if((Net_players[idx].flags & NETINFO_FLAG_CONNECTED) && (Net_player != NULL) && (Net_player->player_id != Net_players[idx].player_id)){
				while( (size = psnet_rel_get(Net_players[idx].reliable_socket, data, MAX_NET_BUFFER)) > 0){
					multi_process_bigdata(data, size, &Net_players[idx].p_info.addr, 1);
				}
			}
		}
	} else {
		// if I'm not the master of the game, read reliable data from my connection with the server
		if((Net_player->reliable_socket != PSNET_INVALID_SOCKET) && (Net_player->reliable_socket != 0)){
			while( (size = psnet_rel_get(Net_player->reliable_socket,data, MAX_NET_BUFFER)) > 0){				
				multi_process_bigdata(data, size, &Netgame.server_addr, 1);
			}
		}
	}
		
	Multi_read_count--;
}

// -------------------------------------------------------------------------------------------------
//	multi_do_frame() is called once per game loop do update all the multiplayer objects, and send
// the player data to all the other net players.
//
//

int eye_tog = 1;
DCF(eye_tog, "Toggles setting of the local player eyepoint on every frame (Multiplayer)")
{
	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("proper eye stuff is %s\n", eye_tog ? "ON" : "OFF");
		return;
	}

	eye_tog = !eye_tog;
	dc_printf("proper eye stuff is %s\n", eye_tog ? "ON" : "OFF");
}

void multi_do_frame()
{	
	PSNET_TOP_LAYER_PROCESS();

	// always set the local player eye position/orientation here so we know its valid throughout all multiplayer
	// function calls
	if((Net_player != NULL) && eye_tog){
		camid cid = player_get_cam();
		if(cid.isValid())
		{
			camera *cam = cid.getCamera();
			cam->get_info(&Net_player->s_info.eye_pos, &Net_player->s_info.eye_orient);
		}
	}

	// send all buffered packets from the previous frame
	multi_io_send_buffered_packets();

	// datarate tracking
	multi_rate_process();

	// always process any pending endgame details
	multi_endgame_process();		

	// process all reliable socket details, including :
	// 1.) Listening for new pending reliable connections (server)
	// 2.) Checking for broken sockets (server/client)
	// 3.) Checking for clients who haven't fully connected
	multi_process_reliable_details();	

	// get the other net players data
	multi_process_incoming();		

	// process object update datarate stuff (for clients and server both)
	multi_oo_rate_process();

	// clients should check when last time they heard from sever was -- if too long, then
	// pause the simulation so wait for it to possibly come back
	if ( (MULTIPLAYER_CLIENT) && (Net_player->flags & NETINFO_FLAG_CONNECTED) ){
		multi_client_check_server();
	}

	// everybody pings all the time	
	if((Next_ping_time < 0) || ((time(NULL) - Next_ping_time) > PING_SEND_TIME) ){
		if( (Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
			send_netplayer_update_packet();
		}
		
		// ping everyone
		multi_ping_send_all();
		Next_ping_time = time(NULL);		
	}	
	
	// if I am the master, and we are not yet actually playing the mission, send off netgame
	// status to all other players in the game.  If I am not the master of the game, and we
	// are not in the game, then send out my netplayer status to the host
	if ( (Net_player->flags & NETINFO_FLAG_CONNECTED) && !(Game_mode & GM_IN_MISSION)){	
		if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {			
			if ( (Netgame_send_time < 0) || ((time(NULL) - Netgame_send_time) > NETGAME_SEND_TIME) ) {
				send_netgame_update_packet(nullptr, true);
				
				Netgame_send_time = (int) time(NULL);
			}		
		} else {
			if ( (State_send_time < 0) || ((time(NULL) - State_send_time) > STATE_SEND_TIME) ){
				// observers shouldn't send an update state packet
				if ( !(Net_player->flags & NETINFO_FLAG_OBSERVER) ){
					send_netplayer_update_packet();
				}				
				
				State_send_time = time(NULL);
			}
		}
	}
	else if ( (Net_player->flags & NETINFO_FLAG_CONNECTED) && (Game_mode & GM_IN_MISSION) ) {	
		// if I am connected and am in the mission, do things that need to be done on a regular basis
		if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
			if ( (Gameinfo_send_time < 0) || ((time(NULL) - Gameinfo_send_time) > GAMEINFO_SEND_TIME)){
				send_game_info_packet();
				
				Gameinfo_send_time = (int) time(NULL);
			}
			
			// for any potential respawns
			multi_respawn_handle_invul_players();
			multi_respawn_check_ai();

			// for any potential ingame joiners
			multi_handle_ingame_joiners();
		} else {
			// the clients need to do some processing of stuff as well			
		}
	}

	// check to see if we're waiting on confirmation for a restricted ingame join
	if(Multi_restr_query_timestamp.isValid()){
		// if it has elapsed, unset the ingame join flag
		if(ui_timestamp_elapsed(Multi_restr_query_timestamp)){
			Multi_restr_query_timestamp = UI_TIMESTAMP::invalid();
			Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);		
		}	
	}

	// while in the mission, send my PlayerControls to the host so that he can process
	// my movement
	if ( Game_mode & GM_IN_MISSION ) {

		if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){					
			if(Net_player->flags & NETINFO_FLAG_OBSERVER){
				// if the rate limiting system says its ok
				if(multi_oo_cirate_can_send()){
					// send my observer position/object update
					send_observer_update_packet();
				}
			} else if ( !(Player_ship->is_departing() ) ){
				// if the rate limiting system says its ok
				if(multi_oo_cirate_can_send()){
					// use the new method
					multi_oo_send_control_info();
				}				
			}

			// bytes received info
			if( (Next_bytes_time < 0) || ((time(NULL) - Next_bytes_time) > BYTES_SENT_TIME) ){
				if(Net_player != NULL){
					send_bytes_recvd_packet(Net_player);

					// reset bytes recvd
					Net_player->cl_bytes_recvd = 0;
				}

				// reset timestamp
				Next_bytes_time = (int) time(NULL);				
			}
		} else {

			// right before sending new positions, we should do any rollback shots and resimulation
			multi_ship_record_do_rollback();

			// sending new objects from here is dependent on having objects only created after
			// the game is done moving the objects.  I think that I can enforce this.				
			multi_oo_process();

			// send updates for turret tracking.
			Multi_Turret_Manager.send_queued_packets();			

			// evaluate whether the time limit has been reached or max kills has been reached
			// Commented out by Sandeep 4/12/98, was causing problems with testing.
			if( ((f2fl(Netgame.options.mission_time_limit) > 0.0f) && (Missiontime > Netgame.options.mission_time_limit)) ||
				 multi_kill_limit_reached() ) {

				// make sure we don't do this more than once
				if(Netgame.game_state == NETGAME_STATE_IN_MISSION){				
					multi_handle_end_mission_request();									
				}
			}			
		}			
	}

	// periodically send a client update packet to all clients
	if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
		int idx;
		for(idx=0;idx<MAX_PLAYERS;idx++){
			if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
				if ( !Multi_client_update_times[idx].isValid() || ui_timestamp_elapsed_safe(Multi_client_update_times[idx], MILLISECONDS_PER_SECOND) ) {
					
					send_client_update_packet(&Net_players[idx]);
					
					Multi_client_update_times[idx] = ui_timestamp(Multi_client_update_intervals[Net_players[idx].p_info.options.obj_update_level]);
				}
			}
		}
	}	


	// process any kicked player details
	multi_kick_process();

	// do any file xfer details
	multi_xfer_do();

	// process any player data details (wav files, pilot pics, etc)
	multi_data_do();

	// do any voice details
	multi_voice_process();

	// process any player messaging details
	multi_msg_process();		
	
	// process any tracker messages
	multi_fs_tracker_process();

	// Cyborg17 update the new frame recording system for accurate client shots, needs to go after most everything else in multi.
	if (Game_mode & GM_IN_MISSION) {
		multi_ship_record_increment_frame();
	}

	// if on the standalone, do any gui stuff
	if (Game_mode & GM_STANDALONE_SERVER) {
		std_do_gui_frame();
	}	

	// if master then maybe do port forwarding setup/refresh/wait
	if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
		multi_port_forward_do();

		// do mdns stuff here too
		if ( !MULTI_IS_TRACKER_GAME && (Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST) ) {
			multi_mdns_service_do();
		}
	}
}

// -------------------------------------------------------------------------------------------------
//	multi_pause_do_frame() is called once per game loop do update all the multiplayer objects, and send
// the player data to all the other net players when the multiplayer game is paused. It only will do 
// checking for a few specialized packets (MULTI_UNPAUSE, etc)
//

void multi_pause_do_frame()
{
	PSNET_TOP_LAYER_PROCESS();

	// always set the local player eye position/orientation here so we know its valid throughout all multiplayer
	// function calls
	// if((Net_player != NULL) && eye_tog){
		// player_get_eye(&Net_player->s_info.eye_pos, &Net_player->s_info.eye_orient);
	// }

	// send all buffered packets from the previous frame
	multi_io_send_buffered_packets();

	// always process any pending endgame details
	multi_endgame_process();		

	// process all reliable socket details, including :
	// 1.) Listening for new pending reliable connections (server)
	// 2.) Checking for broken sockets (server/client)
	// 3.) Checking for clients who haven't fully connected
	multi_process_reliable_details();	

	// these timestamps and handlers shoul be evaluated in the pause state
	if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
		if ( (Gameinfo_send_time < 0) || ((time(NULL) - Gameinfo_send_time) > GAMEINFO_SEND_TIME) ){
			send_game_info_packet();
			
			Gameinfo_send_time = (int) time(NULL);
		}				
	}

	// everybody pings all the time
	if((Next_ping_time < 0) || ((time(NULL) - Next_ping_time) > PING_SEND_TIME) ){
		multi_ping_send_all();
		
		Next_ping_time = time(NULL);
	}

	// periodically send a client update packet to all clients
	if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
		int idx;

		for(idx=0;idx<MAX_PLAYERS;idx++){
			if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){			
				if ( !Multi_client_update_times[idx].isValid() || ui_timestamp_elapsed_safe(Multi_client_update_times[idx], MILLISECONDS_PER_SECOND) ) {
					
					send_client_update_packet(&Net_players[idx]);
					
					Multi_client_update_times[idx] = ui_timestamp(Multi_client_update_intervals[Net_players[idx].p_info.options.obj_update_level]);
				}
			}				
		}

		// for any potential ingame joiners
		multi_handle_ingame_joiners();
	}	

	// do any file xfer details
	multi_xfer_do();

	// process any player data details (wav files, pilot pics, etc)
	multi_data_do();

	// get the other net players data
	multi_process_incoming();	

	// do any voice details
	multi_voice_process();

	// process any player messaging details
	multi_msg_process();

	// process any kicked player details
	multi_kick_process();

	// process any pending endgame details
	multi_endgame_process();

	// process object update stuff (for clients and server both)
	if(MULTIPLAYER_MASTER){
		multi_oo_process();
	}

	// if on the standalone, do any gui stuff
	if (Game_mode & GM_STANDALONE_SERVER) {
		std_do_gui_frame();
	}

	// if master then maybe do port forwarding setup/refresh/wait
	if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
		multi_port_forward_do();

		// do mdns stuff here too
		if ( !MULTI_IS_TRACKER_GAME && (Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST) ) {
			multi_mdns_service_do();
		}
	}
}




// --------------------------------------------------------------------------------
// standalone_main_init()  the standalone equivalent of the main menu
//

extern int sock_inited;
float frame_time = (float)1.0/(float)30.0;

void standalone_main_init()
{
   std_debug_set_standalone_state_string("Main Init");   

	Game_mode = (GM_STANDALONE_SERVER | GM_MULTIPLAYER);	

	// NETLOG
	ml_string(NOX("Standalone server initializing"));

	// read in config file
	// multi_options_read_config();   
#ifdef _WIN32
	// if we failed to startup on our desired protocol, fail
	if ( !psnet_is_active() ) {
		if (Psnet_failure_code == WSAEADDRINUSE) {
			os::dialogs::Message(os::dialogs::MESSAGEBOX_ERROR, XSTR("You have selected TCP/IP for multiplayer FreeSpace, but the TCP socket is already in use.  Check for another instance and/or use the \"-port <port_num>\" command line option to select an available port.", 1604));
		}
		else {
			os::dialogs::Message(os::dialogs::MESSAGEBOX_ERROR, XSTR("You have selected TCP/IP for multiplayer FreeSpace, but the TCP/IP protocol was not detected on your machine.", 362));
		}

		exit(1);
	}
#endif // ifdef _WIN32

	// clear out the Netgame structure and start filling in the values
	// NOTE : these values are not incredibly important since they will be overwritten by the host when he joins
	Netgame.init();
	Netgame.game_state = NETGAME_STATE_FORMING;		// game is currently starting up
	Netgame.security = 0;
	Netgame.server_addr = Psnet_my_addr;

	The_mission.Reset( );
		
	// reinitialize all systems	
	multi_level_init();	

	// intialize endgame stuff
	multi_endgame_init();

	// clear the file xfer system
	multi_xfer_reset();
	multi_xfer_force_dir(CF_TYPE_MULTI_CACHE);

	// setup a blank pilot (this is a standalone usage only!)
	Pilot.load_player(NULL);

	// setup the netplayer for the standalone
	Net_player = &Net_players[0];	
	Net_player->tracker_player_id = -1;
	Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING | NETINFO_FLAG_MISSION_OK);
	Net_player->state = NETPLAYER_STATE_WAITING;
	Net_player->m_player = Player;
	strcpy_s(Player->callsign, "server");
	Net_player->p_info.addr = Psnet_my_addr;
	Net_player->s_info.xfer_handle = -1;	
	Net_player->player_id = multi_get_new_id();	
	Netgame.server = Net_player; 

	// maybe flag the game as having a hacked ships.tbl
	/*if(!Game_ships_tbl_valid){
		Netgame.flags |= NG_FLAG_HACKED_SHIPS_TBL;
	}
	// maybe flag the game as having a hacked weapons.tbl
	if(!Game_weapons_tbl_valid){
		Netgame.flags |= NG_FLAG_HACKED_WEAPONS_TBL;
	}*/

	// hacked data
	if(game_hacked_data()){
		Netgame.flags |= NG_FLAG_HACKED_SHIPS_TBL;
		Net_player->flags |= NETINFO_FLAG_HAXOR;
	}

	// setup debug flags
	Netgame.debug_flags = 0;

	// setup the default game name for the standalone
	std_connect_set_gamename(NULL);

	// set netgame default options
	multi_options_set_netgame_defaults(&Netgame.options);

	// set local netplayer default options
	multi_options_set_local_defaults(&Net_player->p_info.options);

	// set our object update level from the standalone default	
	Net_player->p_info.options.obj_update_level = Multi_options_g.std_datarate;
	switch(Net_player->p_info.options.obj_update_level){
	case OBJ_UPDATE_LOW:
		nprintf(("Network","STANDALONE USING LOW UPDATES\n"));
		break;
	case OBJ_UPDATE_MEDIUM:
		nprintf(("Network","STANDALONE USING MEDIUM UPDATES\n"));
		break;
	case OBJ_UPDATE_HIGH:
		nprintf(("Network","STANDALONE USING HIGH UPDATE\n"));
		break;
	case OBJ_UPDATE_LAN:
		nprintf(("Network","STANDALONE USING LAN UPDATE\n"));
		break;
	}

	// clear out various things
	animation::ModelAnimationParseHelper::parseTables();
	psnet_flush();
	game_flush();
	virtual_pof_init();
	ship_init();

	// setup port forwarding
	multi_port_forward_init();

	// setup mdns
	if ( !MULTI_IS_TRACKER_GAME ) {
		multi_mdns_service_init();
	}

	// login to game tracker
	std_tracker_login();

	std_debug_set_standalone_state_string("Main Do");
	std_set_standalone_fps((float)0);
	std_multi_set_standalone_missiontime((float)0);

	// load my missions and campaigns
	multi_create_list_load_missions();
	multi_create_list_load_campaigns();

	// if this is a tracker game, validate missions
	if(MULTI_IS_TRACKER_GAME){
		multi_update_valid_missions();
	}
}


// --------------------------------------------------------------------------------
// standalone_main_do()
//

// DESCRIPTION : the standalone server will wait in this state until the host of the game 
//               is "Waiting". That is, his state==NETPLAYER_STATE_WAITING, and he has finished
//               doing everything and wants to play the game. Once this happens, we will jump
//               into GS_STATE_MULTI_SERVER_WAIT

void standalone_main_do()
{
 
   os_sleep(10);  // since nothing will really be going on here, we can afford to give some time
               // back to the operating system.

	// kind of a do-nothing spin state.
	// The standalone will eventually move into the GS_STATE_MULTI_MISSION_SYNC state when a host connects and
	// attempts to start a game

   // process/renew port mapping
   multi_port_forward_do();

	// handle mdns messages
	if ( !MULTI_IS_TRACKER_GAME ) {
		multi_mdns_service_do();
	}
}

// --------------------------------------------------------------------------------
// standalone_main_close()
//

void standalone_main_close()
{
   std_debug_set_standalone_state_string("Main Close");

	// disconnect game from tracker
	multi_fs_tracker_logout();

	// remove port forwarding
	multi_port_forward_close();

	// stop mdns
	multi_mdns_service_close();
}

void multi_standalone_reset_all()
{	
	int idx;

	// NETLOG
	ml_string(NOX("Standalone resetting"));
	
	// shut all game stuff down
	game_level_close();

	// reinitialize the gui
	std_reset_standalone_gui();	

	// close down all sockets
	for(idx=0;idx<MAX_PLAYERS;idx++){

		// 6/25/98 -- MWA -- call delete_player here to remove the player.  This closes down the socket
		// and marks the player as not connected anymore.  It is probably cleaner to do this.
		if ( &Net_players[idx] != Net_player ) {
			delete_player( idx );
		}		
	}

	// make sure we go to the proper state.	
	if(gameseq_get_state() == GS_STATE_STANDALONE_MAIN){
		standalone_main_init();
	}
	gameseq_post_event(GS_EVENT_STANDALONE_MAIN);	
}

// --------------------------------------------------------------------------------
// multi_server_wait_init()  do stuff like setting the status bits correctly
//

void multi_standalone_wait_init()
{	
	std_debug_set_standalone_state_string("Wait Do");
	std_multi_add_goals();   // fill in the goals for the mission into the tree view
	multi_reset_timestamps();

	// create the bogus standalone object
	multi_create_standalone_object();
}


// --------------------------------------------------------------------------------
// multi_server_wait_do_frame() wait for everyone to log in or the host to send commands
// 

// DESCRIPTION : we will be in this state once the host of the game is waiting for everyone
//               to be finished and ready to go, at which point, we will will tell everyone
//               to enter the game, and we will start simulating ourselves. Note that most of
//               this code is lifted from multi_wait_do_frame()
void multi_standalone_wait_do()
{
}

// --------------------------------------------------------------------------------
// multi_server_wait_close() cleanup
//

void multi_standalone_wait_close()
{
	std_debug_set_standalone_state_string("Wait Close / Game Play");
	
	// all players should reset sequencing
	int idx;
	for(idx=0;idx<MAX_PLAYERS;idx++){
		if(Net_player->flags & NETINFO_FLAG_CONNECTED){
			Net_players[idx].client_cinfo_seq = 0;
			Net_players[idx].client_server_seq = 0;	
		}
	}
}


// this is an artificial state which will push the standalone into the main state without it having to go through 
// the init function (which would disconnect everyone and generally just screw things up)
// it will also eventually do tracker stats update
extern int Multi_debrief_server_framecount;
void multi_standalone_postgame_init()	
{
	std_debug_set_standalone_state_string("Postgame / Send Stats");

	// NETLOG
	ml_string(NOX("Standlone entering postgame"));

	mission_goal_fail_incomplete();

	// handle campaign stuff
	if ( Game_mode & GM_CAMPAIGN_MODE ) {
		// MUST store goals and events first - may be used to evaluate next mission
		// store goals and events
		mission_campaign_store_goals_and_events_and_variables();

		// evaluate next mission
		mission_campaign_eval_next_mission();
	}	

	// always set my state to be "DEBRIEF_ACCEPT"
	Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;	

	// mark stats as not being store yet
	Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);

	Multi_debrief_server_framecount = 0;

	// reset network timestamps
	multi_reset_timestamps();
}

void multi_standalone_postgame_do()
{
	// wait until everyone is in the debriefing
	if((Netgame.game_state != NETGAME_STATE_DEBRIEF) && multi_netplayer_state_check(NETPLAYER_STATE_DEBRIEF, 1)){		
		Netgame.game_state = NETGAME_STATE_DEBRIEF;
		send_netgame_update_packet();
		debrief_multi_server_stuff();
	}
	
	// process server debriefing details
	if(Netgame.game_state == NETGAME_STATE_DEBRIEF){
		multi_debrief_server_process();
	}
}

void multi_standalone_postgame_close()
{
	// maybe store stats on tracker
	if ( MULTI_IS_TRACKER_GAME && !(Netgame.flags & NG_FLAG_STORED_MT_STATS) ) {
		if (multi_debrief_stats_accept_code() != 0) {
			int stats_saved = multi_fs_std_tracker_store_stats();

			if (stats_saved) {
				Netgame.flags |= NG_FLAG_STORED_MT_STATS;
				send_netgame_update_packet();
			} else {
				send_store_stats_packet(0);
			}

			if (Netgame.type_flags & NG_TYPE_SW) {
				multi_sw_report(stats_saved);
			}
		}
	}
}


void multi_reset_timestamps()
{
	int i;

	for ( i = 0 ; i < MAX_PLAYERS; i++ ){
		Multi_client_update_times[i] = UI_TIMESTAMP::invalid();
	}
	Netgame_send_time = -1;
	Gameinfo_send_time = -1;	
	Next_ping_time = -1;
	State_send_time = -1;
	Next_bytes_time = -1;

	chatbox_reset_timestamps();

	// do for all players so that ingame joiners work properly.
	for (i = 0; i < MAX_PLAYERS; i++ ) {
		Players[i].update_dumbfire_time = timestamp(0);
		Players[i].update_lock_time = timestamp(0);

		Net_players[i].s_info.voice_token_timestamp = UI_TIMESTAMP::invalid();
		Net_players[i].s_info.player_collision_timestamp = TIMESTAMP::immediate();
	}

	// reset standalone gui timestamps (these are not game critical, so there is not much danger)
	std_reset_timestamps();
}

// netgame debug flags for debug console stuff
DCF(netd, "change netgame debug flags (Mulitplayer)")
{
	int value;
	dc_stuff_int(&value);
	
	// if we're the server, change flags
	if ((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (value <= 7)) {
		Netgame.debug_flags ^= (1 << value);
	}

	// display network flags
	dc_printf("BITS\n");
}

// display any multiplayer/networking information here
void multi_display_netinfo()
{
	int sx = gr_screen.center_offset_x + gr_screen.center_w - 200;
	int sy = gr_screen.center_offset_y + 20;
	int dy = gr_get_font_height() + 1;
	int idx;

	// not multiplayer
	if(!(Game_mode & GM_MULTIPLAYER)){
		return;
	}

	// HUD is turned off
	if (!HUD_draw) {
		return;
	}

	// message window is open
	if (Player->flags & PLAYER_FLAGS_MSG_MODE) {
		return;
	}

	gr_set_color_fast(&Color_normal);

	// server or client
	if(MULTIPLAYER_MASTER){
		gr_string(sx, sy, "SERVER", GR_RESIZE_NONE); sy += dy;

		for(idx=0; idx<MAX_PLAYERS; idx++){
			if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].m_player != NULL)){
				if(Net_players[idx].sv_last_pl < 0){
					gr_printf_no_resize(sx, sy, "%s: %d, %d pl", Net_players[idx].m_player->callsign, Net_players[idx].sv_bytes_sent, 0);
					sy += dy;
				} else {
					gr_printf_no_resize(sx, sy, "%s: %d, %d pl", Net_players[idx].m_player->callsign, Net_players[idx].sv_bytes_sent, Net_players[idx].sv_last_pl);
					sy += dy;
				}
			}
		}
	} else {
		gr_string(sx, sy, "CLIENT", GR_RESIZE_NONE); sy += dy;

		// display PL
		if(Net_player != NULL){
			if(Net_player->cl_last_pl < 0){
				gr_printf_no_resize(sx, sy, "PL: %d %d pl\n", Net_player->cl_bytes_recvd, 0);
				sy += dy;
			} else {
				gr_printf_no_resize(sx, sy, "PL: %d %d pl\n", Net_player->cl_bytes_recvd, Net_player->cl_last_pl);
				sy += dy;
			}
		}
	}
}