/* NETWORK_SERVER_MANAGER.C symbols in this file: 0011B5D0 0030: _countdown_timer_update (0000) 0011B600 0060: _countdown_timer_get_time_remaining (0000) 0011B660 0090: _countdown_timer_increment (0000) 0011B6F0 0090: _countdown_timer_decrement (0000) 0011B780 0040: _countdown_timer_set_time_remaining (0000) 0011B7C0 0070: _network_game_server_set_game_name (0000) 0011B830 0040: _network_game_server_get_game_name (0000) 0011B870 0040: _network_game_server_get_state (0000) 0011B8B0 0050: _network_game_server_open_game (0000) 0011B900 0050: _network_game_server_close_game (0000) 0011B950 0060: _network_game_server_game_is_open (0000) 0011B9B0 0060: _network_game_server_game_is_valid (0000) 0011BA10 00d0: _network_game_server_send_player_quit_messages_ingame (0000) 0011BAE0 00e0: _network_game_server_start_network_game (0000) 0011BBC0 00a0: _network_game_server_switch_to_postgame (0000) 0011BC60 00f0: _network_game_server_graceful_shutdown (0000) 0011BD50 0060: _network_game_server_client_machine_is_joined_to_game (0000) 0011BDB0 0130: _network_game_server_accept_client_machine_into_game (0000) 0011BEE0 0040: _player_name_is_unique (0000) 0011BF20 0080: _get_unique_random_name (0000) 0011BFA0 0090: _get_unique_random_color (0000) 0011C030 0140: _network_game_server_add_player_to_game (0000) 0011C170 00e0: _network_game_server_remove_player_from_game (0000) 0011C250 00a0: _network_game_server_adjust_machine_settings (0000) 0011C2F0 0080: _network_game_server_all_machines_have_loaded (0000) 0011C370 00c0: _network_game_server_client_machine_game_loading_complete (0000) 0011C430 0030: _network_game_server_client_machine_is_precached (0000) 0011C460 0150: _network_game_server_handle_client_update_packet (0000) 0011C5B0 0050: _network_game_server_switch_machine_from_postgame_to_pregame (0000) 0011C600 01b0: _network_game_server_update_ticks (0000) 0011C7B0 0070: _network_game_server_queue_player_for_addition (0000) 0011C820 0070: _network_game_server_begin_game_start_countdown (0000) 0011C890 0080: _server_needs_more_teams (0000) 0011C910 0090: _server_has_a_player_on_each_machine (0000) 0011C9A0 0070: _server_has_enough_machines (0000) 0011CA10 0050: _server_ok_to_countdown (0000) 0011CA60 0040: _network_game_server_invalidate_network_machine (0000) 0011CAA0 00a0: _network_game_generate_join_game_token (0000) 0011CB40 0090: _network_game_server_get_client_machine (0000) 0011CBD0 0030: _network_game_server_get_connection (0000) 0011CC00 0020: _network_game_server_get_client_connection (0000) 0011CC20 0080: _network_game_server_get_machine_connection (0000) 0011CCA0 0050: _network_game_server_get_client_machine_at_index (0000) 0011CCF0 00d0: _network_game_server_get_client_machine_at_address (0000) 0011CDC0 0040: _network_game_server_get_game (0000) 0011CE00 0090: _network_game_server_get_oldest_client_update_received (0000) 0011CE90 0050: _network_game_server_game_can_start (0000) 0011CEE0 0060: _network_game_server_pause_countdown (0000) 0011CF40 0100: _network_game_server_change_map_name (0000) 0011D040 0090: _network_game_server_change_game_variant (0000) 0011D0D0 0170: _code_0011d0d0 (0000) 0011D240 00a0: _code_0011d240 (0000) 0011D2E0 00a0: _network_game_server_send_rejection_message (0000) 0011D380 0030: _network_game_server_reject_connection_game_is_full (0000) 0011D3B0 0050: _code_0011d3b0 (0000) 0011D400 0070: _code_0011d400 (0000) 0011D470 00d0: _code_0011d470 (0000) 0011D540 0030: _network_game_server_get_client_machine_count (0000) 0011D570 0100: _dump_network_game_data (0000) 0011D670 0130: _network_game_server_dump (0000) 0011D7A0 0140: _network_game_server_remove_client_machine_from_game (0000) 0011D8E0 0140: _network_game_server_remove_machine_from_game (0000) 0011DA20 01d0: _network_game_server_stalled_on_client (0000) 0011DBF0 01e0: _network_game_server_update_countdown (0000) 0011DDD0 01d0: _code_0011ddd0 (0000) 0011DFA0 02b0: _code_0011dfa0 (0000) 0011E250 0120: _network_game_server_dispose (0000) 0011E370 0180: _network_game_server_idle (0000) 0011E4F0 0250: _network_game_server_reset_to_pregame (0000) 0011E740 0150: _network_game_server_create (0000) 00285104 001b: ??_C@_0BL@DEKPBLAE@timer?9?$DOtime_remaining?5?$DO?$DN?50?$AA@ (0000) 00285120 0033: ??_C@_0DD@CFCGIJJL@c?3?2halo?2SOURCE?2networking?2networ@ (0000) 00285154 0010: ??_C@_0BA@LLGNDOEI@adjustment?5?$DO?$DN?50?$AA@ (0000) 00285164 0007: ??_C@_06HJHJCKIO@server?$AA@ (0000) 0028516C 000d: ??_C@_0N@JPPODHNK@opening?5game?$AA@ (0000) 0028517C 000d: ??_C@_0N@DMKJHMIA@closing?5game?$AA@ (0000) 0028518C 0032: ??_C@_0DC@GCAPAPEJ@?$CITRUE?5?$DN?$DN?5game_is_open?$CJ?5?$HM?$HM?5?$CIFALSE@ (0000) 002851C0 0034: ??_C@_0DE@CFAOOLOB@?$CITRUE?5?$DN?$DN?5game_is_valid?$CJ?5?$HM?$HM?5?$CIFALS@ (0000) 002851F8 0086: ??_C@_0IG@MNBBLADP@network_game_server_send_message@ (0000) 00285280 0024: ??_C@_0CE@IKCKJLGP@sending?5quit?5out?5of?5game?0?5time?5?$DN@ (0000) 002852A4 0033: ??_C@_0DD@OJCFEAMH@_network_game_server_state_ingam@ (0000) 002852D8 0043: ??_C@_0ED@MDKIICLM@failed?5to?5signal?5client?5machines@ (0000) 0028531C 003d: ??_C@_0DN@JMGGBBEG@signalling?5client?5machines?5to?5be@ (0000) 0028535C 003a: ??_C@_0DK@JICNHAFB@failed?5to?5create?5a?5_message_type@ (0000) 00285398 003b: ??_C@_0DL@ICMNGBFO@failed?5to?5signal?5all?5client?5mach@ (0000) 002853D4 002d: ??_C@_0CN@DJJDFOFP@server?5sent?5message_game_over?5to@ (0000) 00285408 0045: ??_C@_0EF@JHLALKAD@server?5going?5down?0?5but?5failed?5to@ (0000) 00285450 0040: ??_C@_0EA@EJEKFFFL@server?5closing?5down?$DL?5all?5client?5@ (0000) 00285490 003d: ??_C@_0DN@KLHIBLCI@failed?5to?5create?5a?5message_serve@ (0000) 002854D0 003e: ??_C@_0DO@IAODKJJI@failed?5to?5create?5a?5message_serve@ (0000) 00285510 0008: ??_C@_07MHDNFCJE@machine?$AA@ (0000) 00285518 006e: ??_C@_0GO@BGKBFJIE@network_game_server_accept_clien@ (0000) 00285588 005b: ??_C@_0FL@EIIOKBIG@network_game_add_machine?$CI?$CJ?5faile@ (0000) 002855E4 003b: ??_C@_0DL@MGNGFOBG@server?5added?5machine?5?$EA?5?$CFs?5to?5the@ (0000) 00285620 004c: ??_C@_0EM@PJMBGAIG@client?5machine?5tried?5to?5add?5a?5pl@ (0000) 00285670 004d: ??_C@_0EN@DNDECHNF@network_game_add_player?$CI?$CJ?5failed@ (0000) 002856C0 0049: ??_C@_0EJ@LCGCJMBE@server?5added?5player?5from?5machine@ (0000) 00285710 004f: ??_C@_0EP@COKGKJNJ@client?5machine?5tried?5to?5remove?5a@ (0000) 00285760 0055: ??_C@_0FF@BKKCLGKI@network_game_remove_player?$CI?$CJ?5fai@ (0000) 002857B8 004d: ??_C@_0EN@LDDPDGNO@server?5removed?5player?5from?5machi@ (0000) 00285808 004d: ??_C@_0EN@KMKKCDOM@client?5machine?5tried?5to?5update?5i@ (0000) 00285858 0056: ??_C@_0FG@IPMBAPCA@network_game_update_machine?$CI?$CJ?5fa@ (0000) 002858B0 0024: ??_C@_0CE@NCDPADPN@server?5updated?5machine?5?$CD?$CFd?5setti@ (0000) 002858D4 0029: ??_C@_0CJ@ECFINOOJ@server?5?$CG?$CG?5machine?5?$CG?$CG?5machine_des@ (0000) 00285900 001b: ??_C@_0BL@PFJPBEPF@local?5game?5data?5not?5loaded?$AA@ (0000) 0028591C 0026: ??_C@_0CG@JHLMGKLE@all?5machines?5have?5successfully?5l@ (0000) 00285944 002f: ??_C@_0CP@JBNFJJAE@still?5waiting?5on?5machine?5?$CD?$CFd?5to?5@ (0000) 00285978 0047: ??_C@_0EH@NKIEAGCA@client?5update?5packet?5from?5machin@ (0000) 002859C0 0040: ??_C@_0EA@BIPOHPNI@received?5an?5outdated?5client?5upda@ (0000) 00285A00 004b: ??_C@_0EL@GBABNBCE@client?5machine?5?$CD?$CFd?5is?5out?5of?5syn@ (0000) 00285A4C 000f: ??_C@_0P@OKCBNOPP@message_packet?$AA@ (0000) 00285A5C 0031: ??_C@_0DB@OKJCLNBN@machine?5?$CD?$CFd?5has?5successfully?5swi@ (0000) 00285A90 0012: ??_C@_0BC@HJLNGPOP@server?5?$CG?$CG?5machine?$AA@ (0000) 00285AA8 0085: ??_C@_0IF@CCOGGKBF@network_game_server_send_player_@ (0000) 00285B30 002e: ??_C@_0CO@BNMPLHFO@server?5failed?5to?5add?5a?5network?5p@ (0000) 00285B60 005d: ??_C@_0FN@BDPHLOBF@server?5failed?5to?5send?5game?5updat@ (0000) 00285BC0 0011: ??_C@_0BB@JLJAPHKN@server?5?$CG?$CG?5player?$AA@ (0000) 00285BD4 0024: ??_C@_0CE@KMCEHK@server?5game?5start?5countdown?5star@ (0000) 00285BF8 000b: ??_C@_0L@DEEHFDNB@join_token?$AA@ (0000) 00285C04 003c: ??_C@_0DM@GFDOGMMN@client_machine?9?$DOmachine_index?$DMMA@ (0000) 00285C40 0019: ??_C@_0BJ@PDBLHLMI@server?5?$CG?$CG?5client_machine?$AA@ (0000) 00285C5C 002c: ??_C@_0CM@HFLLHELF@server?5?$CG?$CG?5network_machine_is_val@ (0000) 00285C88 0030: ??_C@_0DA@BAJANDFN@server?5?$CG?$CG?5?$CIindex?$DMMAXIMUM_NETWORK@ (0000) 00285CB8 001b: ??_C@_0BL@OEHAOKDP@no?5machine?5found?5?$EA?5ip?5?$CD?$CFlX?$AA@ (0000) 00285CD4 0026: ??_C@_0CG@LADAADEM@server?9?$DOclient_machines?$FLi?$FN?4conne@ (0000) 00285CFC 0015: ??_C@_0BF@BGCFGMDN@server?5?$CG?$CG?5ip_address?$AA@ (0000) 00285D18 0056: ??_C@_0FG@ENHBPECG@network_game_server_change_map_n@ (0000) 00285D70 0034: ??_C@_0DE@OPKCBBPP@server?9?$DOstate?5?$DN?$DN?5_network_game_s@ (0000) 00285DA4 0022: ??_C@_0CC@DFOMMGAI@server?5?$CG?$CG?5map_name?5?$CG?$CG?5map_name?$FL0@ (0000) 00285DC8 005a: ??_C@_0FK@HEDIBIPF@network_game_server_change_game_@ (0000) 00285E24 0012: ??_C@_0BC@DLOLLBOD@server?5?$CG?$CG?5variant?$AA@ (0000) 00285E38 0047: ??_C@_0EH@GICIMKGF@network_game_server_add_new_clie@ (0000) 00285E80 0051: ??_C@_0FB@EIEHAMPG@failed?5to?5find?5an?5available?5mach@ (0000) 00285ED8 0067: ??_C@_0GH@CKJGDANO@network_connection_get_address?$CI?$CJ@ (0000) 00285F40 0027: ??_C@_0CH@GMPLHAIM@new?5remote?5connection?5accepted?5f@ (0000) 00285F68 0062: ??_C@_0GC@GMAADHDI@remote?5system?5tried?5to?5join?5our?5@ (0000) 00285FCC 0019: ??_C@_0BJ@BMKILIN@server?5?$CG?$CG?5new_connection?$AA@ (0000) 00285FE8 005d: ??_C@_0FN@HKDNEOAD@network_game_server_handle_datag@ (0000) 00286048 0069: ??_C@_0GJ@NFGOFIEO@failed?5to?5create?5a?5message_serve@ (0000) 002860B8 0041: ??_C@_0EB@ILPMAAHI@error?5sending?5rejection?5message?5@ (0000) 002860FC 0038: ??_C@_0DI@LLMEOPEK@endpoint?5?$CG?$CG?5?$CIreason?5?$DM?5NUMBER_OF_@ (0000) 00286134 0028: ??_C@_0CI@NMINAOIB@client?5connection?5refused?$DL?5game?5@ (0000) 00286160 0061: ??_C@_0GB@DCECGEGP@?$CBall_machines_have_precached?5?$HM?$HM?5@ (0000) 002861C4 003e: ??_C@_0DO@MCCLFBCA@network?5game?5setup?5failed?$DL?5proba@ (0000) 00286204 0016: ??_C@_0BG@OLFNBGNL@setting?5up?5a?5net?5game?$AA@ (0000) 0028621C 001c: ??_C@_0BM@JFGLKNKN@?$CFsnumber_of_games_played?5?$CFd?$AA@ (0000) 00286238 001e: ??_C@_0BO@JOAPPGLE@?$CFsnetwork_game_random_seed?5?$CFx?$AA@ (0000) 00286258 0018: ??_C@_0BI@BAJBJCMP@?$CFs?7player_list_index?5?$CFx?$AA@ (0000) 00286270 0011: ??_C@_0BB@EOOHLDJC@?$CFs?7team_index?5?$CFx?$AA@ (0000) 00286284 0017: ??_C@_0BH@LNCHICCP@?$CFs?7controller_index?5?$CFx?$AA@ (0000) 0028629C 0014: ??_C@_0BE@HLHKAFCI@?$CFs?7machine_index?5?$CFx?$AA@ (0000) 002862B0 000c: ??_C@_0M@ONJMLKP@?$CFsplayer?5?$CFd?$AA@ (0000) 002862BC 0012: ??_C@_0BC@MPDEELBJ@?$CFsplayer_count?5?$CFd?$AA@ (0000) 002862D0 0011: ??_C@_0BB@NMBMNGAB@?7?$CFsmachine?5?$CFd?5?$CFx?$AA@ (0000) 002862E4 0013: ??_C@_0BD@FGMHAHMN@?$CFsmachine_count?5?$CFd?$AA@ (0000) 002862F8 0014: ??_C@_0BE@JAKJKGBL@?$CFsnetwork_game_data?$AA@ (0000) 0028630C 001e: ??_C@_0BO@KBMOCMCI@?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CKEND?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$AA@ (0000) 0028632C 002c: ??_C@_0CM@KBELBPJF@?7time_of_first_client_loading_co@ (0000) 00286358 001c: ??_C@_0BM@EADPCOLB@?7time_of_last_keep_alive?5?$CFd?$AA@ (0000) 00286374 0017: ??_C@_0BH@BNHPAHB@?7next_update_number?5?$CFd?$AA@ (0000) 0028638C 000b: ??_C@_0L@EEANCMOB@?7?7flags?5?$CFx?$AA@ (0000) 00286398 0013: ??_C@_0BD@JKAKGNJM@?7?7machine_index?5?$CFx?$AA@ (0000) 002863AC 0016: ??_C@_0BG@PPFEBDPA@?7?7stall_start_time?5?$CFd?$AA@ (0000) 002863C4 002a: ??_C@_0CK@BMPEFDDJ@?7?7last_received_update_sequence_@ (0000) 002863F0 0013: ??_C@_0BD@NOBCPADN@?7?7connection?5?$CFx?5?$CFs?$AA@ (0000) 00286404 000b: ??_C@_0L@NIBBMIDE@?7client?5?$CFd?$AA@ (0000) 00286410 0007: ??_C@_06NEHEIOHK@?$CIdead?$CJ?$AA@ (0000) 00286418 0009: ??_C@_08MGFPAODM@?$CIactive?$CJ?$AA@ (0000) 00286424 000e: ??_C@_0O@EFMNOFHN@no?5connection?$AA@ (0000) 00286434 0011: ??_C@_0BB@HDACINMK@client_machines?3?$AA@ (0000) 00286448 0002: ??_C@_01GPOEFGEJ@?7?$AA@ (0000) 0028644C 000a: ??_C@_09LHENMLPO@?7flags?5?$CFx?$AA@ (0000) 00286458 000a: ??_C@_09JEKGPEEJ@?7state?5?$CFx?$AA@ (0000) 00286464 000f: ??_C@_0P@IGEGAEKH@?7connection?5?$CFx?$AA@ (0000) 00286474 0020: ??_C@_0CA@JIMNGMN@?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CKBEGIN?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$CK?$AA@ (0000) 00286494 002d: ??_C@_0CN@BOOLPEKD@server?5failed?5to?5close?5a?5client?8@ (0000) 002864C8 005b: ??_C@_0FL@JJMLLPJG@network_game_server_remove_clien@ (0000) 00286528 0080: ??_C@_0IA@HCKJGBDF@network_game_server_remove_clien@ (0000) 002865A8 0013: ??_C@_0BD@OGPILLHP@machine?5index?5?$DN?5?$CFx?$AA@ (0000) 002865BC 0021: ??_C@_0CB@BNBDJEGI@machine?5name?5?$DN?5?$DMnot?5implemented?$DO@ (0000) 002865E0 0067: ??_C@_0GH@MMONEEJC@attempted?5to?5remove?5an?5invalid?5m@ (0000) 00286648 0069: ??_C@_0GJ@MMCLLNAF@network_game_server_remove_machi@ (0000) 002866B8 0057: ??_C@_0FH@BABKECJC@network_game_remove_machine?$CI?$CJ?5fa@ (0000) 00286710 0054: ??_C@_0FE@BFPCFLEJ@network_game_server_remove_machi@ (0000) 00286768 006f: ??_C@_0GP@EIJIPAGB@network_game_server_remove_clien@ (0000) 002867D8 0051: ??_C@_0FB@JOJEBAMD@network_game_server_remove_machi@ (0000) 0028682C 0008: ??_C@_07DPHHBAJ@removed?$AA@ (0000) 00286834 003c: ??_C@_0DM@CCJHIKEB@forcibly?5removing?5client?5system?5@ (0000) 00286870 000f: ??_C@_0P@IOAOPNJI@?$DMunknown?5name?$DO?$AA@ (0000) 00286880 0010: ??_C@_0BA@JMALINEK@culprit?5?$CB?$DN?5NONE?$AA@ (0000) 00286890 003e: ??_C@_0DO@BHFBBCGG@server?5?$CG?$CG?5server?9?$DOstate?5?$DN?$DN?5_netw@ (0000) 002868D0 002a: ??_C@_0CK@CDKFKGHB@failed?5to?5remove?5client?5machine?5@ (0000) 002868FC 0021: ??_C@_0CB@MJKIPMIF@client?5machine?5removed?5from?5game@ (0000) 00286920 0063: ??_C@_0GD@ILCKCFLA@network_game_server_handle_clien@ (0000) 00286984 002d: ??_C@_0CN@GAKEIIC@failed?5to?5remove?5client?5machine?5@ (0000) 002869B4 0024: ??_C@_0CE@CPAGILJL@client?5machine?5?$CFx?5removed?5from?5g@ (0000) 002869D8 004b: ??_C@_0EL@DJHOLFOA@forcibly?5removing?5client?5system?5@ (0000) 00286A28 0041: ??_C@_0EB@FNLOGEMK@failed?5to?5send?5a?5message_server_@ (0000) 00286A6C 0030: ??_C@_0DA@IPEDOIIA@network_game_server_start_networ@ (0000) 00286A9C 001f: ??_C@_0BP@CKGEOJEP@booting?5dead?5client?5machine?5?$CFd?$AA@ (0000) 00286ABC 0018: ??_C@_0BI@BNPOAEPH@network?5server?5disposed?$AA@ (0000) 00286AD4 0036: ??_C@_0DG@DABJFLIB@network_game_server_memory_do_no@ (0000) 00286B10 0059: ??_C@_0FJ@KJNIDGHK@network_game_server_handle_clien@ (0000) 00286B70 004b: ??_C@_0EL@BMJJENN@failed?5to?5create?5a?5_message_type@ (0000) 00286BBC 0034: ??_C@_0DE@CJDOLHKA@failed?5to?5notify?5all?5clients?5tha@ (0000) 00286BF0 002c: ??_C@_0CM@DJIIBLPE@notified?5all?5clients?5that?5we?5are@ (0000) 00286C20 004c: ??_C@_0EM@NAIJHAGO@failed?5to?5create?5a?5_message_type@ (0000) 00286C6C 001d: ??_C@_0BN@CJOEAMG@the?5server?8s?5game?5is?5invalid?$AA@ (0000) 00286C8C 0021: ??_C@_0CB@OBPDACNL@network_connection_idle?$CI?$CJ?5failed@ (0000) 00286CB0 0034: ??_C@_0DE@MJEBDOOH@network_game_server_handle_publi@ (0000) 00286CE4 0034: ??_C@_0DE@ECHDPAEM@network_game_server_handle_clien@ (0000) 00286D18 0015: ??_C@_0BF@HDEDCFJD@unknown?5server?5state?$AA@ (0000) 00286D30 0030: ??_C@_0DA@GGCMIGDD@failed?5to?5add?5new?5client?5connect@ (0000) 00286D60 0035: ??_C@_0DF@CAHJJMAP@new?5client?5connected?5from?5ip?5?$CFs?5@ (0000) 00286D98 003a: ??_C@_0DK@OICDNHOL@failed?5to?5signal?5all?5client?5mach@ (0000) 00286DD8 0050: ??_C@_0FA@CGIOIDCF@the?5playlist?5has?5ended?5?9?5server?5@ (0000) 00286E28 002b: ??_C@_0CL@GEFANPJI@the?5playlist?5has?5ended?5?9?5server?5@ (0000) 00286E54 001c: ??_C@_0BM@DEOHMBDO@server?5resetting?5to?5pregame?$AA@ (0000) 00286E70 0027: ??_C@_0CH@LGNFBGFC@failed?5to?5create?5the?5server?5conn@ (0000) 00286E98 002d: ??_C@_0CN@LEAKMMND@failed?5to?5initialize?5server?5preg@ (0000) 00286EC8 0037: ??_C@_0DH@KNNCDAKL@?$CBnetwork_game_server_memory_do_n@ (0000) 00456CF4 0008: _network_game_server_memory_do_not_use_directly_in_use (0000) */ /* ---------- headers */ #include "cseries.h" #include "bungie_net/common/message_header.h" #include "bungie_net/network/transport.h" #include "bungie_net/network/transport_address_constants.h" #include "bungie_net/network/transport_endpoint_winsock.h" #include "cseries/cseries_windows.h" #include "cseries/errors.h" #include "game/game.h" #include "game/game_engine_runtime.h" #include "game/player_queues_new.h" #include "game/players.h" #include "interface/ui_widget.h" #include "main/main.h" #include "math/random_math.h" #include "networking/network_client_manager.h" #include "networking/network_connection.h" #include "networking/network_game_globals.h" #include "networking/network_game_manager.h" #include "networking/network_game_protocol.h" #include "networking/network_game_ui.h" #include "networking/network_messages.h" #include "networking/network_server_manager.h" #include "networking/network_server_manager_internal.h" #include "networking/network_server_message_handler.h" #include "saved games/player_profile.h" #include "text/unicode.h" #include "cache/cache_files.h" /* ---------- constants */ #define NETWORK_SERVER_MANAGER_FILE "c:\\halo\\SOURCE\\networking\\network_server_manager.c" enum { MAXIMUM_NETWORK_MACHINE_COUNT = 4, MAXIMUM_NETWORK_PLAYER_COUNT = 16, NETWORK_GAME_NAME_LENGTH = 16, NETWORK_GAME_MAP_NAME_LENGTH = 0x80, NETWORK_PLAYER_NAME_LENGTH = 12, MAXIMUM_MACHINE_NAME_LENGTH = 32, NUMBER_OF_MULTIPLAYER_TEAMS = 2, NETWORK_GAME_PLAYER_QUIT_DELAY = 33, NETWORK_GAME_CLIENT_STALL_TIMEOUT = 2000, NETWORK_GAME_SERVER_MAXIMUM_WAIT_TIME_FOR_LEVEL_LOADING = 15 * MILLISECONDS_PER_SECOND, MAXIMUM_PLAYERS_PER_MACHINE = MAXIMUM_LOCAL_PLAYERS, PLAYER_UPDATE_SIZE = 0x20, MAXIMUM_GOOD_COLOR_ATTEMPTS = 10, _client_update_out_of_sync_bit = 31, CLIENT_UPDATE_SEQUENCE_NUMBER_MASK = 0x7FFFFFFF, NETWORK_GAME_COUNTDOWN_TIME = 30999, NETWORK_GAME_SPLITSCREEN_COUNTDOWN_TIME = 10999, NETWORK_GAME_COUNTDOWN_ADJUSTMENT = 5000, NETWORK_GAME_MINIMUM_COUNTDOWN_TIME = 999, _network_client_machine_connected_bit = 0, _network_client_machine_validated_bit, _network_client_machine_level_loaded_bit, _network_client_machine_precached_bit, NUMBER_OF_NETWORK_CLIENT_MACHINE_FLAGS, MAXIMUM_NETWORK_MESSAGE_SIZE = 0x800, }; enum { _network_game_server_game_open_bit = 0, _network_game_server_game_valid_bit, }; enum { _network_game_server_countdown_event_player_left, _network_game_server_countdown_event_player_joined, _network_game_server_countdown_event_stop, _network_game_server_countdown_event_start_immediately, NUMBER_OF_NETWORK_GAME_SERVER_COUNTDOWN_EVENTS }; enum { _network_game_server_state_pregame, _network_game_server_state_ingame, _network_game_server_state_postgame, NUMBER_OF_NETWORK_GAME_SERVER_STATES }; /* ---------- macros */ #define network_machine_is_valid(machine) \ ((machine) && (machine)->machine_index >= 0 && \ (machine)->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT) /* ---------- structures */ struct countdown_timer { long time_remaining; unsigned long last_update_time; }; struct message_server_game_over { long unused; }; struct message_server_begin_game { long unused; }; struct message_server_remove_player_ingame { struct network_player player; long reason; }; struct message_server_graceful_game_exit_pregame { long unused; }; struct message_server_switch_to_pregame { long unused; }; struct message_server_graceful_game_exit_postgame { long unused; }; struct message_server_machine_rejected { word reason; }; struct message_server_pregame_countdown { short seconds_to_start; }; struct message_server_pregame_keep_alive { short unused; }; struct message_server_postgame_keep_alive { short unused; }; struct message_client_game_update { unsigned long update_number; short unknown04; short player_count; struct player_action actions[MAXIMUM_PLAYERS_PER_MACHINE]; }; struct server_update { word player_count; word unknown02; byte player_updates[MAXIMUM_NETWORK_PLAYER_COUNT * PLAYER_UPDATE_SIZE]; }; struct message_server_game_update { long update_number; long random_seed; long game_time; word unknown0C; word player_count; byte player_updates[MAXIMUM_NETWORK_PLAYER_COUNT * PLAYER_UPDATE_SIZE]; }; struct network_machine { wchar_t name[32]; char machine_index; byte padding41[3]; }; typedef char network_machine_size_assert[ sizeof(struct network_machine) == 0x44 ? 1 : -1]; struct network_game_map { long version; char name[NETWORK_GAME_MAP_NAME_LENGTH]; }; struct network_game { wchar_t name[NETWORK_GAME_NAME_LENGTH]; struct network_game_map map; struct game_variant variant; byte opaque10C; char minimum_players; byte maximum_players; byte maximum_teams; short difficulty; short machine_count; struct network_machine machines[MAXIMUM_NETWORK_MACHINE_COUNT]; short player_count; struct network_player players[MAXIMUM_NETWORK_PLAYER_COUNT]; byte opaque426[2]; long random_seed; long number_of_games_played; boolean load_ui; byte padding431[3]; }; struct network_game_server_client_machine { struct network_connection *connection; unsigned long last_received_update_sequence_number; unsigned long stall_start_time; short machine_index; word flags; }; struct network_game_server_countdown_state { struct countdown_timer timer; long last_countdown_message_time; boolean active; boolean paused; boolean adjusted_time_this_tick; byte padding0F; }; struct network_game_server { struct network_connection *connection; word state; word flags; struct network_game game; struct network_game_server_client_machine client_machines[MAXIMUM_NETWORK_MACHINE_COUNT]; long next_update_number; long time_of_last_keep_alive; unsigned long time_of_first_client_loading_completion; struct network_game_server_countdown_state countdown_state; struct network_player queued_player; boolean queued_player_valid; boolean sent_start_game_message; byte padding4BA[2]; }; typedef char network_game_players_offset_assert[ offsetof(struct network_game, players) == 0x226 ? 1 : -1]; typedef char network_game_variant_has_teams_offset_assert[ offsetof(struct network_game, variant) + offsetof(struct game_variant, has_teams) == 0xC0 ? 1 : -1]; typedef char network_game_size_assert[ sizeof(struct network_game) == 0x434 ? 1 : -1]; typedef char network_game_server_client_machines_offset_assert[ offsetof(struct network_game_server, client_machines) == 0x43C ? 1 : -1]; typedef char network_game_server_countdown_state_offset_assert[ offsetof(struct network_game_server, countdown_state) == 0x488 ? 1 : -1]; /* ---------- prototypes */ void countdown_timer_increment( struct countdown_timer *timer, long adjustment, long maximum); static boolean network_game_server_setup_game_from_playlist( struct network_game_server *server); static boolean network_game_server_add_new_client( struct network_game_server *server, struct network_connection *new_connection); static boolean network_game_server_handle_public_endpoint( struct network_game_server *server); static boolean network_game_server_handle_client_machines( struct network_game_server *server); static boolean network_game_server_idle_postgame_tasks( struct network_game_server *server); static boolean network_game_server_have_all_machines_have_precached( struct network_game_server *server); static boolean network_game_server_idle_pregame_tasks( struct network_game_server *server); static void network_game_server_send_rejection_message( struct transport_endpoint *endpoint, word reason); static void network_game_server_reject_connection_game_is_full( struct transport_endpoint *endpoint); static short network_game_server_get_client_machine_count( struct network_game_server *server); void get_unique_random_name( struct network_game_server *server, struct network_player *player); void get_unique_random_color( struct network_game_server *server, struct network_player *player); static boolean player_name_is_unique( struct network_game_server *server, wchar_t const *name); static void network_game_server_dump( struct network_game_server *server); /* ---------- globals */ struct network_game_server network_game_server_memory_do_not_use_directly; boolean network_game_server_memory_do_not_use_directly_in_use = FALSE; /* ---------- public code */ struct network_game_server *network_game_server_create( void) { struct network_game_server *server = &network_game_server_memory_do_not_use_directly; match_assert( NETWORK_SERVER_MANAGER_FILE, 0xE0, !network_game_server_memory_do_not_use_directly_in_use); network_game_server_memory_do_not_use_directly_in_use = TRUE; csmemset(server, 0, sizeof(*server)); if (server != NULL) { server->connection = network_connection_new( FLAG(_connection_create_server_bit), NETWORK_GAME_SERVER_PORT); if (server->connection != NULL) { int i; #ifdef xbox transport_server_initialize(); #endif server->state = _network_game_server_state_pregame; server->flags = FLAG(_network_game_server_game_valid_bit); csmemset(&server->game, 0, sizeof(server->game)); network_connection_set_connection_rejection_procedure( server->connection, network_game_server_reject_connection_game_is_full); network_game_invalidate(&server->game); server->game.difficulty = main_get_difficulty(); server->game.number_of_games_played = -1; for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { server->client_machines[i].connection = NULL; server->client_machines[i].last_received_update_sequence_number = 0; server->client_machines[i].stall_start_time = 0; server->client_machines[i].machine_index = NONE; server->client_machines[i].flags = 0; network_game_invalidate_machine(&server->game, i); } server->sent_start_game_message = FALSE; server->time_of_first_client_loading_completion = 0; if (!network_game_server_reset_to_pregame(server)) { error( _error_silent, "failed to initialize server pregame settings"); network_game_server_dispose(server); server = NULL; } } else { error( _error_silent, "failed to create the server connection"); network_game_server_dispose(server); server = NULL; } } return server; } void network_game_server_dispose( struct network_game_server *server) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x120, server); switch (server->state) { case _network_game_server_state_pregame: { struct message_server_graceful_game_exit_pregame message_packet; struct network_message *message; message = create_network_game_message( _message_server_graceful_game_exit_pregame, &message_packet, sizeof(message_packet)); if (message != NULL) { if (network_game_server_send_message_to_all_machines(server, message)) network_event("notified all clients that we are going down"); else network_event("failed to notify all clients that we are going down"); } else { network_event( "failed to create a _message_type_server_graceful_game_exit_pregame message"); } break; } case _network_game_server_state_ingame: break; case _network_game_server_state_postgame: { struct message_server_graceful_game_exit_postgame message_packet; struct network_message *message; message = create_network_game_message( _message_server_graceful_game_exit_postgame, &message_packet, sizeof(message_packet)); if (message != NULL) { if (network_game_server_send_message_to_all_machines(server, message)) network_event("notified all clients that we are going down"); else network_event("failed to notify all clients that we are going down"); } else { network_event( "failed to create a _message_type_server_graceful_game_exit_postgame message"); } break; } } if (!network_game_server_handle_client_machines(server)) { error( _error_silent, "network_game_server_handle_client_machines() failed inside network_game_server_dispose()"); } if (server->connection) network_connection_delete(server->connection); #ifdef xbox SleepEx(MILLISECONDS_PER_SECOND, FALSE); transport_server_terminate(); #endif csmemset(server, 0, sizeof(*server)); match_assert( NETWORK_SERVER_MANAGER_FILE, 0x171, network_game_server_memory_do_not_use_directly_in_use); network_game_server_memory_do_not_use_directly_in_use = FALSE; network_event("network server disposed"); return; } boolean network_game_server_idle( struct network_game_server *server) { boolean success = TRUE; if (transport_network_available() == FALSE) { if (!network_game_is_splitscreen_local()) { display_error_when_main_menu_loaded(_error_network_connection_lost); error(_error_silent, "network connection went down!"); success = FALSE; goto exit; } } if (network_game_server_game_is_valid(server)) { struct network_connection *new_client_connection = NULL; success = network_connection_idle( server->connection, _connection_dont_timeout, &new_client_connection); if (success == TRUE) { if (new_client_connection) { success = network_game_server_add_new_client( server, new_client_connection); if (success == TRUE) { struct transport_address client_address; network_connection_get_address( new_client_connection, &client_address, NULL); network_event( "new client connected from ip %s (validation pending)", transport_address_to_string(&client_address)); } else { network_event( "failed to add new client connection to the game"); success = network_server_close_client_connection( server->connection, new_client_connection); } } success = network_game_server_handle_public_endpoint(server); if (success) { success = network_game_server_handle_client_machines(server); if (success) { switch (server->state) { case _network_game_server_state_pregame: success = network_game_server_idle_pregame_tasks(server); break; case _network_game_server_state_ingame: break; case _network_game_server_state_postgame: success = network_game_server_idle_postgame_tasks(server); break; default: network_event("unknown server state"); success = FALSE; break; } } else { network_event( "network_game_server_handle_client_machines() failed"); } } else { network_event( "network_game_server_handle_public_endpoint() failed"); } } else { network_event("network_connection_idle() failed"); } } else { network_event("the server's game is invalid"); } exit: return success; } void countdown_timer_update( struct countdown_timer *timer) { long update_time = system_milliseconds(); if (update_time > (long)timer->last_update_time) { long elapsed_time = update_time - timer->last_update_time; if (elapsed_time < timer->time_remaining) timer->time_remaining -= elapsed_time; else timer->time_remaining = 0; } timer->last_update_time = update_time; return; } long countdown_timer_get_time_remaining( struct countdown_timer *timer) { long time_remaining; unsigned long update_time = system_milliseconds(); unsigned long last_update_time = timer->last_update_time; if ((long)update_time > (long)last_update_time) { long elapsed_time = update_time - last_update_time; if (elapsed_time < timer->time_remaining) timer->time_remaining -= elapsed_time; else timer->time_remaining = 0; } time_remaining = timer->time_remaining; timer->last_update_time = update_time; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x5F, timer->time_remaining >= 0); return time_remaining; } void countdown_timer_increment( struct countdown_timer *timer, long adjustment, long maximum) { countdown_timer_update(timer); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x68, adjustment >= 0); if (timer->time_remaining + adjustment < adjustment) { timer->time_remaining = maximum; } else { timer->time_remaining += adjustment; timer->time_remaining = MIN(timer->time_remaining, maximum); } match_assert( NETWORK_SERVER_MANAGER_FILE, 0x75, timer->time_remaining >= 0); return; } void countdown_timer_decrement( struct countdown_timer *timer, long adjustment) { unsigned long update_time = system_milliseconds(); unsigned long last_update_time = timer->last_update_time; if ((long)update_time > (long)last_update_time) { long elapsed_time = update_time - last_update_time; if (elapsed_time < timer->time_remaining) timer->time_remaining -= elapsed_time; else timer->time_remaining = 0; } timer->last_update_time = update_time; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x7E, adjustment >= 0); if (timer->time_remaining > adjustment) timer->time_remaining -= adjustment; else timer->time_remaining = 0; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x89, timer->time_remaining >= 0); return; } struct network_connection *network_game_server_get_client_connection( struct network_game_server_client_machine *client_machine) { if (client_machine) return client_machine->connection; return NULL; } struct network_connection *network_game_server_get_connection( struct network_game_server *server) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x712, server); return server->connection; } struct network_game *network_game_server_get_game( struct network_game_server *server) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x769, server); return &server->game; } wchar_t *network_game_server_get_game_name( struct network_game_server *server) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x1E9, server); return server->game.name; } boolean network_game_server_set_game_name( struct network_game_server *server, wchar_t const *name) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x1DD, server); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x1DE, name); ustrncpy(server->game.name, name, NETWORK_GAME_NAME_LENGTH - 1); server->game.name[NETWORK_GAME_NAME_LENGTH - 1] = 0; return FALSE; } word network_game_server_get_state( struct network_game_server *server, short *substate) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x1F2, server); if (substate) *substate = 0; return server->state; } void network_game_server_open_game( struct network_game_server *server) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x1FC, server); SET_FLAG(server->flags, _network_game_server_game_open_bit, TRUE); network_server_allow_client_connections(server->connection, TRUE); network_event("opening game"); return; } void network_game_server_close_game( struct network_game_server *server) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x208, server); SET_FLAG(server->flags, _network_game_server_game_open_bit, FALSE); network_server_allow_client_connections(server->connection, FALSE); network_event("closing game"); return; } boolean network_game_server_start_network_game( struct network_game_server *server) { boolean success = TRUE; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x2DE, server); if (server->sent_start_game_message == FALSE) { struct network_game game_settings; struct message_server_begin_game begin_game = { 0 }; void *message; csmemcpy(&game_settings, &server->game, sizeof(game_settings)); if (((message = create_network_game_message( _message_server_game_settings_update, &game_settings, sizeof(game_settings))) != NULL) && network_game_server_send_message_to_all_machines(server, message) && ((message = create_network_game_message( _message_server_begin_game, &begin_game, sizeof(begin_game))) != NULL) && network_game_server_send_message_to_all_machines(server, message)) { network_event("signalling client machines to begin loading for network game"); server->sent_start_game_message = TRUE; success = TRUE; } else { network_event("failed to signal client machines to begin loading for network game"); } } server->next_update_number = 0; return success; } void network_game_server_send_player_quit_messages_ingame( struct network_game_server *server, struct network_game_server_client_machine *machine) { long player_index; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x267, _network_game_server_state_ingame == server->state); for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT; player_index++) { struct network_player *player = &server->game.players[player_index]; if (network_player_is_valid(player) && player->machine_index == machine->machine_index) { struct message_server_remove_player_ingame remove_player; void *message; remove_player.player = *player; remove_player.reason = game_time_get() + NETWORK_GAME_PLAYER_QUIT_DELAY; error(_error_silent, "sending quit out of game, time = %x", remove_player.reason); message = create_network_game_message( _message_server_remove_player_ingame, &remove_player, sizeof(remove_player)); if (message && !network_game_server_send_message_to_all_machines(server, message)) { network_event( "network_game_server_send_message_to_all_machines() failed in network_game_server_handle_message_client_remove_player_request_ingame()"); } } } return; } void network_game_server_switch_to_postgame( struct network_game_server *server) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x2FF, server); if (server->state == _network_game_server_state_ingame) { struct message_server_game_over game_over = { 0 }; void *message; server->state = _network_game_server_state_postgame; message = create_network_game_message( _message_server_game_over, &game_over, sizeof(game_over)); if (message) { if (network_game_server_send_message_to_all_machines(server, message)) network_event("server sent message_game_over to all clients"); else network_event("failed to signal all client machines to switch to postgame"); } else { network_event("failed to create a _message_type_server_game_over message"); } } return; } boolean network_game_server_graceful_shutdown( struct network_game_server *server) { boolean success = FALSE; void *message = NULL; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x39F, server); switch (network_game_server_get_state(server, NULL)) { case _network_game_server_state_pregame: { struct message_server_graceful_game_exit_pregame exit_pregame = { 0 }; message = create_network_game_message( _message_server_graceful_game_exit_pregame, &exit_pregame, sizeof(exit_pregame)); if (!message) network_event("failed to create a message_server_graceful_game_exit_pregame"); } break; case _network_game_server_state_postgame: { struct message_server_graceful_game_exit_postgame exit_postgame = { 0 }; message = create_network_game_message( _message_server_graceful_game_exit_postgame, &exit_postgame, sizeof(exit_postgame)); if (!message) network_event("failed to create a message_server_graceful_game_exit_postgame"); } break; } if (message) { success = network_game_server_send_message_to_all_machines(server, message); if (success == TRUE) { network_event( "server closing down; all client machines were properly informed"); } else { network_event( "server going down, but failed to properly inform all client machines"); } } return success; } void countdown_timer_set_time_remaining( struct countdown_timer *timer, long time_remaining) { unsigned long update_time = system_milliseconds(); timer->time_remaining = time_remaining; timer->last_update_time = update_time; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x95, timer->time_remaining >= 0); return; } void network_game_server_client_machine_is_precached( struct network_game_server *server, struct network_game_server_client_machine *client_machine, char const *map_name) { char const *multiplayer_map_name = main_get_multiplayer_map_name(); if (!csstrcmp(multiplayer_map_name, map_name)) SET_FLAG(client_machine->flags, _network_client_machine_precached_bit, TRUE); return; } boolean network_game_server_game_is_open( struct network_game_server *server) { boolean game_is_open; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x214, server); game_is_open = TEST_FLAG(server->flags, _network_game_server_game_open_bit); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x217, (TRUE == game_is_open) || (FALSE == game_is_open)); return game_is_open; } boolean network_game_server_game_is_valid( struct network_game_server *server) { boolean game_is_valid; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x220, server); game_is_valid = TEST_FLAG(server->flags, _network_game_server_game_valid_bit); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x223, (TRUE == game_is_valid) || (FALSE == game_is_valid)); return game_is_valid; } boolean network_game_server_accept_client_machine_into_game( struct network_game_server *server, struct network_game_server_client_machine *machine) { boolean success = FALSE; long machine_index; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x3DA, server); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x3DB, machine); for (machine_index = 0; machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; machine_index++) { if (server->game.machines[machine_index].machine_index < 0 || server->game.machines[machine_index].machine_index >= MAXIMUM_NETWORK_MACHINE_COUNT) { struct network_machine new_machine; csmemcpy( &new_machine, &server->game.machines[machine_index], sizeof(new_machine)); new_machine.machine_index = (char)machine_index; success = network_game_add_machine(&server->game, &new_machine); if (success == TRUE) { struct transport_address address = { { { 0 } } }; network_connection_get_address( machine->connection, &address, FALSE); network_event( "server added machine @ %s to the game at machine index #%d", transport_address_to_string(&address), machine_index); SET_FLAG( machine->flags, _network_client_machine_validated_bit, TRUE); machine->machine_index = (short)machine_index; } else { network_event( "network_game_add_machine() failed in network_game_server_accept_client_machine_into_game()"); } break; } } if (machine_index == MAXIMUM_NETWORK_MACHINE_COUNT) { network_event( "network_game_server_accept_client_machine_into_game() failed to find an available opening for the new machine"); } return success; } boolean network_game_server_client_machine_is_joined_to_game( struct network_game_server *server, struct network_game_server_client_machine *machine) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x3CD, server); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x3CE, machine); return TEST_FLAG(machine->flags, _network_client_machine_validated_bit); } boolean network_game_server_switch_machine_from_postgame_to_pregame( struct network_game_server *server, struct network_game_server_client_machine *machine) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x547, server && machine); network_event( "machine #%d has successfully switched to pregame", machine->machine_index); SET_FLAG(machine->flags, _network_client_machine_level_loaded_bit, FALSE); return TRUE; } void network_game_server_all_machines_have_loaded( struct network_game_server *server) { network_event("all machines have successfully loaded"); server->state = _network_game_server_state_ingame; server->time_of_first_client_loading_completion = 0; server->game.load_ui = global_network_game_client_get() ? network_game_client_get_game(global_network_game_client_get())->load_ui : FALSE; match_vassert(NETWORK_SERVER_MANAGER_FILE, 0x4E0, server->game.load_ui, "local game data not loaded"); return; } void network_game_server_client_machine_game_loading_complete( struct network_game_server *server, struct network_game_server_client_machine *machine) { boolean all_machines_loaded = TRUE; long client_machine_index; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x4ED, server); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x4EE, machine); SET_FLAG(machine->flags, _network_client_machine_level_loaded_bit, TRUE); for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { struct network_game_server_client_machine *client_machine = &server->client_machines[client_machine_index]; if (client_machine->machine_index >= 0 && client_machine->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT && !TEST_FLAG(client_machine->flags, _network_client_machine_level_loaded_bit)) { network_event( "still waiting on machine #%d to finish loading", client_machine->machine_index); all_machines_loaded = FALSE; } } if (all_machines_loaded == TRUE) network_game_server_all_machines_have_loaded(server); if (!server->time_of_first_client_loading_completion) server->time_of_first_client_loading_completion = system_milliseconds(); return; } void network_game_server_handle_client_update_packet( struct network_game_server *server, struct network_game_server_client_machine *machine, struct message_client_game_update *message_packet) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x51E, server); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x51F, machine); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x520, message_packet); if (TEST_FLAG(message_packet->update_number, _client_update_out_of_sync_bit)) { network_event( "client machine #%d is out of sync @ game tick #%ld; switching to post-game", machine->machine_index, game_time_get()); game_engine_switch_to_postgame(); } else if ((message_packet->update_number & CLIENT_UPDATE_SEQUENCE_NUMBER_MASK) < machine->last_received_update_sequence_number) { network_event( "received an outdated client update packet; ignoring (#%d / #%d)", message_packet->update_number & CLIENT_UPDATE_SEQUENCE_NUMBER_MASK, machine->last_received_update_sequence_number); } else if (message_packet->player_count < 0 || message_packet->player_count > MAXIMUM_PLAYERS_PER_MACHINE) { network_event( "client update packet from machine #%d had a bad player count; ignoring", machine->machine_index); } else { struct player_action actions[MAXIMUM_PLAYERS_PER_MACHINE] = { 0 }; long player_index; for (player_index = 0; player_index < message_packet->player_count; player_index++) { actions[player_index] = message_packet->actions[player_index]; } update_server_handle_client_update(machine->machine_index, actions); machine->last_received_update_sequence_number = message_packet->update_number & CLIENT_UPDATE_SEQUENCE_NUMBER_MASK; } return; } boolean network_game_server_add_player_to_game( struct network_game_server *server, struct network_game_server_client_machine *machine, struct network_player *player) { static long network_game_server_next_team_index = 0; boolean success; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x46C, server); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x46D, machine); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x46E, player); if (machine->machine_index == player->machine_index) { player->team_index = (char)network_game_server_next_team_index; network_game_server_next_team_index = (network_game_server_next_team_index + 1) % NUMBER_OF_MULTIPLAYER_TEAMS; if (!player->name[0]) get_unique_random_name(server, player); if (!player_name_is_unique(server, player->name)) get_unique_random_name(server, player); if (player->primary_color_index == NONE) get_unique_random_color(server, player); success = network_game_add_player(&server->game, player); if (success == TRUE) { network_event( "server added player from machine #%d at controller index #%d to the game", player->machine_index, player->controller_index); } else { network_event( "network_game_add_player() failed in network_game_server_add_player_to_game()"); } } else { network_event( "client machine tried to add a player with a non-matching machine identifier"); success = FALSE; } return success; } void network_game_server_update_ticks( struct network_game_server *server, short tick_count) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x553, server); switch (network_game_server_get_state(server, NULL)) { case _network_game_server_state_ingame: { struct network_game_server_client_machine *client_machine = NULL; long client_machine_index; short tick_index; for (tick_index = 0; tick_index < tick_count; tick_index++) { struct message_server_game_update game_update; struct server_update update; long update_number = server->next_update_number++; void *message; update_server_next_update(); update_server_build_server_update(NONE, &update, &update_number); game_update.update_number = update_number; game_update.random_seed = get_random_seed(); game_update.game_time = game_time_get(); game_update.player_count = update.player_count; csmemcpy( game_update.player_updates, update.player_updates, update.player_count * PLAYER_UPDATE_SIZE); message = create_network_game_message( _message_server_game_update, &game_update, sizeof(game_update)); if (message && !network_game_server_send_message_to_all_machines(server, message)) { network_event( "server failed to send game update message to all machines; client machine may be out of sync"); } } if (server->queued_player_valid) { for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { if (server->client_machines[client_machine_index].machine_index == server->queued_player.machine_index) { client_machine = &server->client_machines[client_machine_index]; break; } } if (client_machine && network_game_server_add_player_to_game( server, client_machine, &server->queued_player)) { if (!network_game_server_send_player_joined_info_ingame( server, &server->queued_player)) { network_event( "network_game_server_send_player_joined_info_ingame() failed in network_game_server_handle_message_client_add_player_request_ingame()"); } } else { network_event("server failed to add a network player in-game"); } server->queued_player_valid = FALSE; } } break; case _network_game_server_state_postgame: game_engine_update(); break; } return; } void network_game_server_queue_player_for_addition( struct network_game_server *server, struct network_player *player) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x5DE, server && player); if (!server->queued_player_valid && network_player_is_valid(player)) { csmemcpy(&server->queued_player, player, sizeof(server->queued_player)); server->queued_player_valid = TRUE; } return; } boolean network_game_server_remove_player_from_game( struct network_game_server *server, struct network_game_server_client_machine *machine, struct network_player *player) { boolean success; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x4A0, server); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x4A1, machine); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x4A2, player); if (machine->machine_index == player->machine_index) { success = network_game_remove_player(&server->game, player); if (success == TRUE) { network_event( "server removed player from machine #%d at controller index #%d from the game", player->machine_index, player->controller_index); } else { network_event( "network_game_remove_player() failed in network_game_server_remove_player_from_game()"); } } else { network_event( "client machine tried to remove a player with a non-matching machine identifier"); success = FALSE; } return success; } boolean network_game_server_adjust_machine_settings( struct network_game_server *server, struct network_game_server_client_machine *machine, struct network_machine *machine_description) { boolean success = FALSE; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x4BF, server && machine && machine_description); if (machine->machine_index == machine_description->machine_index) { success = network_game_update_machine(&server->game, machine_description); if (success == TRUE) { network_event( "server updated machine #%d settings", machine_description->machine_index); } else { network_event( "network_game_update_machine() failed in network_game_server_adjust_machine_settings()"); } } else { network_event( "client machine tried to update itself with a non-matching machine identifier"); } return success; } void network_game_server_begin_game_start_countdown( struct network_game_server *server, long time_remaining) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x5ED, server); if (!server->countdown_state.active && !server->countdown_state.paused) { countdown_timer_set_time_remaining( &server->countdown_state.timer, time_remaining); server->countdown_state.adjusted_time_this_tick = FALSE; server->countdown_state.active = TRUE; network_event("server game start countdown started"); } return; } boolean server_needs_more_teams( struct network_game_server *server) { boolean needs_more_teams = FALSE; if (server->game.variant.has_teams) { short player_count_by_team[NUMBER_OF_MULTIPLAYER_TEAMS] = { 0, 0 }; long player_index; long team_index; for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT; player_index++) { struct network_player *player = &server->game.players[player_index]; if (network_player_is_valid(player) && player->team_index >= 0 && player->team_index < NUMBER_OF_MULTIPLAYER_TEAMS) { player_count_by_team[player->team_index]++; } } for (team_index = 0; team_index < NUMBER_OF_MULTIPLAYER_TEAMS; team_index++) { if (player_count_by_team[team_index] == 0) { needs_more_teams = TRUE; break; } } } return needs_more_teams; } boolean server_has_a_player_on_each_machine( struct network_game_server *server) { long client_machine_index; for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { struct network_game_server_client_machine *client_machine = &server->client_machines[client_machine_index]; if (client_machine->machine_index >= 0 && client_machine->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT) { boolean has_a_player = FALSE; long player_index; for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT; player_index++) { if (network_player_is_valid(&server->game.players[player_index]) && server->game.players[player_index].machine_index == client_machine->machine_index) { has_a_player = TRUE; } } if (!has_a_player) return FALSE; } } return TRUE; } boolean server_has_enough_machines( struct network_game_server *server) { boolean has_enough_machines; long minimum_machine_count = network_game_is_splitscreen_local() ? 1 : 2; long machine_count = 0; long client_machine_index; for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { struct network_game_server_client_machine *client_machine = &server->client_machines[client_machine_index]; if (client_machine->machine_index >= 0 && client_machine->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT) { machine_count++; } } has_enough_machines = machine_count >= minimum_machine_count; return has_enough_machines; } boolean server_ok_to_countdown( struct network_game_server *server) { if (server_has_enough_machines(server) && server_has_a_player_on_each_machine(server) && !server_needs_more_teams(server) && server->game.player_count >= server->game.minimum_players) { return TRUE; } return FALSE; } void network_game_server_invalidate_network_machine( struct network_machine *machine) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x6C9, machine); csmemset(machine, 0, sizeof(*machine)); machine->machine_index = NONE; return; } void network_game_generate_join_game_token( byte join_token[NETWORK_JOIN_GAME_TOKEN_SIZE]) { byte join_token_initializer[] = { 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x61, 0x20, 0x62, 0x6F, 0x74, 0x74, 0x6C, 0x65 }; match_assert(NETWORK_SERVER_MANAGER_FILE, 1754, join_token); /* January and the supplied source both clear the decayed pointer's size. */ memset(join_token, 0, sizeof(join_token)); memcpy(join_token, join_token_initializer, MIN(NETWORK_JOIN_GAME_TOKEN_SIZE, sizeof(join_token_initializer))); #ifndef DEBUG { char *build_timestamp = __DATE__ __TIME__; int i, j, length = strlen(build_timestamp); unsigned long tags_checksum = tag_groups_checksum(); /* Release builds also incorporate compilation time and tag checksum. */ for (i = 0; i < sizeof(join_token); i++) { for (j = 0; j < length; j++) { join_token[i] ^= build_timestamp[j]; } } for (i = j = 0; i < sizeof(join_token); i++) { join_token[i] ^= ((byte *)&tags_checksum)[j++]; if (j == sizeof(tags_checksum)) { j = 0; } } } #endif return; } struct network_machine *network_game_server_get_client_machine( struct network_game_server *server, struct network_game_server_client_machine *client_machine, long *machine_index) { struct network_machine *machine; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x701, server && client_machine); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x702, client_machine->machine_indexgame.machines[client_machine->machine_index]; if (machine_index) *machine_index = machine->machine_index; return machine; } struct network_connection *network_game_server_get_machine_connection( struct network_game_server *server, struct network_machine *machine) { struct network_connection *connection = NULL; long index; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x72F, server && network_machine_is_valid(machine)); for (index = 0; index < MAXIMUM_NETWORK_MACHINE_COUNT; index++) { if (server->client_machines[index].machine_index == machine->machine_index) { connection = server->client_machines[index].connection; break; } } return connection; } struct network_game_server_client_machine *network_game_server_get_client_machine_at_index( struct network_game_server *server, long index) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x741, server && (indexclient_machines[index]; } struct network_game_server_client_machine *network_game_server_get_client_machine_at_address( struct network_game_server *server, unsigned long ip_address) { struct network_game_server_client_machine *client_machine = NULL; long i; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x74D, server && ip_address); for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { if (server->client_machines[i].machine_index >= 0 && server->client_machines[i].machine_index < MAXIMUM_NETWORK_MACHINE_COUNT) { struct transport_address address; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x755, server->client_machines[i].connection); network_connection_get_address( server->client_machines[i].connection, &address, FALSE); if (address.address.long_words[0] == ip_address) { client_machine = &server->client_machines[i]; break; } } } if (i == MAXIMUM_NETWORK_MACHINE_COUNT) network_event("no machine found @ ip #%lX", ip_address); return client_machine; } long network_game_server_get_oldest_client_update_received( struct network_game_server *server) { unsigned long oldest_update = (unsigned long)NONE; long index; for (index = 0; index < MAXIMUM_NETWORK_MACHINE_COUNT; index++) { struct network_game_server_client_machine *client_machine = &server->client_machines[index]; if (client_machine->machine_index >= 0 && client_machine->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT) { oldest_update = MIN( oldest_update, client_machine->last_received_update_sequence_number); } } return oldest_update; } boolean network_game_server_game_can_start( struct network_game_server *server) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x782, server); return server->state == 0 && server->game.player_count >= server->game.minimum_players; } void network_game_server_pause_countdown( struct network_game_server *server, boolean pause_countdown) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x78C, server); if (pause_countdown == TRUE) csmemset(&server->countdown_state, 0, sizeof(server->countdown_state)); server->countdown_state.paused = pause_countdown; return; } void network_game_server_change_map_name( struct network_game_server *server, char const *map_name) { long client_machine_index; match_assert( NETWORK_SERVER_MANAGER_FILE, 0x79B, server && map_name && map_name[0]); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x79C, server->state == _network_game_server_state_pregame); for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { struct network_game_server_client_machine *client_machine = &server->client_machines[client_machine_index]; if (client_machine->machine_index >= 0 && client_machine->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT) { SET_FLAG( client_machine->flags, _network_client_machine_precached_bit, FALSE); } } csstrncpy( server->game.map.name, map_name, NETWORK_GAME_MAP_NAME_LENGTH - 1); server->game.map.name[NETWORK_GAME_MAP_NAME_LENGTH - 1] = 0; if (!network_game_server_send_game_data_pregame(server)) { network_event( "network_game_server_change_map_name() failed to send updated game settings to clients"); } return; } void network_game_server_change_game_variant( struct network_game_server *server, struct game_variant *variant) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x7BE, server && variant); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x7BF, server->state == _network_game_server_state_pregame); csmemcpy(&server->game.variant, variant, sizeof(server->game.variant)); if (!network_game_server_send_game_data_pregame(server)) { network_event( "network_game_server_change_game_variant() failed to send updated game settings to clients"); } return; } boolean network_game_server_remove_client_machine_from_game( struct network_game_server *server, struct network_game_server_client_machine *client) { boolean success = FALSE; int i; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x22F, server); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x230, client); if (_network_game_server_state_ingame == server->state) { network_game_server_send_player_quit_messages_ingame(server, client); } for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { if (server->game.machines[i].machine_index == client->machine_index) { if (!network_game_remove_machine( &server->game, &server->game.machines[i])) { error( _error_silent, "network_game_server_remove_client_machine_from_game() failed to remove the offending machine from the server's copy of the game"); } break; } } for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { if (&server->client_machines[i] == client) { if (server->client_machines[i].connection != NULL) { if (!network_server_close_client_connection( server->connection, server->client_machines[i].connection)) { network_event("server failed to close a client's connection"); } } server->client_machines[i].connection = NULL; server->client_machines[i].last_received_update_sequence_number = 0; server->client_machines[i].stall_start_time = 0; server->client_machines[i].machine_index = NONE; server->client_machines[i].flags = 0; success = TRUE; break; } } if (!success) { network_event( "network_game_server_remove_client_machine_from_game() failed to find the specified machine"); } return success; } boolean network_game_server_remove_machine_from_game( struct network_game_server *server, struct network_machine *machine) { boolean success = FALSE; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x299, server); if (NONE == machine->machine_index) { network_event( "network_game_server_remove_machine_from_game called with a machine_index of NONE"); } if (network_machine_is_valid(machine)) { int i; for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { if (server->client_machines[i].machine_index == machine->machine_index) { success = network_game_server_remove_client_machine_from_game( server, &server->client_machines[i]); if (!success) { network_event( "network_game_server_remove_client_machine_from_game() failed in network_game_server_remove_machine_from_game()"); } break; } } if (i == MAXIMUM_NETWORK_MACHINE_COUNT) { network_event( "network_game_server_remove_machine_from_game() failed to find the specified machine"); } if (machine->machine_index != NONE) { success = network_game_remove_machine(&server->game, machine); if (!success) { network_event( "network_game_remove_machine() failed in network_game_server_remove_machine_from_game()"); } } if (server->state == _network_game_server_state_pregame) { boolean sent_game_settings = network_game_server_send_game_data_pregame(server); if (!sent_game_settings) { network_event( "network_game_server_remove_machine_from_game() failed to send updated game settings to remaining clients"); } } } else { network_event( "attempted to remove an invalid machine from the game in network_game_server_remove_machine_from_game()"); network_event("machine name = "); network_event("machine index = %x", machine->machine_index); network_game_server_dump(server); } return success; } static void dump_network_game_data( char *prefix, struct network_game *network_game_data) { #ifdef DEBUG network_event("%snetwork_game_data", prefix); network_event("%smachine_count %d", prefix, network_game_data->machine_count); { long itr; for (itr = 0; itr < MAXIMUM_NETWORK_MACHINE_COUNT; itr++) { network_event( "\t%smachine %d %x", prefix, itr, network_game_data->machines[itr].machine_index); } } network_event("%splayer_count %d", prefix, network_game_data->player_count); { long itr; for (itr = 0; itr < MAXIMUM_NETWORK_PLAYER_COUNT; itr++) { network_event("%splayer %d", prefix, itr); network_event("%s\tmachine_index %x", prefix, network_game_data->players[itr].machine_index); network_event("%s\tcontroller_index %x", prefix, network_game_data->players[itr].controller_index); network_event("%s\tteam_index %x", prefix, network_game_data->players[itr].team_index); network_event("%s\tplayer_list_index %x", prefix, network_game_data->players[itr].player_list_index); } } network_event("%snetwork_game_random_seed %x", prefix, network_game_data->random_seed); network_event("%snumber_of_games_played %d", prefix, network_game_data->number_of_games_played); #endif return; } static void network_game_server_dump( struct network_game_server *server) { #ifdef DEBUG long itr; network_event("*************BEGIN*************"); network_event("\tconnection %x", server->connection); network_event("\tstate %x", server->state); network_event("\tflags %x", server->flags); dump_network_game_data("\t", &server->game); network_event("client_machines:"); for (itr = 0; itr < MAXIMUM_NETWORK_MACHINE_COUNT; itr++) { struct network_game_server_client_machine *client_machine = server->client_machines + itr; char *connection_status = "no connection"; if (client_machine->connection != NULL) { connection_status = network_connection_active(client_machine->connection) ? "(active)" : "(dead)"; } network_event("\tclient %d", itr); network_event("\t\tconnection %x %s", client_machine->connection, connection_status); network_event("\t\tlast_received_update_sequence_number %d", client_machine->last_received_update_sequence_number); network_event("\t\tstall_start_time %d", client_machine->stall_start_time); network_event("\t\tmachine_index %x", client_machine->machine_index); network_event("\t\tflags %x", client_machine->flags); } network_event("\tnext_update_number %d", server->next_update_number); network_event("\ttime_of_last_keep_alive %d", server->time_of_last_keep_alive); network_event("\ttime_of_first_client_loading_completion %d", server->time_of_first_client_loading_completion); network_event("*************END*************"); #endif return; } void network_game_server_stalled_on_client( struct network_game_server *server, boolean stalled) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x59E, server); if (stalled) { unsigned long oldest_update = (unsigned long)NONE; long culprit = NONE; long client_machine_index; for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { if (server->client_machines[client_machine_index].machine_index >= 0 && server->client_machines[client_machine_index].machine_index < MAXIMUM_NETWORK_MACHINE_COUNT && server->client_machines[client_machine_index].last_received_update_sequence_number < oldest_update) { oldest_update = server->client_machines[client_machine_index].last_received_update_sequence_number; culprit = client_machine_index; } } match_assert(NETWORK_SERVER_MANAGER_FILE, 0x5B1, culprit != NONE); if (server->client_machines[culprit].stall_start_time) { if (system_milliseconds() - server->client_machines[culprit].stall_start_time >= NETWORK_GAME_CLIENT_STALL_TIMEOUT) { char machine_name[MAXIMUM_MACHINE_NAME_LENGTH]; boolean removed; network_event( "forcibly removing client system '%s' due to timeout in-game", wide_to_ascii( server->game.machines[ server->client_machines[culprit].machine_index].name, machine_name, MAXIMUM_MACHINE_NAME_LENGTH) ? machine_name : ""); removed = network_game_server_remove_client_machine_from_game( server, &server->client_machines[culprit]); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x5C1, removed); } } else { server->client_machines[culprit].stall_start_time = system_milliseconds(); } for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { if (client_machine_index != culprit) server->client_machines[client_machine_index].stall_start_time = 0; } } else { long client_machine_index; for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { server->client_machines[client_machine_index].stall_start_time = 0; } } return; } void network_game_server_update_countdown( struct network_game_server *server, short countdown_event) { match_assert(NETWORK_SERVER_MANAGER_FILE, 0x66E, server && server->state == _network_game_server_state_pregame); if (server->countdown_state.paused == FALSE) { boolean ok_to_countdown = server_ok_to_countdown(server); if (ok_to_countdown || countdown_event == _network_game_server_countdown_event_stop) { if (server->countdown_state.active == TRUE) { if (!server->countdown_state.adjusted_time_this_tick) { switch (countdown_event) { case _network_game_server_countdown_event_player_left: server->countdown_state.adjusted_time_this_tick = TRUE; countdown_timer_increment( &server->countdown_state.timer, NETWORK_GAME_COUNTDOWN_ADJUSTMENT, NETWORK_GAME_COUNTDOWN_TIME); break; case _network_game_server_countdown_event_player_joined: server->countdown_state.adjusted_time_this_tick = TRUE; if (countdown_timer_get_time_remaining(&server->countdown_state.timer) > NETWORK_GAME_MINIMUM_COUNTDOWN_TIME) { countdown_timer_decrement( &server->countdown_state.timer, NETWORK_GAME_COUNTDOWN_ADJUSTMENT); if (countdown_timer_get_time_remaining(&server->countdown_state.timer) < NETWORK_GAME_MINIMUM_COUNTDOWN_TIME) { countdown_timer_set_time_remaining( &server->countdown_state.timer, NETWORK_GAME_MINIMUM_COUNTDOWN_TIME); } } break; case _network_game_server_countdown_event_stop: server->countdown_state.active = FALSE; server->countdown_state.adjusted_time_this_tick = TRUE; break; case _network_game_server_countdown_event_start_immediately: server->countdown_state.adjusted_time_this_tick = TRUE; countdown_timer_set_time_remaining(&server->countdown_state.timer, 0); break; } } } else { unsigned long now = system_milliseconds(); if (countdown_event == _network_game_server_countdown_event_start_immediately) { countdown_timer_set_time_remaining(&server->countdown_state.timer, 0); server->countdown_state.active = TRUE; server->countdown_state.adjusted_time_this_tick = FALSE; } else { if (network_game_should_accept_remote_connections() == FALSE || network_game_server_get_client_machine_count(server) > 1) { unsigned long countdown; if (network_game_is_splitscreen_local()) countdown = NETWORK_GAME_SPLITSCREEN_COUNTDOWN_TIME; else countdown = NETWORK_GAME_COUNTDOWN_TIME; server->countdown_state.active = TRUE; countdown_timer_set_time_remaining( &server->countdown_state.timer, countdown); server->countdown_state.adjusted_time_this_tick = FALSE; server->countdown_state.last_countdown_message_time = 0; } } } } } return; } /* ---------- private code */ void get_unique_random_name( struct network_game_server *server, struct network_player *player) { wchar_t const *name; long duplicate_count; do { long player_index; name = network_game_get_random_player_name(); duplicate_count = 0; for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT; player_index++) { struct network_player *existing_player = &server->game.players[player_index]; if (network_player_is_valid(existing_player) && !ustrcmp(existing_player->name, name)) { duplicate_count++; } } } while (duplicate_count != 0); ustrncpy(player->name, name, NETWORK_PLAYER_NAME_LENGTH - 1); player->name[NETWORK_PLAYER_NAME_LENGTH - 1] = 0; return; } void get_unique_random_color( struct network_game_server *server, struct network_player *player) { long attempt_count = 0; long color_index; boolean unique; do { long player_index; color_index = attempt_count < MAXIMUM_GOOD_COLOR_ATTEMPTS ? player_profile_get_random_good_color() : player_profile_get_random_color(); unique = TRUE; for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT; player_index++) { if (network_player_is_valid(&server->game.players[player_index]) && server->game.players[player_index].primary_color_index == color_index) { unique = FALSE; break; } } attempt_count++; } while (!unique); player->primary_color_index = (short)color_index; return; } static boolean player_name_is_unique( struct network_game_server *server, wchar_t const *name) { long player_index; for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT; player_index++) { struct network_player *player = &server->game.players[player_index]; if (network_player_is_valid(player) && !ustrcmp(player->name, name)) return FALSE; } return TRUE; } static short network_game_server_get_client_machine_count( struct network_game_server *server) { short client_machine_count = 0; short client_machine_index; for (client_machine_index = 0; client_machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; client_machine_index++) { if (server->client_machines[client_machine_index].connection && server->client_machines[client_machine_index].machine_index != NONE) { client_machine_count++; } } return client_machine_count; } static boolean network_game_server_setup_game_from_playlist( struct network_game_server *server) { boolean success = FALSE; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x961, server); network_event("setting up a net game"); if (game_engine_get_current_stage(&server->game.variant, server->game.map.name)) { wchar_t machine_name[MAXIMUM_MACHINE_NAME_LENGTH] = L""; network_game_generate_local_machine_name(machine_name); ustrncpy(server->game.name, machine_name, NETWORK_GAME_NAME_LENGTH - 1); server->game.name[NETWORK_GAME_NAME_LENGTH - 1] = L'\0'; server->game.map.version = 0; server->game.minimum_players = 2; server->game.maximum_players = MAXIMUM_NETWORK_PLAYER_COUNT; if (server->game.variant.has_teams) { server->game.maximum_teams = 2; } else { server->game.maximum_teams = 1; } network_game_server_open_game(server); success = TRUE; } else { error( _error_silent, "network game setup failed; probably due to a missing playlist"); } return success; } static boolean network_game_server_add_new_client( struct network_game_server *server, struct network_connection *new_connection) { boolean success = FALSE; int i; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x7D4, server && new_connection); if (network_game_server_game_is_open(server)) { for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { if (server->client_machines[i].machine_index == NONE) { struct transport_address client_address = { 0 }; network_connection_get_address( new_connection, &client_address, NULL); if (client_address.address.ipv4_address) { if (!network_game_should_accept_remote_connections() && client_address.address.ipv4_address != IPV4_LOOPBACK_ADDRESS) { network_event( "remote system tried to join our server but we are not accepting remote connections: address= '%s'", transport_address_to_string(&client_address)); } else { server->client_machines[i].connection = new_connection; network_game_invalidate_machine(&server->game, i); server->client_machines[i].machine_index = (short)i; server->client_machines[i].flags = FLAG(_network_client_machine_connected_bit); success = network_connection_server_accept_client_connection( server->connection, new_connection); if (success == TRUE) { network_event( "new remote connection accepted from %s", transport_address_to_string(&client_address)); } } } else { network_event( "network_connection_get_address() failed to get a valid address in network_game_server_add_new_client()"); } break; } } if (i == MAXIMUM_NETWORK_MACHINE_COUNT) { network_event( "failed to find an available machine slot in network_game_server_add_new_client()"); } } else { network_event( "network_game_server_add_new_client() failed because the game is closed"); } return success; } static boolean network_game_server_handle_public_endpoint( struct network_game_server *server) { boolean success = TRUE; word datagram_buffer[DATAGRAM_MAXIMUM_SIZE / sizeof(word)]; word *message = datagram_buffer; word datagram_size = sizeof(datagram_buffer); struct transport_address source_address; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x810, server); while (success && network_connection_read( server->connection, message, &datagram_size, &source_address)) { if ((success = network_game_server_handle_datagram( server, message, datagram_size, &source_address)) == FALSE) { network_event( "network_game_server_handle_datagram() failed in network_game_server_handle_public_endpoint()"); } datagram_size = sizeof(datagram_buffer); } return success; } static boolean network_game_server_handle_client_machines( struct network_game_server *server) { boolean success = TRUE; int i; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x827, server); for (i = 0; success && i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { if (server->client_machines[i].machine_index != NONE) { if (!network_connection_active(server->client_machines[i].connection)) { if (network_game_server_remove_machine_from_game( server, &server->game.machines[server->client_machines[i].machine_index])) { network_event( "client machine %x removed from game", server->client_machines[i].machine_index); network_game_server_dump(server); } else { network_event( "failed to remove client machine %x from game", server->client_machines[i].machine_index); network_game_server_dump(server); } } else if (network_connection_idle( server->client_machines[i].connection, _connection_dont_timeout, NULL) && network_connection_connected(server->client_machines[i].connection)) { word message_buffer[MAXIMUM_NETWORK_MESSAGE_SIZE / sizeof(word)]; word *message = message_buffer; word message_buffer_size = sizeof(message_buffer); while (success && network_connection_read( server->client_machines[i].connection, message, &message_buffer_size, NULL)) { if (network_game_server_handle_client_message( server, server->client_machines + i, message, message_buffer_size)) { message_buffer_size = sizeof(message_buffer); } else { network_event( "network_game_server_handle_client_message() failed in network_game_server_handle_client_machines()"); if (network_game_server_remove_machine_from_game( server, &server->game.machines[server->client_machines[i].machine_index])) { network_event( "client machine removed from game", server->client_machines[i].machine_index); } else if (!network_game_server_remove_client_machine_from_game( server, &server->client_machines[i])) { network_event( "failed to remove client machine from game", server->client_machines[i].machine_index); } break; } } } else { if (network_game_server_remove_machine_from_game( server, &server->game.machines[server->client_machines[i].machine_index])) { network_event( "client machine removed from game", server->client_machines[i].machine_index); } else { network_event( "failed to remove client machine from game", server->client_machines[i].machine_index); } continue; } } } return success; } static void network_game_server_send_rejection_message( struct transport_endpoint *endpoint, word reason) { struct message_server_machine_rejected farewell_message = { reason }; message_header *message; match_assert( NETWORK_SERVER_MANAGER_FILE, 0x878, endpoint && (reason < NUMBER_OF_SERVER_REJECTION_CODES)); message = create_network_game_message( _message_server_machine_rejected, &farewell_message, sizeof(farewell_message)); if (message != NULL) { int length = GET_MESSAGE_SIZE(*message); int bytes_written; byte_swap_message_header(message, _byte_order_network); bytes_written = write_endpoint(endpoint, message, length); if (bytes_written != length) { network_event( "error sending rejection message to client; transport error= '%s'", transport_error_to_string(bytes_written)); } } else { network_event( "failed to create a message_server_machine_rejected message in network_game_server_send_rejection_message"); } return; } static void network_game_server_reject_connection_game_is_full( struct transport_endpoint *endpoint) { network_event("client connection refused; game is full"); network_game_server_send_rejection_message( endpoint, _rejection_code_game_is_full); return; } static boolean network_game_server_idle_postgame_tasks( struct network_game_server *server) { unsigned long now = system_milliseconds(); boolean success = TRUE; unsigned long keep_alive_deadline = (unsigned long)server->time_of_last_keep_alive + 5UL * MILLISECONDS_PER_SECOND; if (now > keep_alive_deadline) { struct message_server_postgame_keep_alive message_packet = { 0 }; struct network_message *message; message = create_network_game_message( _message_server_postgame_keep_alive, &message_packet, sizeof(message_packet)); network_game_server_send_message_to_all_machines(server, message); server->time_of_last_keep_alive = (long)now; } return success; } static boolean network_game_server_have_all_machines_have_precached( struct network_game_server *server) { long i; boolean all_machines_have_precached = TRUE; for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { struct network_game_server_client_machine *machine = server->client_machines + i; if (machine->machine_index >= 0 && machine->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT) { boolean client_has_precached = TEST_FLAG( server->client_machines[i].flags, _network_client_machine_precached_bit); if (!client_has_precached) { all_machines_have_precached = FALSE; break; } } } match_assert( NETWORK_SERVER_MANAGER_FILE, 0x8C8, !all_machines_have_precached || cache_files_precache_map_loaded(main_get_multiplayer_map_name())); return all_machines_have_precached; } static boolean network_game_server_idle_pregame_tasks( struct network_game_server *server) { long now = (long)system_milliseconds(); boolean success = TRUE; if (server->sent_start_game_message == FALSE) { long itr; for (itr = 0; itr < MAXIMUM_NETWORK_MACHINE_COUNT; itr++) { struct network_game_server_client_machine *client_machine = server->client_machines + itr; if (client_machine->connection != NULL && !network_connection_active(client_machine->connection)) { network_event("booting dead client machine %d", itr); network_game_server_remove_client_machine_from_game( server, client_machine); } } if (server->countdown_state.active == TRUE) { boolean send_countdown_update = FALSE; boolean ok_to_countdown = server_ok_to_countdown(server); if (!ok_to_countdown) { csmemset( &server->countdown_state, 0, sizeof(server->countdown_state)); send_countdown_update = TRUE; } else if (countdown_timer_get_time_remaining( &server->countdown_state.timer) == 0 && network_game_server_have_all_machines_have_precached(server) && server->countdown_state.paused == FALSE) { network_game_server_close_game(server); if ((success = network_game_server_start_network_game(server)) != TRUE) network_event("network_game_server_start_network_game() failed"); } else if ((long)((unsigned long)now - (unsigned long)server->countdown_state.last_countdown_message_time) > MILLISECONDS_PER_SECOND) { send_countdown_update = TRUE; } if (send_countdown_update == TRUE) { struct message_server_pregame_countdown message_packet; struct network_message *message; server->countdown_state.adjusted_time_this_tick = FALSE; if (ok_to_countdown) { long time_remaining = countdown_timer_get_time_remaining( &server->countdown_state.timer); message_packet.seconds_to_start = (short)(time_remaining / MILLISECONDS_PER_SECOND); } else { message_packet.seconds_to_start = NONE; } message = create_network_game_message( _message_server_pregame_countdown, &message_packet, sizeof(message_packet)); if (message != NULL) { if (network_game_server_send_message_to_all_machines(server, message)) { server->countdown_state.last_countdown_message_time = now; } else { network_event( "failed to send a message_server_pregame_countdown to all clients"); } } } } else { long keep_alive_deadline = (long)( (unsigned long)server->time_of_last_keep_alive + 5UL * MILLISECONDS_PER_SECOND); if (now > keep_alive_deadline) { struct message_server_pregame_keep_alive message_packet = { 0 }; struct network_message *message; message = create_network_game_message( _message_server_pregame_keep_alive, &message_packet, sizeof(message_packet)); network_game_server_send_message_to_all_machines(server, message); server->time_of_last_keep_alive = now; } } } else if (server->time_of_first_client_loading_completion) { if (system_milliseconds() - server->time_of_first_client_loading_completion >= NETWORK_GAME_SERVER_MAXIMUM_WAIT_TIME_FOR_LEVEL_LOADING) { int i; for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { if (TEST_FLAG( server->client_machines[i].flags, _network_client_machine_connected_bit) && !TEST_FLAG( server->client_machines[i].flags, _network_client_machine_level_loaded_bit)) { char ascii_name[MAXIMUM_MACHINE_NAME_LENGTH]; boolean removed; network_event( "forcibly removing client system '%s' due to timeout while loading for game", wide_to_ascii( server->game.machines[ server->client_machines[i].machine_index].name, ascii_name, sizeof(ascii_name)) ? ascii_name : ""); removed = network_game_server_remove_client_machine_from_game( server, &server->client_machines[i]); match_assert(NETWORK_SERVER_MANAGER_FILE, 0x94E, removed); } } network_game_server_all_machines_have_loaded(server); } } return success; } boolean network_game_server_reset_to_pregame( struct network_game_server *server) { boolean success = FALSE; struct message_server_switch_to_pregame message_packet = { 0 }; struct network_message *message; int i; match_assert(NETWORK_SERVER_MANAGER_FILE, 0x324, server); csmemset(&server->countdown_state, 0, sizeof(server->countdown_state)); server->next_update_number = 0; server->time_of_first_client_loading_completion = 0; server->sent_start_game_message = FALSE; server->queued_player_valid = FALSE; /* Preserve January's 32-bit wrap without overflowing signed arithmetic. * VC7 converts the unsigned result back to the same signed bit pattern. */ server->game.number_of_games_played = (long)((unsigned long)server->game.number_of_games_played + 1); if (server->state == _network_game_server_state_postgame) { message = create_network_game_message( _message_server_switch_to_pregame, &message_packet, sizeof(message_packet)); if (message && network_game_server_send_message_to_all_machines(server, message)) { network_event("server resetting to pregame"); if (server->game.variant.has_teams) { for (i = 0; i < MAXIMUM_NETWORK_PLAYER_COUNT; i++) { if (network_player_is_valid(&server->game.players[i])) { switch (server->game.players[i].team_index) { case _team_red: server->game.players[i].team_index = _team_blue; break; case _team_blue: server->game.players[i].team_index = _team_red; break; } } } } for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++) { SET_FLAG( server->client_machines[i].flags, _network_client_machine_level_loaded_bit, FALSE); server->client_machines[i].last_received_update_sequence_number = 0; server->client_machines[i].stall_start_time = 0; } network_game_reset_for_next_round(&server->game, FALSE); if (network_game_server_setup_game_from_playlist(server)) { struct network_game game_settings; csmemcpy(&game_settings, &server->game, sizeof(server->game)); message = create_network_game_message( _message_server_game_settings_update, &game_settings, sizeof(game_settings)); if (message && network_game_server_send_message_to_all_machines(server, message)) { server->state = _network_game_server_state_pregame; success = TRUE; } } else { struct message_server_graceful_game_exit_pregame shutdown_message = { 0 }; message = create_network_game_message( _message_server_graceful_game_exit_pregame, &shutdown_message, sizeof(shutdown_message)); if (message && network_game_server_send_message_to_all_machines(server, message) && network_game_server_handle_client_machines(server)) { network_event("the playlist has ended - server going down"); } else { network_event( "the playlist has ended - server going down, but failed to alert client machines"); } } } else { network_event("failed to signal all client machines to switch to pregame"); } } else { success = network_game_server_setup_game_from_playlist(server); if (server->game.variant.has_teams) { for (i = 0; i < MAXIMUM_NETWORK_PLAYER_COUNT; i++) { if (network_player_is_valid(&server->game.players[i])) { switch (server->game.players[i].team_index) { case _team_red: server->game.players[i].team_index = _team_blue; break; case _team_blue: server->game.players[i].team_index = _team_red; break; } } } } } return success; }