mirror of
https://github.com/astromaddie/HaloCE-VR.git
synced 2026-10-20 17:00:12 +02:00
3090 lines
90 KiB
C
3090 lines
90 KiB
C
/*
|
|
NETWORK_CLIENT_MANAGER.C
|
|
|
|
symbols in this file:
|
|
001141C0 0040:
|
|
_check_networking_and_generate_error (0000)
|
|
00114200 0060:
|
|
_network_game_client_dispose (0000)
|
|
00114260 0020:
|
|
_network_game_client_keep_alive (0000)
|
|
00114280 0070:
|
|
_network_game_client_get_state (0000)
|
|
001142F0 0100:
|
|
_network_game_client_initiate_join_game (0000)
|
|
001143F0 0070:
|
|
_network_game_client_set_machine (0000)
|
|
00114460 0030:
|
|
_network_game_client_get_machine (0000)
|
|
00114490 0040:
|
|
_network_game_client_get_machine_index (0000)
|
|
001144D0 0040:
|
|
_network_game_client_get_available_games (0000)
|
|
00114510 0040:
|
|
_network_game_client_get_error (0000)
|
|
00114550 0040:
|
|
_network_game_client_get_seconds_to_game_start (0000)
|
|
00114590 0010:
|
|
_network_game_client_write (0000)
|
|
001145A0 00d0:
|
|
_network_game_client_address_matches_server (0000)
|
|
00114670 0070:
|
|
_network_game_client_game_out_of_sync (0000)
|
|
001146E0 00b0:
|
|
_network_game_client_ponged (0000)
|
|
00114790 0110:
|
|
_network_game_client_accepted_into_game (0000)
|
|
001148A0 0130:
|
|
_network_game_client_game_settings_updated (0000)
|
|
001149D0 0060:
|
|
_unstrip_player_index (0000)
|
|
00114A30 01a0:
|
|
_network_game_client_game_has_started (0000)
|
|
00114BD0 0190:
|
|
_network_game_client_handle_game_update (0000)
|
|
00114D60 0100:
|
|
_network_game_client_add_player_to_game (0000)
|
|
00114E60 0050:
|
|
_network_game_client_switch_to_postgame (0000)
|
|
00114EB0 00b0:
|
|
_network_game_client_switch_to_pregame (0000)
|
|
00114F60 0040:
|
|
_network_game_client_get_connection (0000)
|
|
00114FA0 0050:
|
|
_network_game_client_get_remote_server_address (0000)
|
|
00114FF0 0040:
|
|
_network_game_client_get_game (0000)
|
|
00115030 0040:
|
|
_network_game_client_server_has_started_game (0000)
|
|
00115070 0040:
|
|
_network_game_client_get_next_update_number (0000)
|
|
001150B0 0040:
|
|
_network_client_get_oos (0000)
|
|
001150F0 01f0:
|
|
_network_game_client_add_player (0000)
|
|
001152E0 0100:
|
|
_network_game_client_update_local_player_data (0000)
|
|
001153E0 00d0:
|
|
_network_game_client_request_start_time_change (0000)
|
|
001154B0 0050:
|
|
_network_game_client_countdown_timer_update (0000)
|
|
00115500 0030:
|
|
_network_game_client_advertised_game_is_valid (0000)
|
|
00115530 02d0:
|
|
_add_advertised_game (0000)
|
|
00115800 0050:
|
|
_network_game_client_set_error (0000)
|
|
00115850 00c0:
|
|
_network_game_client_update_precache_status (0000)
|
|
00115910 0080:
|
|
_network_game_client_process_incoming_messages (0000)
|
|
00115990 0260:
|
|
_network_game_client_leave_game (0000)
|
|
00115BF0 01f0:
|
|
_network_game_client_request_remove_player (0000)
|
|
00115DE0 0170:
|
|
_network_game_client_remove_player (0000)
|
|
00115F50 0050:
|
|
_network_game_client_new_advertised_game (0000)
|
|
00115FA0 0070:
|
|
_network_game_client_game_shutdown (0000)
|
|
00116010 00e0:
|
|
_network_game_client_reset (0000)
|
|
001160F0 02c0:
|
|
_network_game_client_idle_searching (0000)
|
|
001163B0 0180:
|
|
_network_game_client_idle_joining (0000)
|
|
00116530 00d0:
|
|
_network_game_client_idle_pregame (0000)
|
|
00116600 0190:
|
|
_network_game_client_idle_ingame (0000)
|
|
00116790 00a0:
|
|
_network_game_client_idle_postgame (0000)
|
|
00116830 0090:
|
|
_network_game_client_create (0000)
|
|
001168C0 0130:
|
|
_network_game_client_idle (0000)
|
|
001169F0 00c0:
|
|
_network_game_client_rejected_by_game (0000)
|
|
0027FC5C 0018:
|
|
??_C@_0BI@DEMLIONO@network?5client?5disposed?$AA@ (0000)
|
|
0027FC74 002d:
|
|
??_C@_0CN@POHNGKMA@network_game_client_dont_use_dir@ (0000)
|
|
0027FCA4 0033:
|
|
??_C@_0DD@GHLNCEHN@c?3?2halo?2SOURCE?2networking?2networ@ (0000)
|
|
0027FCD8 0007:
|
|
??_C@_06DGDLPBAN@client?$AA@ (0000)
|
|
0027FCE0 0035:
|
|
??_C@_0DF@IEPBBGCD@failed?5attempt?5to?5initiate?5a?5con@ (0000)
|
|
0027FD18 0023:
|
|
??_C@_0CD@NIGOILLF@attempting?5to?5connect?5to?5game?5?$EA?5@ (0000)
|
|
0027FD40 00e1:
|
|
??_C@_0OB@CAKDCBMD@client?5?$CG?$CG?5?$CIclient?9?$DOstate?5?$DN?$DN?5_net@ (0000)
|
|
0027FE28 0065:
|
|
??_C@_0GF@JAOOPLIH@client?5?$CG?$CG?5?$CIclient?9?$DOmachine_index@ (0000)
|
|
0027FE90 001e:
|
|
??_C@_0BO@IIIGFMHE@address?9?$DOaddress?4ipv4_address?$AA@ (0000)
|
|
0027FEB0 0010:
|
|
??_C@_0BA@IONABMGK@address?5?$CB?$DN?5NULL?$AA@ (0000)
|
|
0027FEC0 0013:
|
|
??_C@_0BD@PONNKDL@client?9?$DOconnection?$AA@ (0000)
|
|
0027FED4 000f:
|
|
??_C@_0P@FOLJNCCO@client?5?$CB?$DN?5NULL?$AA@ (0000)
|
|
0027FEE4 002d:
|
|
??_C@_0CN@FOHIFFFA@local?5machine?5is?5out?5of?5sync?5wit@ (0000)
|
|
0027FF14 0036:
|
|
??_C@_0DG@JNMFILGG@received?5a?5pong?5from?5a?5system?5we@ (0000)
|
|
0027FF4C 0020:
|
|
??_C@_0CA@IDICKDHG@received?5a?5pong?5from?5the?5future?$AA@ (0000)
|
|
0027FF6C 0019:
|
|
??_C@_0BJ@NIPOLBAB@client?5?$CG?$CG?5source_address?$AA@ (0000)
|
|
0027FF88 004c:
|
|
??_C@_0EM@DJKCNCJP@received?5a?5message_server_machin@ (0000)
|
|
0027FFD4 003b:
|
|
??_C@_0DL@PONBKBJJ@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00280010 005b:
|
|
??_C@_0FL@OHPOKFBL@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
0028006C 0033:
|
|
??_C@_0DD@POLEJMBC@successfully?5joined?5a?5net?5game?$DL?5@ (0000)
|
|
002800A0 0064:
|
|
??_C@_0GE@OLBCDHF@client?5?$CG?$CG?5source_address?5?$CG?$CG?5mess@ (0000)
|
|
00280108 005e:
|
|
??_C@_0FO@NIJHHMKN@invalid?5message_server_game_sett@ (0000)
|
|
00280168 0021:
|
|
??_C@_0CB@KMIKJFNP@player?5count?5?$CFd?5machine?5count?5?$CFd@ (0000)
|
|
00280190 0060:
|
|
??_C@_0GA@FCJMEIKG@received?5updated?5game?5settings?5f@ (0000)
|
|
002801F0 0017:
|
|
??_C@_0BH@KPFLOHLF@precaching?5map?5?8?$CFs?8?4?4?4?$AA@ (0000)
|
|
00280208 0019:
|
|
??_C@_0BJ@MGLALEIH@client?5?$CG?$CG?5message_packet?$AA@ (0000)
|
|
00280224 0027:
|
|
??_C@_0CH@CHLJGLMB@failed?5to?5load?5the?5necessary?5gam@ (0000)
|
|
0028024C 0031:
|
|
??_C@_0DB@BGLEMKKH@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00280280 0051:
|
|
??_C@_0FB@NFJPBNDI@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
002802D4 0028:
|
|
??_C@_0CI@DFFIDDAF@local?5machine?5is?5loaded?5?$CG?5ready?5@ (0000)
|
|
00280300 0040:
|
|
??_C@_0EA@MFKNPKNH@client?5?$CG?$CG?5?$CIclient?9?$DOstate?5?$DN?$DN?5_net@ (0000)
|
|
00280340 0036:
|
|
??_C@_0DG@PKKMJLJH@client?5is?5lagging?5behind?5the?5ser@ (0000)
|
|
00280378 0061:
|
|
??_C@_0GB@LLAIIL@out?5of?5sync?3?5client?1server?5rando@ (0000)
|
|
002803DC 002d:
|
|
??_C@_0CN@HEOJJEAC@not?5a?5bug?0?5but?5update?5?$CFd?5time?5?$CFd@ (0000)
|
|
0028040C 003e:
|
|
??_C@_0DO@OFILJPBP@out?5of?5sync?3?5missed?5a?5server?5upd@ (0000)
|
|
0028044C 003c:
|
|
??_C@_0DM@JCJEPCIA@added?5new?5player?5to?5the?5game?5?$CIma@ (0000)
|
|
00280488 0011:
|
|
??_C@_0BB@NJALFKEL@client?5?$CG?$CG?5player?$AA@ (0000)
|
|
0028049C 0016:
|
|
??_C@_0BG@OEHGDLGH@switching?5to?5postgame?$AA@ (0000)
|
|
002804B4 0015:
|
|
??_C@_0BF@FFECCEDB@switching?5to?5pregame?$AA@ (0000)
|
|
002804CC 001e:
|
|
??_C@_0BO@NFAGDPGC@client?5is?5in?5an?5unknown?5state?$AA@ (0000)
|
|
002804EC 002e:
|
|
??_C@_0CO@BDJGBKN@client?5tried?5to?5add?5a?5new?5player@ (0000)
|
|
00280520 0044:
|
|
??_C@_0EE@EPIGONGI@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00280568 0064:
|
|
??_C@_0GE@JOEDNBHN@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
002805D0 0045:
|
|
??_C@_0EF@CDEPJEJP@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00280618 0065:
|
|
??_C@_0GF@EOEDLFEI@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
00280680 0039:
|
|
??_C@_0DJ@IAEGENPP@can?8t?5add?5players?5to?5a?5game?5unti@ (0000)
|
|
002806BC 0034:
|
|
??_C@_0DE@DFECLBOM@requesting?5a?5player?5addition?5?$CIco@ (0000)
|
|
002806F0 005a:
|
|
??_C@_0FK@DNGOMOFK@client?5?$CG?$CG?5?$CIlocal_player_index?$DO?$DN0@ (0000)
|
|
00280750 0075:
|
|
??_C@_0HF@KCCMHIFI@network_game_client_update_local@ (0000)
|
|
002807C8 0020:
|
|
??_C@_0CA@GNHJCNBC@network_player_is_valid?$CIplayer?$CJ?$AA@ (0000)
|
|
002807E8 002d:
|
|
??_C@_0CN@MLPKDKFB@player?9?$DOmachine_index?$DN?$DNclient?9?$DOm@ (0000)
|
|
00280818 005b:
|
|
??_C@_0FL@EPANHPNG@failed?5to?5send?5a?5message_client_@ (0000)
|
|
00280878 006b:
|
|
??_C@_0GL@IAFAMNHJ@network_game_client_request_star@ (0000)
|
|
002808E8 0042:
|
|
??_C@_0EC@OCMHEGGP@?$CIrequest_type?$DO?$DN0?$CJ?5?$CG?$CG?5?$CIrequest_ty@ (0000)
|
|
0028092C 0038:
|
|
??_C@_0DI@NNABEADG@there?5is?5?$CFs?5?$CFs?5net?5game?5with?5?$CFd?5@ (0000)
|
|
00280964 0009:
|
|
??_C@_08EDJFMNDP@a?5closed?$AA@ (0000)
|
|
00280970 0008:
|
|
??_C@_07JPIGLNNP@an?5open?$AA@ (0000)
|
|
00280978 0013:
|
|
??_C@_0BD@DEAGDPEH@?$DMunknown?5platform?$DO?$AA@ (0000)
|
|
0028098C 0003:
|
|
??_C@_02LHFGBEJL@PC?$AA@ (0000)
|
|
00280990 0005:
|
|
??_C@_04KEMOGDLN@XBox?$AA@ (0000)
|
|
00280998 0008:
|
|
??_C@_17JLFEDMMI@?$AA?$DP?$AA?$DP?$AA?$DP?$AA?$AA@ (0000)
|
|
002809A0 0050:
|
|
??_C@_0FA@NLFFOHBN@not?5fatal?0?5but?5we?5have?5to?5many?5a@ (0000)
|
|
002809F0 000f:
|
|
??_C@_0P@JAPJELHA@current?9?$DOvalid?$AA@ (0000)
|
|
00280A00 0065:
|
|
??_C@_0GF@OLAKLCGH@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
00280A68 005f:
|
|
??_C@_0FP@INNHEEHK@network_game_client_handle_messa@ (0000)
|
|
00280AC8 004b:
|
|
??_C@_0EL@PNCEEMAP@network_connection_disconnect?$CI?$CJ?5@ (0000)
|
|
00280B18 0066:
|
|
??_C@_0GG@PLNHOJNK@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
00280B80 0049:
|
|
??_C@_0EJ@ENIOKCKK@network_connection_disconnect?$CI?$CJ?5@ (0000)
|
|
00280BD0 004a:
|
|
??_C@_0EK@GLADDNKH@network_connection_disconnect?$CI?$CJ?5@ (0000)
|
|
00280C20 0045:
|
|
??_C@_0EF@IGAGJDLA@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00280C68 004a:
|
|
??_C@_0EK@PGHLLHE@network_connection_disconnect?$CI?$CJ?5@ (0000)
|
|
00280CB4 0032:
|
|
??_C@_0DC@NBAMODMB@?$CBnetwork_connection_connected?$CIcl@ (0000)
|
|
00280CE8 0015:
|
|
??_C@_0BF@IHNDJMJI@leaving?5network?5game?$AA@ (0000)
|
|
00280D00 001d:
|
|
??_C@_0BN@LOKEMLAG@client?5?$CG?$CG?5client?9?$DOconnection?$AA@ (0000)
|
|
00280D20 0049:
|
|
??_C@_0EJ@ENDAFHHG@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00280D70 0069:
|
|
??_C@_0GJ@FAIKKIGI@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
00280DE0 0047:
|
|
??_C@_0EH@PAHGGHIO@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00280E28 0047:
|
|
??_C@_0EH@NIHALOOC@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00280E70 003e:
|
|
??_C@_0DO@LCAGMDAF@can?8t?5remove?5players?5from?5a?5game@ (0000)
|
|
00280EB0 0033:
|
|
??_C@_0DD@MJJEPI@requesting?5a?5player?5removal?5?$CIcon@ (0000)
|
|
00280EE4 0039:
|
|
??_C@_0DJ@ICFAMCBO@client?8s?5can?5only?5remove?5players@ (0000)
|
|
00280F20 002a:
|
|
??_C@_0CK@HGKAMGLN@client?5?$CG?$CG?5network_player_is_vali@ (0000)
|
|
00280F50 004b:
|
|
??_C@_0EL@IAJNHEOO@network?5game?5tried?5to?5delete?5a?5p@ (0000)
|
|
00280F9C 003a:
|
|
??_C@_0DK@NONHICBE@no?5local?5players?5remain?5in?5the?5g@ (0000)
|
|
00280FD8 0027:
|
|
??_C@_0CH@HDEKFDCH@?$CFx?5quit?5of?5of?5game?5at?5tick?5?$CFd?5?$CIn@ (0000)
|
|
00281000 001f:
|
|
??_C@_0BP@BGMFPCP@the?5game?5host?5is?5shutting?5down?$AA@ (0000)
|
|
00281020 002b:
|
|
??_C@_0CL@FIOOPCIN@failed?5to?5reinitialize?5network?5g@ (0000)
|
|
0028104C 002f:
|
|
??_C@_0CP@KMAGFDPK@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00281080 004f:
|
|
??_C@_0EP@PHHJAEIL@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
002810D0 0040:
|
|
??_C@_0EA@BPICBMAG@failed?5to?5create?5a?5message_clien@ (0000)
|
|
00281110 0060:
|
|
??_C@_0GA@PDNHMAGG@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
00281170 0028:
|
|
??_C@_0CI@BAIHEHNF@sent?5out?5a?5broadcast?5game?5search@ (0000)
|
|
00281198 005f:
|
|
??_C@_0FP@MCNPILK@network_game_client_process_inco@ (0000)
|
|
002811F8 0049:
|
|
??_C@_0EJ@CNGHFAJM@network_connection_idle?$CI?$CJ?5failed@ (0000)
|
|
00281244 0030:
|
|
??_C@_0DA@OHNFENCP@network_game_client_initiate_joi@ (0000)
|
|
00281278 0047:
|
|
??_C@_0EH@NDJKGODE@network_connection_idle?$CI?$CJ?5failed@ (0000)
|
|
002812C0 0045:
|
|
??_C@_0EF@EDNLPKBO@client?5connection?5process?5has?5ti@ (0000)
|
|
00281308 005d:
|
|
??_C@_0FN@FLBCCMKM@network_game_client_process_inco@ (0000)
|
|
00281368 003c:
|
|
??_C@_0DM@ONBHNBGE@failed?5to?5create?5a?5message_clien@ (0000)
|
|
002813A8 0056:
|
|
??_C@_0FG@EPCJDBOJ@network_game_client_write?$CI?$CJ?5fail@ (0000)
|
|
00281400 005d:
|
|
??_C@_0FN@PFNMPIAB@network_game_client_process_inco@ (0000)
|
|
00281460 0047:
|
|
??_C@_0EH@HNFELKJJ@network_connection_idle?$CI?$CJ?5failed@ (0000)
|
|
002814A8 0046:
|
|
??_C@_0EG@BKDKHIKF@network_connection_idle?$CI?$CJ?5failed@ (0000)
|
|
002814F0 001c:
|
|
??_C@_0BM@GGCBKEBE@new2?5idle?5in?5game?5abort?5hit?$AA@ (0000)
|
|
0028150C 001b:
|
|
??_C@_0BL@IEONCJFK@new?5idle?5in?5game?5abort?5hit?$AA@ (0000)
|
|
00281528 005c:
|
|
??_C@_0FM@JKCCAGHN@network_game_client_process_inco@ (0000)
|
|
00281588 0050:
|
|
??_C@_0FA@IKFHHCAC@network?5client?5connection?5has?5be@ (0000)
|
|
002815D8 002d:
|
|
??_C@_0CN@GOKOEOFK@network?5connection?5went?5down?5?$CIid@ (0000)
|
|
00281608 005e:
|
|
??_C@_0FO@IANECALJ@network_game_client_process_inco@ (0000)
|
|
00281668 0048:
|
|
??_C@_0EI@HOIILDIN@network_connection_idle?$CI?$CJ?5failed@ (0000)
|
|
002816B0 0049:
|
|
??_C@_0EJ@KIFFADMI@network_game_create_client?$CI?$CJ?5fai@ (0000)
|
|
002816FC 002e:
|
|
??_C@_0CO@LNIBCIDF@?$CBnetwork_game_client_dont_use_di@ (0000)
|
|
0028172C 0018:
|
|
??_C@_0BI@HFOMKHNG@?$CB?$CCunknown?5client?5state?$CC?$AA@ (0000)
|
|
00281744 002b:
|
|
??_C@_0CL@EHLJKOCJ@network_game_client_idle_postgam@ (0000)
|
|
00281770 0029:
|
|
??_C@_0CJ@NNEAELPC@network_game_client_idle_ingame?$CI@ (0000)
|
|
0028179C 002a:
|
|
??_C@_0CK@IFHOOMMF@network_game_client_idle_pregame@ (0000)
|
|
002817C8 002a:
|
|
??_C@_0CK@NBDMDINO@network_game_client_idle_joining@ (0000)
|
|
002817F4 002c:
|
|
??_C@_0CM@IDLHBFKK@network_game_client_idle_searchi@ (0000)
|
|
00281820 0024:
|
|
??_C@_0CE@DOGOGKDE@unable?5to?5join?5game?3?5reason?$DN?5?$CD?$CFd@ (0000)
|
|
00281844 0024:
|
|
??_C@_0CE@JOPGMOA@_rejection_code_blacklisted_mach@ (0000)
|
|
00281868 001f:
|
|
??_C@_0BP@EHGPFGEK@_rejection_code_game_is_closed?$AA@ (0000)
|
|
00281888 001d:
|
|
??_C@_0BN@IDHMJFHO@_rejection_code_game_is_full?$AA@ (0000)
|
|
002818A8 001d:
|
|
??_C@_0BN@NIEJJECI@_rejection_code_bad_password?$AA@ (0000)
|
|
002818C8 001f:
|
|
??_C@_0BP@MGMNOIHD@_rejection_code_bad_join_token?$AA@ (0000)
|
|
002818E8 0020:
|
|
??_C@_0CA@EEFCENHL@_rejection_code_version_too_new?$AA@ (0000)
|
|
00281908 0020:
|
|
??_C@_0CA@JCNAFAAD@_rejection_code_version_too_old?$AA@ (0000)
|
|
004566D8 0002:
|
|
_allow_out_of_sync (0000)
|
|
_network_game_client_dont_use_directly_in_use (0001)
|
|
*/
|
|
|
|
/* ---------- 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 "cache/cache_files.h"
|
|
#include "cseries/cseries_windows.h"
|
|
#include "cseries/errors.h"
|
|
#include "game/game_engine.h"
|
|
#include "game/local_players.h"
|
|
#include "game/player_queues_new.h"
|
|
#include "game/players.h"
|
|
#include "interface/player_ui.h"
|
|
#include "saved games/player_profile.h"
|
|
#include "interface/ui_widget.h"
|
|
#include "main/main.h"
|
|
#include "memory/data.h"
|
|
#include "networking/network_client_manager.h"
|
|
#include "networking/network_client_message_handler.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_messages.h"
|
|
#include "networking/network_server_manager.h"
|
|
#include "text/unicode.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
/* the native builds' session limits (port/linux/include/halo_port_limits.h) */
|
|
MAXIMUM_NETWORK_MACHINE_COUNT = HALO_PORT_MAXIMUM_NETWORK_MACHINES,
|
|
MAXIMUM_NETWORK_ADVERTISED_GAMES = 9,
|
|
MAXIMUM_NUMBER_OF_PLAYERS = HALO_PORT_MAXIMUM_NETWORK_PLAYERS,
|
|
MAXIMUM_MACHINE_NAME_LENGTH = 32,
|
|
NETWORK_GAME_NAME_LENGTH = 16,
|
|
TRANSPORT_NONCE_LENGTH = 8,
|
|
NETWORK_PLAYER_NAME_LENGTH = 12,
|
|
NETWORK_GAME_MAP_NAME_LENGTH = 0x100,
|
|
/* the client's buffer for any message from the server */
|
|
MAXIMUM_NETWORK_MESSAGE_SIZE = HALO_PORT_MAXIMUM_NETWORK_MESSAGE_SIZE,
|
|
/* how long the network may be down in a game before the game ends (as
|
|
long as the host waits for a silent machine, network_server_manager.c):
|
|
network_game_client_network_lost */
|
|
NETWORK_GAME_CLIENT_LINK_DOWN_TIMEOUT = 15000,
|
|
};
|
|
|
|
enum network_game_client_state
|
|
{
|
|
_network_game_client_state_searching,
|
|
_network_game_client_state_joining,
|
|
_network_game_client_state_pregame,
|
|
_network_game_client_state_ingame,
|
|
_network_game_client_state_postgame,
|
|
NUMBER_OF_NETWORK_GAME_CLIENT_STATES,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_network_game_client_error_none = 0,
|
|
_network_game_client_error_unknown = 1,
|
|
_network_game_client_error_server_shutdown = 8,
|
|
NUMBER_OF_NETWORK_GAME_CLIENT_ERRORS = 9
|
|
};
|
|
|
|
enum
|
|
{
|
|
_network_game_client_connection_established_bit,
|
|
_network_game_client_join_request_sent_bit
|
|
};
|
|
|
|
enum
|
|
{
|
|
NUMBER_OF_GAME_START_REQUESTS = 4
|
|
};
|
|
|
|
enum
|
|
{
|
|
_game_advertisement_open_bit = 1,
|
|
_game_advertisement_has_teams_bit,
|
|
_game_advertisement_oddball_variant_bit
|
|
};
|
|
|
|
enum network_game_platform
|
|
{
|
|
_network_game_platform_xbox,
|
|
_network_game_platform_pc,
|
|
NUMBER_OF_NETWORK_GAME_PLATFORMS
|
|
};
|
|
|
|
enum
|
|
{
|
|
_network_connection_type_client = 2,
|
|
};
|
|
|
|
enum
|
|
{
|
|
BROADCAST_IPV4_ADDRESS = 0xFFFFFFFF
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
#define network_game_get_local_platform() _network_game_platform_xbox
|
|
|
|
#define network_machine_is_valid(machine) \
|
|
((machine) && (machine)->machine_index >= 0 && \
|
|
(machine)->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT)
|
|
|
|
/* ---------- structures */
|
|
|
|
struct transport_key_id
|
|
{
|
|
byte data[8];
|
|
};
|
|
|
|
struct transport_key
|
|
{
|
|
byte data[16];
|
|
};
|
|
|
|
struct transport_xnaddr
|
|
{
|
|
byte data[12];
|
|
};
|
|
|
|
struct network_advertised_game
|
|
{
|
|
struct transport_key_id key_id;
|
|
struct transport_key key;
|
|
struct transport_xnaddr xnaddr;
|
|
byte nonce[TRANSPORT_NONCE_LENGTH];
|
|
unsigned long update_time;
|
|
wchar_t game_name[NETWORK_GAME_NAME_LENGTH];
|
|
struct network_game_map map;
|
|
short engine_type;
|
|
word machine_count;
|
|
word player_count;
|
|
short maximum_player_count;
|
|
short unknown100;
|
|
short platform;
|
|
boolean open;
|
|
boolean valid;
|
|
boolean has_teams;
|
|
boolean oddball_variant;
|
|
};
|
|
|
|
struct message_server_game_advertise
|
|
{
|
|
byte client_nonce[TRANSPORT_NONCE_LENGTH];
|
|
byte nonce[TRANSPORT_NONCE_LENGTH];
|
|
struct transport_key_id key_id;
|
|
struct transport_key key;
|
|
struct transport_xnaddr xnaddr;
|
|
word port;
|
|
word version;
|
|
word platform;
|
|
wchar_t game_name[NETWORK_GAME_NAME_LENGTH];
|
|
byte __unknown5A[0x1A];
|
|
struct network_game_map map;
|
|
short engine_type;
|
|
short machine_count;
|
|
short player_count;
|
|
short maximum_player_count;
|
|
short unknown100;
|
|
word flags;
|
|
byte join_game_token[0x10];
|
|
};
|
|
|
|
struct network_join_parameters
|
|
{
|
|
word __unknown0;
|
|
word __unknown2;
|
|
byte __unknown4[0xE];
|
|
byte join_token[0x10];
|
|
};
|
|
|
|
struct network_connection;
|
|
struct connect_process_input;
|
|
|
|
struct message_client_game_start_request
|
|
{
|
|
short request_type;
|
|
};
|
|
|
|
struct message_client_add_player_request
|
|
{
|
|
struct network_player player;
|
|
};
|
|
|
|
struct message_client_remove_player_request
|
|
{
|
|
struct network_player player;
|
|
};
|
|
|
|
struct message_server_game_update
|
|
{
|
|
unsigned long update_number;
|
|
long random_seed;
|
|
long game_time;
|
|
byte __unknown0C[2];
|
|
short local_player_count;
|
|
struct player_action player_actions[MAXIMUM_NUMBER_OF_PLAYERS];
|
|
};
|
|
|
|
struct message_client_loaded
|
|
{
|
|
long opaque;
|
|
};
|
|
|
|
struct message_client_broadcast_game_search
|
|
{
|
|
word port;
|
|
word __unknown2;
|
|
byte nonce[8];
|
|
};
|
|
|
|
struct message_client_ping
|
|
{
|
|
long timestamp;
|
|
word port;
|
|
byte __padding6[2];
|
|
};
|
|
|
|
/* port/linux/src/p2p.c's */
|
|
void p2p_hardware_id(char *hex, int size);
|
|
|
|
struct message_client_join_game_request
|
|
{
|
|
wchar_t machine_name[MAXIMUM_MACHINE_NAME_LENGTH];
|
|
byte join_token[0x10];
|
|
/* port: this machine's hardware id, as hex (p2p.c's p2p_hardware_id) */
|
|
char hardware_id[0x20];
|
|
};
|
|
|
|
struct message_client_map_is_precached_pregame
|
|
{
|
|
char map_name[NETWORK_GAME_MAP_NAME_LENGTH];
|
|
};
|
|
|
|
struct message_server_machine_accepted
|
|
{
|
|
long random_seed;
|
|
short machine_index;
|
|
byte __padding6[2];
|
|
};
|
|
|
|
struct message_client_settings_request
|
|
{
|
|
wchar_t machine_name[MAXIMUM_MACHINE_NAME_LENGTH];
|
|
char machine_index;
|
|
byte __padding41[3];
|
|
};
|
|
|
|
struct network_game_client
|
|
{
|
|
word machine_index;
|
|
byte __padding2[2];
|
|
struct network_advertised_game available_games[MAXIMUM_NETWORK_ADVERTISED_GAMES];
|
|
struct transport_address ping_address;
|
|
unsigned long last_ping_time;
|
|
byte __padding824[2];
|
|
word ping_sample_count;
|
|
word average_ping;
|
|
boolean pinging;
|
|
byte __padding82B;
|
|
struct network_connection *connection;
|
|
struct connect_process_input *connect_process;
|
|
unsigned long connection_attempt_time;
|
|
struct network_join_parameters join_parameters;
|
|
byte __padding85A[2];
|
|
struct network_game game;
|
|
unsigned long join_in_progress;
|
|
unsigned long last_broadcast_search_time;
|
|
unsigned long next_update_number;
|
|
unsigned long last_precache_time;
|
|
short seconds_to_game_start;
|
|
word state;
|
|
short error;
|
|
word flags;
|
|
boolean connection_silent;
|
|
};
|
|
|
|
typedef char network_machine_size_assert[
|
|
sizeof(struct network_machine) == 0x44 ? 1 : -1];
|
|
typedef char player_profile_size_assert[
|
|
sizeof(struct player_profile) == 0x30 ? 1 : -1];
|
|
/* the update and settings layouts follow the session limits
|
|
(port/linux/include/halo_port_limits.h) */
|
|
typedef char message_server_game_update_size_assert[
|
|
sizeof(struct message_server_game_update) == 0x10 + MAXIMUM_NUMBER_OF_PLAYERS * 0x20 ? 1 : -1];
|
|
typedef char message_server_machine_accepted_size_assert[
|
|
sizeof(struct message_server_machine_accepted) == 8 ? 1 : -1];
|
|
typedef char message_client_settings_request_size_assert[
|
|
sizeof(struct message_client_settings_request) == 0x44 ? 1 : -1];
|
|
typedef char network_game_size_assert[
|
|
sizeof(struct network_game) == HALO_PORT_NETWORK_GAME_SIZE ? 1 : -1];
|
|
typedef char network_game_map_name_offset_assert[
|
|
offsetof(struct network_game, map.name) == 0x24 ? 1 : -1];
|
|
typedef char network_game_machine_count_offset_assert[
|
|
offsetof(struct network_game, machine_count) == 0x112 ? 1 : -1];
|
|
typedef char network_game_local_data_offset_assert[
|
|
offsetof(struct network_game, local_data) == HALO_PORT_NETWORK_GAME_LOCAL_DATA_OFFSET ? 1 : -1];
|
|
typedef char network_game_player_count_offset_assert[
|
|
offsetof(struct network_game, player_count) == HALO_PORT_NETWORK_GAME_PLAYER_COUNT_OFFSET ? 1 : -1];
|
|
typedef char network_game_players_offset_assert[
|
|
offsetof(struct network_game, players) == HALO_PORT_NETWORK_GAME_PLAYERS_OFFSET ? 1 : -1];
|
|
typedef char network_advertised_game_update_time_offset_assert[
|
|
offsetof(struct network_advertised_game, update_time) == 0x2C ? 1 : -1];
|
|
typedef char network_advertised_game_size_assert2[
|
|
sizeof(struct message_server_game_advertise) == 0x114 ? 1 : -1];
|
|
typedef char network_advertised_game_game_name_offset_assert[
|
|
offsetof(struct network_advertised_game, game_name) == 0x30 ? 1 : -1];
|
|
typedef char network_advertised_game_engine_type_offset_assert[
|
|
offsetof(struct network_advertised_game, engine_type) == 0xD4 ? 1 : -1];
|
|
typedef char network_advertised_game_platform_offset_assert[
|
|
offsetof(struct network_advertised_game, platform) == 0xDE ? 1 : -1];
|
|
typedef char network_advertised_game_open_offset_assert[
|
|
offsetof(struct network_advertised_game, open) == 0xE0 ? 1 : -1];
|
|
typedef char network_advertised_game_valid_offset_assert[
|
|
offsetof(struct network_advertised_game, valid) == 0xE1 ? 1 : -1];
|
|
typedef char network_join_parameters_size_assert[
|
|
sizeof(struct network_join_parameters) == 0x22 ? 1 : -1];
|
|
typedef char data_iterator_datum_index_offset_assert[
|
|
offsetof(struct data_iterator, datum_index) == 8 ? 1 : -1];
|
|
typedef char network_advertised_game_size_assert[
|
|
sizeof(struct network_advertised_game) == 0xE4 ? 1 : -1];
|
|
typedef char network_game_client_ping_address_offset_assert[
|
|
offsetof(struct network_game_client, ping_address) == 0x808 ? 1 : -1];
|
|
typedef char network_game_client_ping_sample_count_offset_assert[
|
|
offsetof(struct network_game_client, ping_sample_count) == 0x826 ? 1 : -1];
|
|
typedef char network_game_client_pinging_offset_assert[
|
|
offsetof(struct network_game_client, pinging) == 0x82A ? 1 : -1];
|
|
typedef char network_game_client_connection_offset_assert[
|
|
offsetof(struct network_game_client, connection) == 0x82C ? 1 : -1];
|
|
typedef char network_game_client_connect_process_offset_assert[
|
|
offsetof(struct network_game_client, connect_process) == 0x830 ? 1 : -1];
|
|
typedef char network_game_client_connection_attempt_time_offset_assert[
|
|
offsetof(struct network_game_client, connection_attempt_time) == 0x834 ? 1 : -1];
|
|
typedef char network_game_client_join_parameters_offset_assert[
|
|
offsetof(struct network_game_client, join_parameters) == 0x838 ? 1 : -1];
|
|
typedef char network_game_client_game_offset_assert[
|
|
offsetof(struct network_game_client, game) == 0x85C ? 1 : -1];
|
|
/* the fields after the game settings record move with its size */
|
|
#define NETWORK_GAME_CLIENT_TAIL_OFFSET (0x85C + HALO_PORT_NETWORK_GAME_SIZE)
|
|
typedef char network_game_client_join_in_progress_offset_assert[
|
|
offsetof(struct network_game_client, join_in_progress) == NETWORK_GAME_CLIENT_TAIL_OFFSET ? 1 : -1];
|
|
typedef char network_game_client_next_update_number_offset_assert[
|
|
offsetof(struct network_game_client, next_update_number) == NETWORK_GAME_CLIENT_TAIL_OFFSET + 8 ? 1 : -1];
|
|
typedef char network_game_client_last_broadcast_search_time_offset_assert[
|
|
offsetof(struct network_game_client, last_broadcast_search_time) == NETWORK_GAME_CLIENT_TAIL_OFFSET + 4 ? 1 : -1];
|
|
typedef char network_game_client_flags_offset_assert[
|
|
offsetof(struct network_game_client, flags) == NETWORK_GAME_CLIENT_TAIL_OFFSET + 0x16 ? 1 : -1];
|
|
typedef char network_game_client_last_precache_time_offset_assert[
|
|
offsetof(struct network_game_client, last_precache_time) == NETWORK_GAME_CLIENT_TAIL_OFFSET + 0xC ? 1 : -1];
|
|
typedef char message_client_broadcast_game_search_size_assert[
|
|
sizeof(struct message_client_broadcast_game_search) == 0xC ? 1 : -1];
|
|
typedef char message_client_ping_size_assert[
|
|
sizeof(struct message_client_ping) == 8 ? 1 : -1];
|
|
typedef char message_client_join_game_request_size_assert[
|
|
sizeof(struct message_client_join_game_request) == 0x70 ? 1 : -1];
|
|
typedef char network_game_client_seconds_to_game_start_offset_assert[
|
|
offsetof(struct network_game_client, seconds_to_game_start) == NETWORK_GAME_CLIENT_TAIL_OFFSET + 0x10 ? 1 : -1];
|
|
typedef char network_game_client_error_offset_assert[
|
|
offsetof(struct network_game_client, error) == NETWORK_GAME_CLIENT_TAIL_OFFSET + 0x14 ? 1 : -1];
|
|
typedef char network_game_client_connection_silent_offset_assert[
|
|
offsetof(struct network_game_client, connection_silent) == NETWORK_GAME_CLIENT_TAIL_OFFSET + 0x18 ? 1 : -1];
|
|
|
|
/* ---------- prototypes */
|
|
|
|
static boolean check_networking_and_generate_error(
|
|
void);
|
|
static void network_game_client_set_error(
|
|
struct network_game_client *client,
|
|
word error);
|
|
static boolean add_advertised_game(
|
|
struct network_advertised_game *available_games,
|
|
struct message_server_game_advertise *advertisement);
|
|
static boolean network_game_client_process_incoming_messages(
|
|
struct network_game_client *client);
|
|
static boolean network_game_client_process_last_messages(
|
|
struct network_game_client *client);
|
|
static void network_game_client_update_precache_status(
|
|
struct network_game_client *client);
|
|
static boolean network_game_client_map_name_is_valid(
|
|
char const *map_name,
|
|
long size);
|
|
static boolean network_game_client_idle_searching(
|
|
struct network_game_client *client);
|
|
static boolean network_game_client_idle_joining(
|
|
struct network_game_client *client);
|
|
static boolean network_game_client_idle_pregame(
|
|
struct network_game_client *client);
|
|
static boolean network_game_client_idle_ingame(
|
|
struct network_game_client *client);
|
|
static boolean network_game_client_idle_postgame(
|
|
struct network_game_client *client);
|
|
|
|
/* ---------- globals */
|
|
|
|
/* the host's game time when it told this client to start a game in
|
|
progress, 16 bits of it (0 for a game starting); and whether the first game
|
|
update is to bring the host's time (at every start) */
|
|
long network_game_client_late_join_time;
|
|
static boolean network_game_client_late_join_clock_pending;
|
|
|
|
/* whether the automated tests' join has told the player why a host cannot
|
|
be joined (network_game_client_join_first_available_game) */
|
|
static boolean network_game_client_incompatibility_told;
|
|
|
|
/* when each controller last asked the host for its player in the pregame
|
|
(network_game_client_add_player): the pregame screen asks every frame, and
|
|
is answered once every half second. A removal asked for since lets the next
|
|
ask go at once, so that a quick join again is not taken for a repeat. */
|
|
static unsigned long network_game_client_add_player_request_times[MAXIMUM_NUMBER_OF_LOCAL_PLAYERS];
|
|
static boolean network_game_client_add_player_requested[MAXIMUM_NUMBER_OF_LOCAL_PLAYERS];
|
|
|
|
/* since when the network has been down in a game (while
|
|
network_game_client_link_down is set): network_game_client_network_lost */
|
|
static unsigned long network_game_client_link_down_time;
|
|
static unsigned long network_game_client_link_checked_time;
|
|
static boolean network_game_client_link_down;
|
|
|
|
/* each advertised game's host's network version and netcode, by its place
|
|
in the client's available_games (HALO_PORT_NETWORK_VERSION) */
|
|
static struct
|
|
{
|
|
word version;
|
|
byte flags;
|
|
} network_game_client_advertised_versions[MAXIMUM_NETWORK_ADVERTISED_GAMES];
|
|
|
|
struct network_game_client network_game_client_dont_use_directly;
|
|
boolean allow_out_of_sync = FALSE;
|
|
boolean network_game_client_dont_use_directly_in_use = FALSE;
|
|
|
|
/* ---------- public code */
|
|
|
|
/* transport_network_available asks the system for its interfaces
|
|
(getifaddrs): asked at most once a second, not every frame */
|
|
static boolean network_game_client_network_available(
|
|
void)
|
|
{
|
|
static boolean asked = FALSE;
|
|
static boolean available;
|
|
static unsigned long ask_time;
|
|
unsigned long now = system_milliseconds();
|
|
|
|
if (!asked || now - ask_time >= 1000)
|
|
{
|
|
available = transport_network_available();
|
|
ask_time = now;
|
|
asked = TRUE;
|
|
}
|
|
|
|
return available;
|
|
}
|
|
|
|
/* port: whether the network has been down too long for a game to go on. In
|
|
a game, a network that goes down for a moment (the IPv4 address lost while
|
|
Wi-Fi roams or DHCP renews) does not end it at once: only one down for as
|
|
long as the host waits for a machine it hears nothing from
|
|
(NETWORK_GAME_CLIENT_LINK_DOWN_TIMEOUT). Out of a game it ends at once
|
|
(check_networking_and_generate_error). */
|
|
static boolean network_game_client_network_lost(
|
|
void)
|
|
{
|
|
unsigned long now = system_milliseconds();
|
|
boolean lost = FALSE;
|
|
|
|
/* (a time down from before the last check a moment ago is not counted:
|
|
the network was not asked in between) */
|
|
if (now - network_game_client_link_checked_time > 2000)
|
|
{
|
|
network_game_client_link_down = FALSE;
|
|
}
|
|
network_game_client_link_checked_time = now;
|
|
|
|
if (network_game_client_network_available())
|
|
{
|
|
if (network_game_client_link_down)
|
|
{
|
|
network_event(
|
|
"the network is back after %lu ms",
|
|
now - network_game_client_link_down_time);
|
|
network_game_client_link_down = FALSE;
|
|
}
|
|
}
|
|
else if (!network_game_client_link_down)
|
|
{
|
|
network_event("the network went down (in game); waiting for it to come back");
|
|
network_game_client_link_down = TRUE;
|
|
network_game_client_link_down_time = now;
|
|
}
|
|
else if (now - network_game_client_link_down_time >= NETWORK_GAME_CLIENT_LINK_DOWN_TIMEOUT)
|
|
{
|
|
lost = TRUE;
|
|
}
|
|
|
|
return lost;
|
|
}
|
|
|
|
static boolean check_networking_and_generate_error(
|
|
void)
|
|
{
|
|
boolean connected = TRUE;
|
|
|
|
if (!network_game_is_splitscreen_local())
|
|
{
|
|
connected = network_game_client_network_available();
|
|
|
|
if (!connected)
|
|
{
|
|
error(_error_silent, "network connection went down!");
|
|
display_error_when_main_menu_loaded(6);
|
|
}
|
|
}
|
|
|
|
return connected;
|
|
}
|
|
|
|
void network_game_client_dispose(
|
|
struct network_game_client *client)
|
|
{
|
|
if (client)
|
|
{
|
|
if (client->connection)
|
|
network_connection_delete(client->connection);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0xB2,
|
|
network_game_client_dont_use_directly_in_use);
|
|
|
|
network_game_client_dont_use_directly_in_use = FALSE;
|
|
}
|
|
|
|
network_event("network client disposed");
|
|
|
|
return;
|
|
}
|
|
|
|
long unstrip_player_index(
|
|
long player_index)
|
|
{
|
|
struct data_iterator iterator;
|
|
long result = NONE;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
|
|
while (data_iterator_next(&iterator))
|
|
{
|
|
if (DATUM_INDEX_TO_ABSOLUTE_INDEX(iterator.datum_index) ==
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(player_index))
|
|
{
|
|
result = iterator.datum_index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
short network_game_client_get_state(
|
|
struct network_game_client *client,
|
|
short *state_data)
|
|
{
|
|
unsigned long elapsed_time;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0xF9,
|
|
client);
|
|
|
|
if (state_data)
|
|
{
|
|
*state_data = 0;
|
|
|
|
if (client->state == _network_game_client_state_joining)
|
|
{
|
|
elapsed_time = system_milliseconds() * 100 -
|
|
client->connection_attempt_time * 100;
|
|
*state_data = (short)(elapsed_time / 120000);
|
|
}
|
|
}
|
|
|
|
return client->state;
|
|
}
|
|
|
|
boolean network_game_client_set_machine(
|
|
struct network_game_client *client,
|
|
struct network_machine *machine)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x1E1,
|
|
client && (client->machine_index<MAXIMUM_NETWORK_MACHINE_COUNT) && network_machine_is_valid(machine));
|
|
|
|
/* port: the assert is not checked in release builds */
|
|
if (client->machine_index >= MAXIMUM_NETWORK_MACHINE_COUNT)
|
|
return FALSE;
|
|
csmemcpy(
|
|
&client->game.machines[client->machine_index],
|
|
machine,
|
|
sizeof(*machine));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void network_game_client_switch_to_postgame(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x48C,
|
|
client);
|
|
|
|
game_engine_switch_to_postgame();
|
|
client->state = _network_game_client_state_postgame;
|
|
network_event("switching to postgame");
|
|
|
|
return;
|
|
}
|
|
|
|
boolean network_game_client_switch_to_pregame(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x499,
|
|
client);
|
|
|
|
if (client->state != _network_game_client_state_pregame)
|
|
{
|
|
network_game_reset_for_next_round(&client->game, TRUE);
|
|
network_connection_keep_alive(client->connection);
|
|
client->next_update_number = 0;
|
|
client->join_in_progress = TRUE;
|
|
client->connection_silent = FALSE;
|
|
client->state = _network_game_client_state_pregame;
|
|
network_game_client_late_join_time = 0;
|
|
network_game_client_late_join_clock_pending = FALSE;
|
|
network_event("switching to pregame");
|
|
network_game_reset_to_pregame_ui();
|
|
network_connection_keep_alive(client->connection);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void network_game_client_countdown_timer_update(
|
|
struct network_game_client *client,
|
|
short seconds_to_game_start)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x5C3,
|
|
client);
|
|
|
|
client->seconds_to_game_start = seconds_to_game_start;
|
|
|
|
return;
|
|
}
|
|
|
|
boolean network_game_client_advertised_game_is_valid(
|
|
struct network_advertised_game *advertised_game)
|
|
{
|
|
boolean valid = TRUE;
|
|
|
|
if (!advertised_game->valid ||
|
|
(long)(system_milliseconds() - advertised_game->update_time) > 6000)
|
|
{
|
|
valid = FALSE;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
void network_game_client_game_shutdown(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x3FC,
|
|
client);
|
|
|
|
network_game_client_set_error(
|
|
client,
|
|
_network_game_client_error_server_shutdown);
|
|
|
|
network_event("the game host is shutting down");
|
|
network_game_client_all_local_players_have_quit();
|
|
|
|
return;
|
|
}
|
|
|
|
struct network_machine *network_game_client_get_machine(
|
|
struct network_game_client *client)
|
|
{
|
|
if (client && client->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT)
|
|
return &client->game.machines[client->machine_index];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
short network_game_client_get_machine_index(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x1FD,
|
|
client);
|
|
|
|
return client->machine_index;
|
|
}
|
|
|
|
struct network_advertised_game *network_game_client_get_available_games(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x2AC,
|
|
client);
|
|
|
|
return client->available_games;
|
|
}
|
|
|
|
short network_game_client_get_error(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x2B4,
|
|
client);
|
|
|
|
return client->error;
|
|
}
|
|
|
|
short network_game_client_get_seconds_to_game_start(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x2BC,
|
|
client);
|
|
|
|
return client->seconds_to_game_start;
|
|
}
|
|
|
|
void network_game_client_keep_alive(
|
|
struct network_game_client *client)
|
|
{
|
|
network_connection_keep_alive(client->connection);
|
|
|
|
return;
|
|
}
|
|
|
|
struct network_connection *network_game_client_get_connection(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x4B3,
|
|
client);
|
|
|
|
return client->connection;
|
|
}
|
|
|
|
void network_game_client_get_remote_server_address(
|
|
struct network_game_client *client,
|
|
struct transport_address *address)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x4BC,
|
|
client);
|
|
|
|
network_connection_get_address(client->connection, address, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
struct network_game *network_game_client_get_game(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x4CD,
|
|
client);
|
|
|
|
return &client->game;
|
|
}
|
|
|
|
boolean network_game_client_server_has_started_game(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x4D5,
|
|
client);
|
|
|
|
return client->next_update_number > 0;
|
|
}
|
|
|
|
long network_game_client_get_next_update_number(
|
|
struct network_game_client *client)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x4DD,
|
|
client);
|
|
|
|
return client->next_update_number;
|
|
}
|
|
|
|
boolean network_game_client_write(
|
|
struct network_connection *connection,
|
|
message_header *message,
|
|
word message_size,
|
|
struct transport_address *address,
|
|
boolean reliable)
|
|
{
|
|
return network_connection_write(
|
|
connection,
|
|
message,
|
|
message_size,
|
|
address,
|
|
reliable);
|
|
}
|
|
|
|
boolean network_game_client_idle(
|
|
struct network_game_client *client)
|
|
{
|
|
boolean success = FALSE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0xC6,
|
|
client);
|
|
|
|
switch (client->state)
|
|
{
|
|
case _network_game_client_state_searching:
|
|
if (!(success = network_game_client_idle_searching(client)))
|
|
network_event("network_game_client_idle_searching() failed");
|
|
break;
|
|
|
|
case _network_game_client_state_joining:
|
|
if (!(success = network_game_client_idle_joining(client)))
|
|
network_event("network_game_client_idle_joining() failed");
|
|
break;
|
|
|
|
case _network_game_client_state_pregame:
|
|
if (!(success = network_game_client_idle_pregame(client)))
|
|
network_event("network_game_client_idle_pregame() failed");
|
|
break;
|
|
|
|
case _network_game_client_state_ingame:
|
|
if (!(success = network_game_client_idle_ingame(client)))
|
|
network_event("network_game_client_idle_ingame() failed");
|
|
break;
|
|
|
|
case _network_game_client_state_postgame:
|
|
if (!(success = network_game_client_idle_postgame(client)))
|
|
network_event("network_game_client_idle_postgame() failed");
|
|
break;
|
|
|
|
default:
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0xEE,
|
|
!"unknown client state");
|
|
break;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
boolean network_game_client_game_settings_updated(
|
|
struct network_game_client *client,
|
|
struct network_game *message_packet)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x375,
|
|
client && message_packet);
|
|
|
|
if (message_packet->machine_count >= 0 &&
|
|
message_packet->machine_count <= MAXIMUM_NETWORK_MACHINE_COUNT &&
|
|
message_packet->player_count >= 0 &&
|
|
message_packet->player_count <= MAXIMUM_NUMBER_OF_PLAYERS &&
|
|
network_game_client_map_name_is_valid(message_packet->map.name, sizeof(message_packet->map.name)) &&
|
|
VALID_INDEX(message_packet->difficulty, NUMBER_OF_GAME_DIFFICULTY_LEVELS))
|
|
{
|
|
struct network_game previous_game;
|
|
|
|
if (csstrcmp(message_packet->map.name, client->game.map.name))
|
|
{
|
|
char build[0x20];
|
|
|
|
/* port: a map of a build this version does not play with others
|
|
(its objects would not be the host's): said, and the game left */
|
|
if (!network_game_is_splitscreen_local() &&
|
|
!cache_files_map_plays_multiplayer(message_packet->map.name, build))
|
|
{
|
|
cache_files_show_multiplayer_unavailable(message_packet->map.name, build);
|
|
/* (the menu's error the join's, not the connection lost that
|
|
the failure would otherwise give) */
|
|
display_error_when_main_menu_loaded(_error_network_failed_to_join_game);
|
|
return FALSE;
|
|
}
|
|
network_event("precaching map '%s'...", message_packet->map.name);
|
|
main_set_multiplayer_map_name(message_packet->map.name);
|
|
}
|
|
|
|
csmemcpy(&previous_game, &client->game, sizeof(client->game));
|
|
csmemcpy(&client->game, message_packet, sizeof(client->game));
|
|
csmemcpy(
|
|
&client->game.local_data,
|
|
&previous_game.local_data,
|
|
sizeof(client->game.local_data));
|
|
|
|
network_event(
|
|
"received updated game settings from the server; there are %d players on %d machines in the game",
|
|
message_packet->player_count,
|
|
message_packet->machine_count);
|
|
network_event(
|
|
"player count %d machine count %d",
|
|
message_packet->player_count,
|
|
message_packet->machine_count);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
network_event(
|
|
"invalid message_server_game_settings_update message received player count %d machine count %d difficulty %d",
|
|
message_packet->player_count,
|
|
message_packet->machine_count,
|
|
message_packet->difficulty);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void network_game_client_new_advertised_game(
|
|
struct network_game_client *client,
|
|
struct message_server_game_advertise *message_packet)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x2FC,
|
|
client && message_packet);
|
|
|
|
add_advertised_game(client->available_games, message_packet);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean network_game_client_request_remove_player(
|
|
struct network_game_client *client,
|
|
struct network_player *player)
|
|
{
|
|
boolean success = TRUE;
|
|
struct message_client_remove_player_request remove_player_request;
|
|
message_header *message;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x208,
|
|
client && network_player_is_valid(player));
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x209,
|
|
client->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT &&
|
|
client->game.machines[client->machine_index].machine_index ==
|
|
player->machine_index,
|
|
"client's can only remove players from their own machines");
|
|
|
|
network_event(
|
|
"requesting a player removal (controller index #%d)",
|
|
player->controller_index);
|
|
|
|
/* (the controller's next join goes at once: network_game_client_add_player) */
|
|
if (player->controller_index >= 0 &&
|
|
player->controller_index < MAXIMUM_NUMBER_OF_LOCAL_PLAYERS)
|
|
{
|
|
network_game_client_add_player_requested[player->controller_index] = FALSE;
|
|
}
|
|
|
|
switch (client->state)
|
|
{
|
|
case _network_game_client_state_searching:
|
|
case _network_game_client_state_joining:
|
|
network_event("can't remove players from a game until after a game is joined");
|
|
success = FALSE;
|
|
break;
|
|
|
|
case _network_game_client_state_pregame:
|
|
csmemcpy(&remove_player_request, player, sizeof(remove_player_request));
|
|
|
|
message = create_network_game_message(
|
|
_message_client_remove_player_request_pregame,
|
|
&remove_player_request,
|
|
sizeof(remove_player_request));
|
|
if (message)
|
|
{
|
|
success = network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1);
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_remove_player_request_pregame mesage");
|
|
success = FALSE;
|
|
}
|
|
break;
|
|
|
|
case _network_game_client_state_ingame:
|
|
csmemcpy(&remove_player_request, player, sizeof(remove_player_request));
|
|
|
|
message = create_network_game_message(
|
|
_message_client_remove_player_request_ingame,
|
|
&remove_player_request,
|
|
sizeof(remove_player_request));
|
|
if (message)
|
|
{
|
|
success = network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1);
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_remove_player_request_ingame message");
|
|
success = FALSE;
|
|
}
|
|
break;
|
|
|
|
case _network_game_client_state_postgame:
|
|
csmemcpy(&remove_player_request, player, sizeof(remove_player_request));
|
|
|
|
message = create_network_game_message(
|
|
_message_client_remove_player_request_postgame,
|
|
&remove_player_request,
|
|
sizeof(remove_player_request));
|
|
if (message)
|
|
{
|
|
if (!(success = network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1)))
|
|
{
|
|
network_event("network_game_client_write() failed while sending a message_client_remove_player_request_postgame message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_remove_player_request_postgame message");
|
|
success = FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
network_event("client is in an unknown state");
|
|
break;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
boolean network_game_client_add_player(
|
|
struct network_game_client *client,
|
|
short local_player_index)
|
|
{
|
|
boolean success = TRUE;
|
|
struct network_player player;
|
|
struct message_client_add_player_request add_player_request;
|
|
struct player_profile profile;
|
|
message_header *message;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x530,
|
|
client && (local_player_index>=0) && (local_player_index<MAXIMUM_NUMBER_OF_LOCAL_PLAYERS));
|
|
|
|
/* port: the pregame screen asks each frame until the host's settings
|
|
have the player: once every half second, not each frame (the host
|
|
refused and logged each repeat) */
|
|
if (client->state == _network_game_client_state_pregame)
|
|
{
|
|
unsigned long now = system_milliseconds();
|
|
|
|
if (network_game_client_add_player_requested[local_player_index] &&
|
|
now - network_game_client_add_player_request_times[local_player_index] < 500)
|
|
{
|
|
return TRUE;
|
|
}
|
|
network_game_client_add_player_requested[local_player_index] = TRUE;
|
|
network_game_client_add_player_request_times[local_player_index] = now;
|
|
}
|
|
|
|
player_ui_get_active_player_profile(local_player_index, &profile);
|
|
|
|
player.controller_index = (char)local_player_index;
|
|
player.machine_index = (char)client->machine_index;
|
|
ustrncpy(player.name, profile.player_name, NETWORK_PLAYER_NAME_LENGTH - 1);
|
|
player.name[NETWORK_PLAYER_NAME_LENGTH - 1] = 0;
|
|
player.primary_color_index = profile.primary_color_index;
|
|
player.icon_index = NONE;
|
|
player.team_index = NONE;
|
|
player.player_list_index = NONE;
|
|
|
|
network_event(
|
|
"requesting a player addition (controller index #%d)",
|
|
player.controller_index);
|
|
|
|
switch (client->state)
|
|
{
|
|
case _network_game_client_state_searching:
|
|
case _network_game_client_state_joining:
|
|
network_event("can't add players to a game until after a game is joined");
|
|
success = FALSE;
|
|
break;
|
|
|
|
case _network_game_client_state_pregame:
|
|
csmemcpy(&add_player_request, &player, sizeof(add_player_request));
|
|
|
|
message = create_network_game_message(
|
|
_message_client_add_player_request_pregame,
|
|
&add_player_request,
|
|
sizeof(add_player_request));
|
|
if (message)
|
|
{
|
|
if (!(success = network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1)))
|
|
{
|
|
network_event("network_game_client_write() failed while sending a message_client_add_player_request_pregame message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_add_player_request_pregame message");
|
|
}
|
|
break;
|
|
|
|
case _network_game_client_state_ingame:
|
|
csmemcpy(&add_player_request, &player, sizeof(add_player_request));
|
|
|
|
message = create_network_game_message(
|
|
_message_client_add_player_request_ingame,
|
|
&add_player_request,
|
|
sizeof(add_player_request));
|
|
if (message)
|
|
{
|
|
if (!(success = network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1)))
|
|
{
|
|
network_event("network_game_client_write() failed while sending a message_client_add_player_request_ingame message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_add_player_request_ingame message");
|
|
}
|
|
break;
|
|
|
|
case _network_game_client_state_postgame:
|
|
network_event("client tried to add a new player in post-game");
|
|
success = FALSE;
|
|
break;
|
|
|
|
default:
|
|
network_event("client is in an unknown state");
|
|
break;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
boolean network_game_client_handle_game_update(
|
|
struct network_game_client *client,
|
|
struct message_server_game_update *message_packet)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x40D,
|
|
client && message_packet);
|
|
|
|
/* (every machine ticks on its own clock with the inputs the host relays,
|
|
network_distributed.c: the host's game update carries no actions, and
|
|
keeps only the count of updates, which a machine that joined the game in
|
|
progress takes up where it is) */
|
|
client->next_update_number = message_packet->update_number + 1;
|
|
/* (the host's time at the start, and a game in progress's past 16 bits
|
|
of ticks: the host's whole time, if it is ahead; never back, which the
|
|
host would take for old messages) */
|
|
if (network_game_client_late_join_clock_pending)
|
|
{
|
|
network_game_client_late_join_clock_pending = FALSE;
|
|
if (message_packet->game_time > game_time_get())
|
|
{
|
|
game_time_set_distributed(message_packet->game_time);
|
|
network_event("the game in progress is at game tick #%ld", message_packet->game_time);
|
|
}
|
|
}
|
|
/* port: and whenever the host's time is more than a second ahead: the
|
|
clock catches up no more than a second (game_time_update), so a stall
|
|
longer than that (the app in the background, a hitch) left this machine
|
|
behind the host for the rest of the game. The host's time is when it
|
|
sent the update, before this machine's by the way there, so a clock in
|
|
step is never moved; one behind is, at the next update (once a second at
|
|
least) */
|
|
else if (message_packet->game_time > game_time_get() + TICKS_PER_SECOND)
|
|
{
|
|
network_event(
|
|
"the host's clock is %ld ticks ahead of this machine's; taking its game tick #%ld",
|
|
message_packet->game_time - game_time_get(),
|
|
message_packet->game_time);
|
|
game_time_set_distributed(message_packet->game_time);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
boolean network_game_client_game_has_started(
|
|
struct network_game_client *client)
|
|
{
|
|
/* (the host's time came with the start: the loading below is counted) */
|
|
unsigned long loading_started = system_milliseconds();
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x3B0,
|
|
client && (client->state == _network_game_client_state_pregame));
|
|
|
|
client->seconds_to_game_start = NONE;
|
|
network_connection_keep_alive(client->connection);
|
|
|
|
if (network_game_create_game_objects(&client->game))
|
|
{
|
|
long network_player_index;
|
|
|
|
/* (this machine's players need not be in adjacent slots) */
|
|
for (network_player_index = 0;
|
|
network_player_index < MAXIMUM_NUMBER_OF_PLAYERS;
|
|
network_player_index++)
|
|
{
|
|
struct network_player *player = &client->game.players[network_player_index];
|
|
|
|
if (network_player_is_valid(player) &&
|
|
player->machine_index == client->machine_index)
|
|
{
|
|
local_player_set_player_index(
|
|
player->controller_index,
|
|
unstrip_player_index(player->player_list_index));
|
|
}
|
|
}
|
|
|
|
network_connection_keep_alive(client->connection);
|
|
|
|
{
|
|
struct message_client_loaded loaded = {0};
|
|
message_header *message = create_network_game_message(
|
|
_message_client_loaded,
|
|
&loaded,
|
|
sizeof(loaded));
|
|
|
|
if (message)
|
|
{
|
|
if (network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1))
|
|
{
|
|
network_event("local machine is loaded & ready to play");
|
|
|
|
client->state = _network_game_client_state_ingame;
|
|
client->next_update_number = 0;
|
|
client->connection_silent = FALSE;
|
|
|
|
ui_widgets_close_all();
|
|
game_time_start();
|
|
/* (a game in progress: the host's time when it said to start,
|
|
and the ticks this machine spent loading it) */
|
|
if (network_game_client_late_join_time > 0)
|
|
{
|
|
unsigned long elapsed = system_milliseconds() - loading_started;
|
|
/* (no longer than a minute: a stalled clock counts nothing) */
|
|
long ticks = elapsed < 60000UL ? (long)(elapsed * TICKS_PER_SECOND / 1000) : 0;
|
|
|
|
game_time_set_distributed(network_game_client_late_join_time + ticks);
|
|
network_event("joined the game in progress at game tick #%ld",
|
|
network_game_client_late_join_time + ticks);
|
|
}
|
|
network_game_client_late_join_time = 0;
|
|
/* (the clock waits for the host's first game update, which
|
|
brings the host's time, game_time_update: at a start the host
|
|
ticks only once every machine has loaded) */
|
|
network_game_client_late_join_clock_pending =
|
|
game_connection() == _game_connection_network_client;
|
|
game_initial_pulse();
|
|
}
|
|
else
|
|
{
|
|
network_event("network_game_client_write() failed while sending a message_client_loaded message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_loaded message");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to load the necessary game data");
|
|
}
|
|
|
|
return client->state == _network_game_client_state_ingame;
|
|
}
|
|
|
|
boolean network_game_client_remove_player(
|
|
struct network_game_client *client,
|
|
struct network_player *player,
|
|
long reason)
|
|
{
|
|
boolean success = FALSE;
|
|
long player_index = NONE;
|
|
long network_player_index;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x273,
|
|
client && player);
|
|
|
|
for (network_player_index = 0;
|
|
network_player_index < MAXIMUM_NUMBER_OF_PLAYERS;
|
|
network_player_index++)
|
|
{
|
|
if (network_player_is_valid(&client->game.players[network_player_index]) &&
|
|
client->game.players[network_player_index].machine_index == player->machine_index &&
|
|
client->game.players[network_player_index].controller_index == player->controller_index)
|
|
{
|
|
player_index = unstrip_player_index(
|
|
client->game.players[network_player_index].player_list_index);
|
|
success = network_game_remove_player(&client->game, player);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (success && client->game.local_data.game_objects_loaded)
|
|
{
|
|
if (player_index && player_index != NONE)
|
|
{
|
|
struct player_datum *player_datum = player_get(player_index);
|
|
|
|
if (reason != NONE)
|
|
{
|
|
/* (a quit time this clock has passed would never be reached,
|
|
game.c) */
|
|
reason = MAX(reason, game_time_get() + 1);
|
|
error(
|
|
_error_silent,
|
|
"%x quit of of game at tick %d (now %d)",
|
|
player_index,
|
|
reason,
|
|
game_time_get());
|
|
player_datum->quit_out_of_game_time = reason;
|
|
/* (and is no longer its machine's: a machine that joins the
|
|
game in progress at its index fills the list afresh,
|
|
players.c) */
|
|
machine_remove_player(player_index);
|
|
}
|
|
|
|
for (network_player_index = 0;
|
|
network_player_index < MAXIMUM_NUMBER_OF_PLAYERS;
|
|
network_player_index++)
|
|
{
|
|
if (network_player_is_valid(&client->game.players[network_player_index]) &&
|
|
client->game.players[network_player_index].machine_index == client->machine_index)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (network_player_index == MAXIMUM_NUMBER_OF_PLAYERS)
|
|
{
|
|
network_game_client_all_local_players_have_quit();
|
|
network_event("no local players remain in the game, exiting the game now");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"network game tried to delete a player with a phony player index (#0x%08lX)",
|
|
player_index);
|
|
success = FALSE;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void network_game_client_accepted_into_game(
|
|
struct network_game_client *client,
|
|
struct transport_address *source_address,
|
|
struct message_server_machine_accepted *message_packet)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x327,
|
|
client && source_address && message_packet && (client->state == _network_game_client_state_joining));
|
|
|
|
if (message_packet->machine_index >= 0 &&
|
|
message_packet->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT)
|
|
{
|
|
struct message_client_settings_request settings_request;
|
|
message_header *message;
|
|
|
|
client->machine_index = message_packet->machine_index;
|
|
client->game.machines[message_packet->machine_index].machine_index =
|
|
(char)message_packet->machine_index;
|
|
client->state = _network_game_client_state_pregame;
|
|
|
|
network_game_set_random_seed(message_packet->random_seed);
|
|
network_event(
|
|
"successfully joined a net game; our machine is #%d",
|
|
message_packet->machine_index);
|
|
|
|
network_game_generate_local_machine_name(settings_request.machine_name);
|
|
settings_request.machine_index = (char)message_packet->machine_index;
|
|
|
|
message = create_network_game_message(
|
|
_message_client_settings_request,
|
|
&settings_request,
|
|
sizeof(settings_request));
|
|
if (message)
|
|
{
|
|
if (!network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1))
|
|
{
|
|
network_event("network_game_client_write() failed while sending a message_client_settings_request message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_settings_request message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("received a message_server_machine_accepted message with a bad machine_index");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean network_game_client_update_local_player_data(
|
|
struct network_game_client *client,
|
|
struct network_player *player)
|
|
{
|
|
boolean success = FALSE;
|
|
struct network_player player_settings;
|
|
message_header *message;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x587,
|
|
client && player);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x588,
|
|
player->machine_index==client->machine_index);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x589,
|
|
network_player_is_valid(player));
|
|
|
|
csmemcpy(&player_settings, player, sizeof(player_settings));
|
|
|
|
if (player_settings.team_index == NONE)
|
|
player_settings.team_index = 0;
|
|
|
|
message = create_network_game_message(
|
|
_message_client_player_settings_request,
|
|
&player_settings,
|
|
sizeof(player_settings));
|
|
if (message)
|
|
{
|
|
if (network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1))
|
|
{
|
|
success = TRUE;
|
|
}
|
|
else
|
|
{
|
|
network_event("network_game_client_update_local_player_data() failed while sending a message_client_player_settings_request message");
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
boolean network_game_client_add_player_to_game(
|
|
struct network_game_client *client,
|
|
struct network_player *player)
|
|
{
|
|
boolean success = FALSE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x462,
|
|
client && player);
|
|
|
|
if (network_player_is_valid(player))
|
|
{
|
|
success = network_game_add_player(&client->game, player);
|
|
|
|
if (success)
|
|
{
|
|
if (client->state == _network_game_client_state_ingame)
|
|
{
|
|
/* port: the slot it went in, which in the distributed netcode's
|
|
games need not be the last (network_game_add_player) */
|
|
{
|
|
struct network_player const *added = player;
|
|
long slot;
|
|
|
|
player = &client->game.players[client->game.player_count - 1];
|
|
for (slot = 0; slot < MAXIMUM_NUMBER_OF_PLAYERS; slot++)
|
|
{
|
|
if (network_player_is_valid(&client->game.players[slot]) &&
|
|
client->game.players[slot].machine_index == added->machine_index &&
|
|
client->game.players[slot].controller_index == added->controller_index)
|
|
{
|
|
player = &client->game.players[slot];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
success = network_game_spawn_player(player);
|
|
|
|
if (success)
|
|
{
|
|
long player_index = unstrip_player_index(player->player_list_index);
|
|
|
|
/* port: a player added to the game in progress gets its team
|
|
and the game type's data, as the players at the start have
|
|
(game_initial_pulse); otherwise it keeps player_new's team 1,
|
|
in free for all the team of the player in slot 1, whose kills
|
|
of it and its of them count as betrayals */
|
|
{
|
|
extern void game_engine_player_added(long player_index);
|
|
|
|
game_engine_player_added(player_index);
|
|
}
|
|
if (player->machine_index == client->machine_index)
|
|
{
|
|
local_player_set_player_index(
|
|
player->controller_index,
|
|
player_index);
|
|
}
|
|
|
|
update_client_add_player(player_index);
|
|
|
|
if (global_network_game_server_get())
|
|
update_server_add_player(player_index);
|
|
}
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
network_event(
|
|
"added new player to the game (machine #%d / controller #%d)",
|
|
player->machine_index,
|
|
player->controller_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
boolean network_game_client_initiate_join_game(
|
|
struct network_game_client *client,
|
|
struct network_advertised_game *game,
|
|
struct network_join_parameters *join_parameters,
|
|
struct transport_address *address)
|
|
{
|
|
boolean success;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x157,
|
|
client && (client->state == _network_game_client_state_searching) && game && join_parameters && client->connection && !network_connection_connected(client->connection) && (game->platform == network_game_get_local_platform()));
|
|
|
|
client->join_in_progress = TRUE;
|
|
client->connect_process = 0;
|
|
client->connection_attempt_time = system_milliseconds();
|
|
|
|
csmemcpy(
|
|
&client->join_parameters,
|
|
join_parameters,
|
|
sizeof(*join_parameters));
|
|
|
|
success = network_connection_connect(client->connection, address, 0);
|
|
|
|
if (success == TRUE)
|
|
{
|
|
/* port: the join's wait counted from the connection made, not from
|
|
before a connect that took seconds (idle_joining) */
|
|
network_connection_keep_alive(client->connection);
|
|
client->state = _network_game_client_state_joining;
|
|
network_event(
|
|
"attempting to connect to game @ %s",
|
|
transport_address_to_string(address));
|
|
}
|
|
else
|
|
{
|
|
client->join_in_progress = FALSE;
|
|
display_error_when_main_menu_loaded(7);
|
|
network_event(
|
|
"failed attempt to initiate a connection to game @ %s",
|
|
transport_address_to_string(address));
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void network_game_client_ponged(
|
|
struct network_game_client *client,
|
|
struct transport_address *source_address,
|
|
long timestamp)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x307,
|
|
client && source_address);
|
|
|
|
if (client->pinging &&
|
|
client->ping_address.address.long_words[0] ==
|
|
source_address->address.long_words[0])
|
|
{
|
|
unsigned long now = system_milliseconds();
|
|
|
|
if ((long)(now - (unsigned long)timestamp) >= 0)
|
|
{
|
|
client->average_ping = (word)((client->average_ping *
|
|
client->ping_sample_count + now - timestamp) /
|
|
(client->ping_sample_count + 1));
|
|
client->ping_sample_count += 1;
|
|
}
|
|
else
|
|
{
|
|
network_event("received a pong from the future");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("received a pong from a system we aren't interested in");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean network_game_client_address_matches_server(
|
|
struct network_game_client *client,
|
|
struct transport_address *address)
|
|
{
|
|
struct transport_address server_address;
|
|
struct transport_address server_unreliable_address;
|
|
boolean address_matches;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x2D2,
|
|
client != NULL);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x2D3,
|
|
client->connection);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x2D4,
|
|
address != NULL);
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x2D5,
|
|
address->address.long_words[0],
|
|
"address->address.ipv4_address");
|
|
|
|
network_connection_get_address(client->connection, &server_address, &server_unreliable_address);
|
|
|
|
/* port: the host's stream's peer, or its datagrams' (internet play's
|
|
tunnel has them at different local addresses: the client's datagram
|
|
socket is connected to the host, so what comes to it is the host's) */
|
|
address_matches = server_address.address.long_words[0] == address->address.long_words[0] ||
|
|
(server_unreliable_address.address.long_words[0] &&
|
|
server_unreliable_address.address.long_words[0] == address->address.long_words[0]);
|
|
|
|
return address_matches;
|
|
}
|
|
|
|
boolean network_game_client_request_start_time_change(
|
|
struct network_game_client *client,
|
|
short request_type)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x5A5,
|
|
client);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x5A6,
|
|
(request_type>=0) && (request_type<NUMBER_OF_GAME_START_REQUESTS));
|
|
|
|
if (client->state == _network_game_client_state_pregame)
|
|
{
|
|
struct message_client_game_start_request game_start_request;
|
|
message_header *message;
|
|
|
|
game_start_request.request_type = request_type;
|
|
|
|
message = create_network_game_message(
|
|
_message_client_game_start_request,
|
|
&game_start_request,
|
|
sizeof(game_start_request));
|
|
if (message)
|
|
{
|
|
if (!network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1))
|
|
{
|
|
network_event("network_game_client_request_start_time_change() failed to send a message_client_game_start_request message");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to send a message_client_game_start_request because we are not in the pregame state");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void network_game_client_reset(
|
|
struct network_game_client *client,
|
|
boolean teardown_connection)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x4EE,
|
|
client);
|
|
|
|
network_game_invalidate(&client->game);
|
|
|
|
client->machine_index = NONE;
|
|
client->state = _network_game_client_state_searching;
|
|
|
|
/* (port: or one that was lost, whose socket stays until it is torn down) */
|
|
if (teardown_connection && client->connection &&
|
|
((boolean)network_connection_connected(client->connection) ||
|
|
!network_connection_active(client->connection)))
|
|
{
|
|
if (network_connection_disconnect(client->connection))
|
|
{
|
|
SET_FLAG(
|
|
client->flags,
|
|
_network_game_client_connection_established_bit,
|
|
FALSE);
|
|
}
|
|
else
|
|
{
|
|
network_game_client_set_error(
|
|
client,
|
|
_network_game_client_error_unknown);
|
|
network_event("failed to reinitialize network game client");
|
|
}
|
|
}
|
|
|
|
SET_FLAG(client->flags, _network_game_client_join_request_sent_bit, FALSE);
|
|
client->error = _network_game_client_error_none;
|
|
client->join_in_progress = FALSE;
|
|
client->last_broadcast_search_time = 0;
|
|
client->next_update_number = 0;
|
|
client->connection_silent = FALSE;
|
|
client->seconds_to_game_start = NONE;
|
|
network_game_client_late_join_time = 0;
|
|
network_game_client_late_join_clock_pending = FALSE;
|
|
network_game_client_incompatibility_told = FALSE;
|
|
csmemset(network_game_client_add_player_requested, 0, sizeof(network_game_client_add_player_requested));
|
|
|
|
return;
|
|
}
|
|
|
|
struct network_game_client *network_game_client_create(
|
|
void)
|
|
{
|
|
struct network_game_client *client = &network_game_client_dont_use_directly;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x94,
|
|
!network_game_client_dont_use_directly_in_use);
|
|
|
|
network_game_client_dont_use_directly_in_use = TRUE;
|
|
|
|
csmemset(
|
|
&network_game_client_dont_use_directly,
|
|
0,
|
|
sizeof(network_game_client_dont_use_directly));
|
|
|
|
network_game_client_dont_use_directly.connection = network_connection_new(
|
|
_network_connection_type_client,
|
|
NETWORK_GAME_CLIENT_PORT);
|
|
|
|
if (network_game_client_dont_use_directly.connection)
|
|
{
|
|
network_game_client_reset(&network_game_client_dont_use_directly, FALSE);
|
|
}
|
|
else
|
|
{
|
|
network_event("network_game_create_client() failed; could not create network connection");
|
|
network_game_client_dispose(&network_game_client_dont_use_directly);
|
|
client = NULL;
|
|
}
|
|
|
|
return client;
|
|
}
|
|
|
|
void network_game_client_rejected_by_game(
|
|
struct network_game_client *client,
|
|
struct transport_address *source_address,
|
|
word rejection_code)
|
|
{
|
|
const char *reason = "<unknown>";
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x35A,
|
|
client && source_address);
|
|
|
|
client->state = _network_game_client_state_searching;
|
|
|
|
switch (rejection_code)
|
|
{
|
|
case _rejection_code_version_too_old:
|
|
reason = "_rejection_code_version_too_old";
|
|
break;
|
|
|
|
case _rejection_code_version_too_new:
|
|
reason = "_rejection_code_version_too_new";
|
|
break;
|
|
|
|
case _rejection_code_bad_join_token:
|
|
reason = "_rejection_code_bad_join_token";
|
|
break;
|
|
|
|
case _rejection_code_bad_password:
|
|
reason = "_rejection_code_bad_password";
|
|
break;
|
|
|
|
case _rejection_code_game_is_full:
|
|
reason = "_rejection_code_game_is_full";
|
|
break;
|
|
|
|
case _rejection_code_game_is_closed:
|
|
reason = "_rejection_code_game_is_closed";
|
|
break;
|
|
|
|
case _rejection_code_blacklisted_machine:
|
|
reason = "_rejection_code_blacklisted_machine";
|
|
break;
|
|
}
|
|
|
|
network_event(
|
|
"unable to join game: reason= #%d/%s",
|
|
rejection_code,
|
|
reason);
|
|
/* port: the join went to the pregame screen at once (and a machine
|
|
refused a game in progress, network_game_server_refuse_late_joiner, is in
|
|
it): with no game behind it, it is left for the main menu, which says
|
|
why, rather than sitting in a lobby that will never start */
|
|
display_error_when_main_menu_loaded(
|
|
rejection_code == _rejection_code_game_is_full || rejection_code == _rejection_code_game_is_closed ?
|
|
_error_network_join_game_closed :
|
|
_error_network_failed_to_join_game);
|
|
network_game_abort();
|
|
network_game_client_reset(client, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
/* a map name from the host ends within its field and names a map in the maps
|
|
folder: it goes into the map's path (cache_files_windows.c) */
|
|
static boolean network_game_client_map_name_is_valid(
|
|
char const *map_name,
|
|
long size)
|
|
{
|
|
/* (a scenario's tag path, of which the cache takes the name after the
|
|
last backslash) */
|
|
return memchr(map_name, '\0', size) != NULL &&
|
|
!strchr(map_name, '/') &&
|
|
!strstr(map_name, "..");
|
|
}
|
|
|
|
static boolean add_advertised_game(
|
|
struct network_advertised_game *available_games,
|
|
struct message_server_game_advertise *advertisement)
|
|
{
|
|
boolean success = FALSE;
|
|
boolean open = TEST_FLAG(advertisement->flags, _game_advertisement_open_bit) &&
|
|
advertisement->machine_count < MAXIMUM_NETWORK_MACHINE_COUNT;
|
|
struct network_advertised_game *advertised_game = NULL;
|
|
long game_index;
|
|
|
|
for (game_index = 0; game_index < MAXIMUM_NETWORK_ADVERTISED_GAMES; game_index++)
|
|
{
|
|
struct network_advertised_game *current = available_games + game_index;
|
|
|
|
if (!network_game_client_advertised_game_is_valid(current))
|
|
{
|
|
csmemset(current, 0, sizeof(*current));
|
|
}
|
|
}
|
|
|
|
for (game_index = 0; game_index < MAXIMUM_NETWORK_ADVERTISED_GAMES; game_index++)
|
|
{
|
|
struct network_advertised_game *current = available_games + game_index;
|
|
|
|
if (transport_nonce_is_equal(current->nonce, advertisement->nonce))
|
|
{
|
|
advertised_game = current;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!advertised_game)
|
|
{
|
|
for (game_index = 0; game_index < MAXIMUM_NETWORK_ADVERTISED_GAMES; game_index++)
|
|
{
|
|
struct network_advertised_game *current = available_games + game_index;
|
|
|
|
if (!current->valid)
|
|
{
|
|
advertised_game = current;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!advertised_game && open)
|
|
{
|
|
for (game_index = 0; game_index < MAXIMUM_NETWORK_ADVERTISED_GAMES; game_index++)
|
|
{
|
|
struct network_advertised_game *current = available_games + game_index;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x61F,
|
|
current->valid);
|
|
|
|
if (!current->open)
|
|
{
|
|
advertised_game = current;
|
|
csmemset(advertised_game, 0, sizeof(*advertised_game));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (advertised_game)
|
|
{
|
|
advertised_game->valid = TRUE;
|
|
advertised_game->key = advertisement->key;
|
|
advertised_game->key_id = advertisement->key_id;
|
|
advertised_game->xnaddr = advertisement->xnaddr;
|
|
|
|
csmemcpy(
|
|
advertised_game->nonce,
|
|
advertisement->nonce,
|
|
sizeof(advertisement->nonce));
|
|
|
|
advertised_game->update_time = system_milliseconds();
|
|
advertised_game->platform = advertisement->platform;
|
|
/* (a host built before there was a version sends zeros: 0) */
|
|
{
|
|
long game_index = advertised_game - available_games;
|
|
|
|
network_game_client_advertised_versions[game_index].version = (word)(
|
|
advertisement->__unknown5A[HALO_PORT_ADVERTISED_VERSION_OFFSET] |
|
|
(advertisement->__unknown5A[HALO_PORT_ADVERTISED_VERSION_OFFSET + 1] << 8));
|
|
network_game_client_advertised_versions[game_index].flags =
|
|
advertisement->__unknown5A[HALO_PORT_ADVERTISED_FLAGS_OFFSET];
|
|
}
|
|
|
|
if (advertisement->game_name[0] != L'\0')
|
|
{
|
|
ustrncpy(
|
|
advertised_game->game_name,
|
|
advertisement->game_name,
|
|
NETWORK_GAME_NAME_LENGTH - 1);
|
|
}
|
|
else
|
|
{
|
|
ustrncpy(
|
|
advertised_game->game_name,
|
|
L"???",
|
|
NETWORK_GAME_NAME_LENGTH - 1);
|
|
}
|
|
advertised_game->game_name[NETWORK_GAME_NAME_LENGTH - 1] = L'\0';
|
|
|
|
advertised_game->engine_type = advertisement->engine_type;
|
|
|
|
csmemcpy(
|
|
&advertised_game->map,
|
|
&advertisement->map,
|
|
sizeof(advertisement->map));
|
|
/* (from any machine on the network: not trusted to end) */
|
|
advertised_game->map.name[NUMBEROF(advertised_game->map.name) - 1] = '\0';
|
|
|
|
advertised_game->machine_count = advertisement->machine_count;
|
|
advertised_game->player_count = advertisement->player_count;
|
|
advertised_game->maximum_player_count = advertisement->maximum_player_count;
|
|
advertised_game->unknown100 = advertisement->unknown100;
|
|
advertised_game->open = open;
|
|
advertised_game->has_teams = TEST_FLAG(
|
|
advertisement->flags,
|
|
_game_advertisement_has_teams_bit);
|
|
advertised_game->oddball_variant =
|
|
advertised_game->engine_type == game_engine_oddball &&
|
|
TEST_FLAG(advertisement->flags, _game_advertisement_oddball_variant_bit);
|
|
|
|
success = TRUE;
|
|
|
|
network_event(
|
|
"there is %s %s net game with %d players and %d machines",
|
|
advertised_game->open ? "an open" : "a closed",
|
|
advertised_game->platform == _network_game_platform_xbox ? "XBox" :
|
|
(advertised_game->platform == _network_game_platform_pc ? "PC" : "<unknown platform>"),
|
|
advertised_game->player_count,
|
|
advertised_game->machine_count);
|
|
}
|
|
else
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"not fatal, but we have to many active network games cannot add more to the list");
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static boolean network_game_client_process_incoming_messages(
|
|
struct network_game_client *client)
|
|
{
|
|
boolean success = TRUE;
|
|
word message_packet_size;
|
|
struct transport_address source_address;
|
|
word message_packet[MAXIMUM_NETWORK_MESSAGE_SIZE / sizeof(word)];
|
|
|
|
message_packet_size = sizeof(message_packet);
|
|
|
|
while (success && network_connection_read(
|
|
client->connection,
|
|
message_packet,
|
|
&message_packet_size,
|
|
&source_address))
|
|
{
|
|
if (!(success = network_game_client_handle_message(
|
|
client,
|
|
message_packet,
|
|
message_packet_size,
|
|
&source_address)))
|
|
{
|
|
network_event("network_game_client_handle_message() failed in network_game_client_process_incoming_messages()");
|
|
}
|
|
|
|
message_packet_size = sizeof(message_packet);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/* port: the messages the connection has queued when it has just failed or
|
|
closed: the host's last before it closed the connection (a rejection, which
|
|
it follows with the close) came with the close, and the queue holds only
|
|
whole messages. TRUE when they took the client out of its state (a
|
|
rejection, network_game_client_rejected_by_game, which says why and ends the
|
|
game): the connection's failure is then no failure of the client's. */
|
|
static boolean network_game_client_process_last_messages(
|
|
struct network_game_client *client)
|
|
{
|
|
short state = client->state;
|
|
|
|
if (!network_game_client_process_incoming_messages(client))
|
|
{
|
|
network_event("network_game_client_process_incoming_messages() failed after the connection failed");
|
|
}
|
|
|
|
return client->state != state;
|
|
}
|
|
|
|
static void network_game_client_update_precache_status(
|
|
struct network_game_client *client)
|
|
{
|
|
unsigned long now = system_milliseconds();
|
|
|
|
if (now - client->last_precache_time > 1000)
|
|
{
|
|
char *map_name = main_get_multiplayer_map_name();
|
|
|
|
client->last_precache_time = now;
|
|
|
|
if (cache_files_give_time_to_precache(map_name))
|
|
{
|
|
struct message_client_map_is_precached_pregame map_is_precached = {0};
|
|
message_header *message;
|
|
|
|
csstrncpy(
|
|
map_is_precached.map_name,
|
|
map_name,
|
|
sizeof(map_is_precached.map_name));
|
|
|
|
message = create_network_game_message(
|
|
_message_client_map_is_precached_pregame,
|
|
&map_is_precached,
|
|
sizeof(map_is_precached));
|
|
if (message)
|
|
{
|
|
if (!network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1))
|
|
{
|
|
network_event("network_game_client_write() failed while sending a message_client_graceful_game_exit_pregame message");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean network_game_client_idle_searching(
|
|
struct network_game_client *client)
|
|
{
|
|
unsigned long now = system_milliseconds();
|
|
boolean success;
|
|
|
|
network_connection_keep_alive(client->connection);
|
|
|
|
success = check_networking_and_generate_error();
|
|
|
|
if (success == TRUE)
|
|
{
|
|
if (global_network_game_server_get())
|
|
{
|
|
struct network_advertised_game local_game = {0};
|
|
struct network_join_parameters join_parameters;
|
|
struct transport_address server_address;
|
|
|
|
server_address.address.long_words[0] = IPV4_LOOPBACK_ADDRESS;
|
|
server_address.port = NETWORK_GAME_SERVER_PORT;
|
|
server_address.address_length = IPV4_ADDRESS_LENGTH;
|
|
local_game.platform = network_game_get_local_platform();
|
|
|
|
transport_get_nonce(local_game.nonce, sizeof(local_game.nonce));
|
|
|
|
join_parameters.__unknown2 = 0;
|
|
network_game_generate_join_game_token(join_parameters.join_token);
|
|
|
|
if (!network_game_client_initiate_join_game(
|
|
client,
|
|
&local_game,
|
|
&join_parameters,
|
|
&server_address))
|
|
{
|
|
success = FALSE;
|
|
display_error_when_main_menu_loaded(7);
|
|
network_event("network_game_client_initiate_join_game() failed");
|
|
}
|
|
}
|
|
else if (!(success = network_connection_idle(client->connection, 5000, NULL)))
|
|
{
|
|
display_error_when_main_menu_loaded(7);
|
|
network_event("network_connection_idle() failed in network_game_client_idle_searching()");
|
|
}
|
|
else if (!(success = network_game_client_process_incoming_messages(client)))
|
|
{
|
|
network_event("network_game_client_process_incoming_messages() failed in network_game_client_idle_searching()");
|
|
}
|
|
else if (now - client->last_broadcast_search_time > 2000)
|
|
{
|
|
if (!global_network_game_server_get())
|
|
{
|
|
struct message_client_broadcast_game_search broadcast_game_search;
|
|
struct transport_address broadcast_address;
|
|
message_header *message;
|
|
|
|
broadcast_game_search.port = NETWORK_GAME_CLIENT_PORT;
|
|
/* the search version: the native builds' games are not the Xbox's */
|
|
broadcast_game_search.__unknown2 = HALO_PORT_NETWORK_GAME_MESSAGE_VERSION;
|
|
transport_get_nonce(
|
|
broadcast_game_search.nonce,
|
|
sizeof(broadcast_game_search.nonce));
|
|
broadcast_address.address_length = IPV4_ADDRESS_LENGTH;
|
|
broadcast_address.address.long_words[0] = BROADCAST_IPV4_ADDRESS;
|
|
broadcast_address.port = NETWORK_GAME_SERVER_PORT;
|
|
|
|
message = create_network_game_message(
|
|
_message_client_broadcast_game_search,
|
|
&broadcast_game_search,
|
|
sizeof(broadcast_game_search));
|
|
if (message)
|
|
{
|
|
success = network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
&broadcast_address,
|
|
0);
|
|
if (success == TRUE)
|
|
{
|
|
network_event("sent out a broadcast game search packet");
|
|
client->last_broadcast_search_time = now;
|
|
}
|
|
else
|
|
{
|
|
network_event("network_game_client_write() failed while sending a message_client_broadcast_game_search message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_broadcast_game_search message");
|
|
}
|
|
}
|
|
}
|
|
else if (client->pinging == TRUE && now - client->last_ping_time > 1000)
|
|
{
|
|
struct message_client_ping ping;
|
|
message_header *message;
|
|
|
|
ping.timestamp = now;
|
|
ping.port = NETWORK_GAME_CLIENT_PORT;
|
|
|
|
message = create_network_game_message(
|
|
_message_client_ping,
|
|
&ping,
|
|
sizeof(ping));
|
|
if (message)
|
|
{
|
|
if (network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
&client->ping_address,
|
|
0))
|
|
{
|
|
client->last_ping_time = now;
|
|
}
|
|
else
|
|
{
|
|
network_event("network_game_client_write() failed while sending a message_client_ping message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_ping message");
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static boolean network_game_client_idle_joining(
|
|
struct network_game_client *client)
|
|
{
|
|
boolean success = check_networking_and_generate_error();
|
|
|
|
if (success == TRUE)
|
|
{
|
|
if ((boolean)network_connection_connected(client->connection))
|
|
{
|
|
if (!TEST_FLAG(client->flags, _network_game_client_join_request_sent_bit))
|
|
{
|
|
struct message_client_join_game_request join_game_request;
|
|
message_header *message;
|
|
|
|
csmemset(&join_game_request, 0, sizeof(join_game_request));
|
|
network_game_generate_local_machine_name(join_game_request.machine_name);
|
|
csmemcpy(
|
|
join_game_request.join_token,
|
|
client->join_parameters.join_token,
|
|
sizeof(join_game_request.join_token));
|
|
/* (port: and its hardware id, which a host logs and may have
|
|
banned: all 32 digits, unended) */
|
|
{
|
|
char hardware_id[sizeof(join_game_request.hardware_id) + 1];
|
|
|
|
p2p_hardware_id(hardware_id, sizeof(hardware_id));
|
|
csmemcpy(join_game_request.hardware_id, hardware_id, csstrlen(hardware_id));
|
|
}
|
|
|
|
message = create_network_game_message(
|
|
_message_client_join_game_request,
|
|
&join_game_request,
|
|
sizeof(join_game_request));
|
|
if (message)
|
|
{
|
|
if (network_game_client_write(
|
|
client->connection,
|
|
message,
|
|
GET_MESSAGE_SIZE(*message),
|
|
NULL,
|
|
1))
|
|
{
|
|
SET_FLAG(
|
|
client->flags,
|
|
_network_game_client_join_request_sent_bit,
|
|
TRUE);
|
|
}
|
|
else
|
|
{
|
|
network_event("network_game_client_write() failed to send a message_client_join_game_request message");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_client_join_game_request message");
|
|
}
|
|
}
|
|
|
|
client->connect_process = 0;
|
|
}
|
|
else if (client->connect_process &&
|
|
system_milliseconds() - client->connection_attempt_time > 120000)
|
|
{
|
|
network_event("client connection process has timed out; aborting connection attempt");
|
|
cancel_connect_process(client->connect_process);
|
|
client->connect_process = 0;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
success = network_connection_idle(client->connection, 5000, NULL);
|
|
|
|
if (success)
|
|
{
|
|
if (!(success = network_game_client_process_incoming_messages(client)))
|
|
{
|
|
network_event("network_game_client_process_incoming_messages() failed in network_game_client_idle_joining()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("network_connection_idle() failed in network_game_client_idle_joining()");
|
|
success = network_game_client_process_last_messages(client);
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static boolean network_game_client_idle_pregame(
|
|
struct network_game_client *client)
|
|
{
|
|
boolean success = check_networking_and_generate_error();
|
|
|
|
if (success)
|
|
{
|
|
if (network_connection_active(client->connection) &&
|
|
(boolean)network_connection_connected(client->connection))
|
|
{
|
|
network_game_client_update_precache_status(client);
|
|
|
|
if (!(success = network_connection_idle(client->connection, 15000, NULL)))
|
|
{
|
|
network_event("network_connection_idle() failed in network_game_client_idle_pregame()");
|
|
if (network_game_client_process_last_messages(client))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (!(success = network_game_client_process_incoming_messages(client)))
|
|
{
|
|
network_event("network_game_client_process_incoming_messages() failed in network_game_client_idle_pregame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
success = FALSE;
|
|
if (network_game_client_process_last_messages(client))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
if (!network_connection_active(client->connection))
|
|
{
|
|
display_error_when_main_menu_loaded(4);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static boolean network_game_client_idle_ingame(
|
|
struct network_game_client *client)
|
|
{
|
|
boolean success = TRUE;
|
|
/* port: until the host's first update the host may still be loading the
|
|
map (its main thread, which sends nothing meanwhile), as long as it waits
|
|
for the other machines to load (network_server_manager.c's
|
|
NETWORK_GAME_SERVER_MAXIMUM_WAIT_TIME_FOR_LEVEL_LOADING) */
|
|
boolean started = network_game_client_server_has_started_game(client);
|
|
|
|
if (!network_connection_active(client->connection) ||
|
|
!network_connection_connected(client->connection))
|
|
{
|
|
error(_error_silent, "new idle in game abort hit");
|
|
display_error_when_main_menu_loaded(_error_network_server_shut_down);
|
|
success = FALSE;
|
|
}
|
|
else if (!network_game_is_splitscreen_local())
|
|
{
|
|
boolean connection_stale = network_connection_going_stale(client->connection);
|
|
|
|
/* (port: not at once, network_game_client_network_lost) */
|
|
if (network_game_client_network_lost())
|
|
{
|
|
display_error_when_main_menu_loaded(_error_network_connection_lost);
|
|
network_event("network connection went down (idle in game)!");
|
|
success = FALSE;
|
|
}
|
|
else if (connection_stale && !client->connection_silent && started)
|
|
{
|
|
short local_player_index;
|
|
|
|
for (local_player_index = local_player_get_next(NONE);
|
|
local_player_index != NONE;
|
|
local_player_index = local_player_get_next(local_player_index))
|
|
{
|
|
display_error(
|
|
_error_network_trouble_is_brewing,
|
|
local_player_index,
|
|
FALSE,
|
|
FALSE);
|
|
}
|
|
|
|
network_event("network client connection has been silent for a dangerously long amount of time");
|
|
}
|
|
|
|
client->connection_silent = connection_stale;
|
|
}
|
|
|
|
if (success == TRUE)
|
|
{
|
|
success = network_connection_idle(client->connection, started ? 15000 : 75000, NULL);
|
|
|
|
if (success)
|
|
{
|
|
success = network_game_client_process_incoming_messages(client);
|
|
|
|
if (!success)
|
|
{
|
|
network_event("network_game_client_process_incoming_messages() failed in network_game_client_idle_ingame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!network_connection_active(client->connection) ||
|
|
!network_connection_connected(client->connection))
|
|
{
|
|
error(_error_silent, "new2 idle in game abort hit");
|
|
display_error_when_main_menu_loaded(_error_network_server_shut_down);
|
|
success = FALSE;
|
|
}
|
|
|
|
network_event("network_connection_idle() failed in network_game_client_idle_ingame()");
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
static boolean network_game_client_idle_postgame(
|
|
struct network_game_client *client)
|
|
{
|
|
/* (the scores of the game just over: the network may be down for a
|
|
moment, as in the game, network_game_client_network_lost) */
|
|
boolean success = network_game_is_splitscreen_local() || !network_game_client_network_lost();
|
|
|
|
if (!success)
|
|
{
|
|
error(_error_silent, "network connection went down!");
|
|
display_error_when_main_menu_loaded(_error_network_connection_lost);
|
|
}
|
|
else
|
|
{
|
|
if (!(success = network_connection_idle(client->connection, 15000, NULL)))
|
|
{
|
|
network_event("network_connection_idle() failed in network_game_client_idle_postgame()");
|
|
if (network_game_client_process_last_messages(client))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (!(success = network_game_client_process_incoming_messages(client)))
|
|
{
|
|
network_event("network_game_client_process_incoming_messages() failed in network_game_client_idle_postgame()");
|
|
}
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
if (!network_connection_active(client->connection))
|
|
{
|
|
display_error_when_main_menu_loaded(4);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
static void network_game_client_set_error(
|
|
struct network_game_client *client,
|
|
word error)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_client_manager.c",
|
|
0x662,
|
|
client);
|
|
|
|
if (error >= NUMBER_OF_NETWORK_GAME_CLIENT_ERRORS)
|
|
error = _network_game_client_error_unknown;
|
|
|
|
if (client->error == _network_game_client_error_none)
|
|
client->error = error;
|
|
|
|
return;
|
|
}
|
|
|
|
/* the native ports' automated network tests (port/linux/game/network_test.c):
|
|
joins the first open game the client's search has found, as picking it in
|
|
the system link list does (network_game_join_game_from_server_list) */
|
|
/* the platform layer's (sdl_platform.c) */
|
|
void platform_show_message(char const *title, char const *message);
|
|
|
|
/* whether this client can join the advertised game: its host's network
|
|
version is this machine's (HALO_PORT_NETWORK_VERSION), and it plays the
|
|
distributed netcode (a host of this version built before the lockstep
|
|
netcode was removed may play that). If not the player is told why (when
|
|
tell), and nothing is joined. */
|
|
boolean network_game_client_advertised_game_compatible(
|
|
struct network_game_client *client,
|
|
struct network_advertised_game const *game,
|
|
boolean tell)
|
|
{
|
|
long game_index = client ? game - client->available_games : NONE;
|
|
unsigned int ours = HALO_PORT_NETWORK_VERSION;
|
|
unsigned int theirs;
|
|
boolean distributed;
|
|
char message[400];
|
|
|
|
if (game_index < 0 || game_index >= MAXIMUM_NETWORK_ADVERTISED_GAMES)
|
|
return FALSE;
|
|
theirs = network_game_client_advertised_versions[game_index].version;
|
|
distributed = (network_game_client_advertised_versions[game_index].flags &
|
|
HALO_PORT_ADVERTISED_DISTRIBUTED_FLAG) != 0;
|
|
if (theirs == ours && distributed)
|
|
{
|
|
network_event("joining a host of network version %u", theirs);
|
|
return TRUE;
|
|
}
|
|
if (theirs == ours)
|
|
{
|
|
csprintf(message,
|
|
"The host is using the lockstep network code, which this version no longer has.\n\n"
|
|
"Ask the host to update the game.");
|
|
}
|
|
else if (theirs > ours)
|
|
{
|
|
csprintf(message,
|
|
"The host is using a newer version of the network code than you.\n\n"
|
|
"You are on version %u. The host is on version %u.\n\n"
|
|
"Update the game to join this host.",
|
|
ours, theirs);
|
|
}
|
|
else
|
|
{
|
|
csprintf(message,
|
|
"The host is using an older version of the network code than you.\n\n"
|
|
"You are on version %u. The host is on version %u.\n\n"
|
|
"Ask the host to update the game.",
|
|
ours, theirs);
|
|
}
|
|
if (tell)
|
|
{
|
|
network_event("not joining a host of network version %u%s (this machine's is %u)", theirs,
|
|
distributed ? "" : " with the lockstep netcode", ours);
|
|
platform_show_message("Halo: cannot join this game", message);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
boolean network_game_client_join_first_available_game(
|
|
void)
|
|
{
|
|
struct network_game_client *client = global_network_game_client_get();
|
|
long game_index;
|
|
|
|
if (!client || client->state != _network_game_client_state_searching || client->join_in_progress ||
|
|
!client->connection || network_connection_connected(client->connection))
|
|
{
|
|
return FALSE;
|
|
}
|
|
for (game_index = 0; game_index < MAXIMUM_NETWORK_ADVERTISED_GAMES; game_index++)
|
|
{
|
|
struct network_advertised_game *game = &client->available_games[game_index];
|
|
|
|
if (network_game_client_advertised_game_is_valid(game) &&
|
|
game->platform == network_game_get_local_platform() && game->open)
|
|
{
|
|
struct transport_address address = { { { 0 } } };
|
|
struct network_join_parameters join_parameters;
|
|
|
|
/* (the automated tests try every frame: told once until the client resets) */
|
|
if (!network_game_client_advertised_game_compatible(client, game,
|
|
!network_game_client_incompatibility_told))
|
|
{
|
|
network_game_client_incompatibility_told = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
csmemset(&join_parameters, 0, sizeof(join_parameters));
|
|
transport_client_start((XNADDR const *)&game->xnaddr, (XNKEY const *)&game->key,
|
|
(XNKID const *)&game->key_id, NETWORK_GAME_SERVER_PORT, &address);
|
|
if (!address.address.long_words[0] || !address.port)
|
|
return FALSE;
|
|
network_game_generate_join_game_token(join_parameters.join_token);
|
|
return network_game_client_initiate_join_game(client, game, &join_parameters, &address);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* ... and puts this machine's players on a team (a team game needs both
|
|
teams), as the pregame screen's team choice does; NONE: the other team from
|
|
another machine's player */
|
|
boolean network_game_client_set_team(
|
|
char team_index)
|
|
{
|
|
struct network_game_client *client = global_network_game_client_get();
|
|
boolean success = FALSE;
|
|
short player_index;
|
|
|
|
if (!client || client->state != _network_game_client_state_pregame)
|
|
return FALSE;
|
|
for (player_index = 0; player_index < MAXIMUM_NUMBER_OF_PLAYERS && team_index == NONE; player_index++)
|
|
{
|
|
struct network_player const *player = &client->game.players[player_index];
|
|
|
|
if (network_player_is_valid(player) && player->machine_index != (char)client->machine_index)
|
|
team_index = player->team_index == 1 ? 0 : 1;
|
|
}
|
|
if (team_index == NONE)
|
|
team_index = 1;
|
|
for (player_index = 0; player_index < MAXIMUM_NUMBER_OF_PLAYERS; player_index++)
|
|
{
|
|
struct network_player player = client->game.players[player_index];
|
|
|
|
if (network_player_is_valid(&player) && player.machine_index == (char)client->machine_index)
|
|
{
|
|
player.team_index = team_index;
|
|
success |= network_game_client_update_local_player_data(client, &player);
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|