mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 16:00:26 +02:00
2812 lines
88 KiB
C
2812 lines
88 KiB
C
/*
|
|
NETWORK_SERVER_MESSAGE_HANDLER.C
|
|
|
|
symbols in this file:
|
|
0011E890 0090:
|
|
_code_0011e890 (0000)
|
|
0011E920 00a0:
|
|
_code_0011e920 (0000)
|
|
0011E9C0 0090:
|
|
_code_0011e9c0 (0000)
|
|
0011EA50 0090:
|
|
_code_0011ea50 (0000)
|
|
0011EAE0 00a0:
|
|
_code_0011eae0 (0000)
|
|
0011EB80 00a0:
|
|
_code_0011eb80 (0000)
|
|
0011EC20 0020:
|
|
_code_0011ec20 (0000)
|
|
0011EC40 0040:
|
|
_network_game_server_send_message_to_machine (0000)
|
|
0011EC80 0110:
|
|
_network_game_server_send_message_to_all_machines (0000)
|
|
0011ED90 0090:
|
|
_network_game_server_send_player_joined_info_ingame (0000)
|
|
0011EE20 00c0:
|
|
_network_game_server_send_game_data_pregame (0000)
|
|
0011EEE0 0240:
|
|
_code_0011eee0 (0000)
|
|
0011F120 00c0:
|
|
_code_0011f120 (0000)
|
|
0011F1E0 03f0:
|
|
_code_0011f1e0 (0000)
|
|
0011F5D0 00c0:
|
|
_code_0011f5d0 (0000)
|
|
0011F690 00c0:
|
|
_code_0011f690 (0000)
|
|
0011F750 00e0:
|
|
_code_0011f750 (0000)
|
|
0011F830 00d0:
|
|
_code_0011f830 (0000)
|
|
0011F900 00d0:
|
|
_code_0011f900 (0000)
|
|
0011F9D0 00f0:
|
|
_code_0011f9d0 (0000)
|
|
0011FAC0 0310:
|
|
_network_game_server_handle_datagram (0000)
|
|
0011FDD0 0400:
|
|
_network_game_server_handle_client_message (0000)
|
|
00286F00 005a:
|
|
??_C@_0FK@KMJJAKFL@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00286F60 0043:
|
|
??_C@_0ED@BONJCMOD@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
00286FA8 0065:
|
|
??_C@_0GF@BGPKJGEH@failed?5to?5handle?5a?5message_type_@ (0000)
|
|
00287010 004e:
|
|
??_C@_0EO@HMJBCLNP@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
00287060 0056:
|
|
??_C@_0FG@LPGCCINN@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
002870B8 0037:
|
|
??_C@_0DH@MMOKOPCN@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
002870F0 005e:
|
|
??_C@_0FO@NHCIMMDF@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00287150 004a:
|
|
??_C@_0EK@ONPDOJKK@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
002871A0 0068:
|
|
??_C@_0GI@DLKCJODI@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00287208 004f:
|
|
??_C@_0EP@DAOANLDM@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
00287258 0033:
|
|
??_C@_0DD@FJKDMFNA@server?5failed?5to?5remove?5a?5networ@ (0000)
|
|
00287290 005b:
|
|
??_C@_0FL@NDHDFCNK@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
002872F0 0045:
|
|
??_C@_0EF@MJLJMBIP@network_game_server_switch_machi@ (0000)
|
|
00287338 0059:
|
|
??_C@_0FJ@PEAAKLFN@network_game_server_write?$CI?$CJ?5fail@ (0000)
|
|
00287394 0027:
|
|
??_C@_0CH@EEEHHOGO@message_length?$DM?$DNsizeof?$CImessage_b@ (0000)
|
|
002873BC 0012:
|
|
??_C@_0BC@FDHBPMJE@server?5?$CG?$CG?5message?$AA@ (0000)
|
|
002873D0 003b:
|
|
??_C@_0DL@ICDCPLFI@c?3?2halo?2SOURCE?2networking?2networ@ (0000)
|
|
0028740C 003c:
|
|
??_C@_0DM@MOMFHJIG@failed?5to?5create?5a?5message_serve@ (0000)
|
|
00287448 0072:
|
|
??_C@_0HC@FGJDKKLC@network_game_server_send_message@ (0000)
|
|
002874C0 0058:
|
|
??_C@_0FI@LBJCENJB@failed?5to?5handle?5a?5message_serve@ (0000)
|
|
00287518 003f:
|
|
??_C@_0DP@CCCLFFGC@failed?5to?5create?5a?5message_serve@ (0000)
|
|
00287558 004b:
|
|
??_C@_0EL@NFHCHAEG@failed?5to?5send?5message_server_ga@ (0000)
|
|
002875A4 0039:
|
|
??_C@_0DJ@PCNJDMDI@failed?5to?5create?5a?5message_serve@ (0000)
|
|
002875E0 0054:
|
|
??_C@_0FE@HMKEDIDF@network_game_server_write?$CI?$CJ?5fail@ (0000)
|
|
00287634 002b:
|
|
??_C@_0CL@FOJJMNII@server?5?$CG?$CG?5source_address?5?$CG?$CG?5clie@ (0000)
|
|
00287660 002f:
|
|
??_C@_0CP@JCCANCCC@failed?5to?5create?5a?5message_serve@ (0000)
|
|
00287690 0043:
|
|
??_C@_0ED@LGNNJLKJ@network_game_server_write?$CI?$CJ?5fail@ (0000)
|
|
002876D8 0042:
|
|
??_C@_0EC@MGMKMMHI@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
0028771C 003b:
|
|
??_C@_0DL@JAHACCAG@failed?5to?5create?5a?5message_serve@ (0000)
|
|
00287758 0043:
|
|
??_C@_0ED@PLLDKIEI@client?5machine?5?8?$CFs?8?5?$EA?$CFs?5tried?5to@ (0000)
|
|
002877A0 0041:
|
|
??_C@_0EB@ONCPMINI@client?5machine?5?8?$CFs?8?5?$EA?$CFs?5tried?5to@ (0000)
|
|
002877E8 0043:
|
|
??_C@_0ED@DKNIPKFL@network_game_server_write?$CI?$CJ?5fail@ (0000)
|
|
00287830 0044:
|
|
??_C@_0EE@MGEHHOCB@server?5failed?5to?5accept?5valid?5cl@ (0000)
|
|
00287878 0075:
|
|
??_C@_0HF@HFOMEJBD@network_game_server_send_game_da@ (0000)
|
|
002878F0 0039:
|
|
??_C@_0DJ@CKOCAMEF@sent?5_message_type_server_machin@ (0000)
|
|
00287930 0064:
|
|
??_C@_0GE@MLODGBHN@network_game_server_write?$CI?$CJ?5fail@ (0000)
|
|
00287994 0029:
|
|
??_C@_0CJ@EPMEBAJK@network_machine_is_valid?$CIclient_@ (0000)
|
|
002879C0 0040:
|
|
??_C@_0EA@HPKAIHHK@server?5refused?5client?5?8?$CFs?8?5becau@ (0000)
|
|
00287A00 000d:
|
|
??_C@_0N@MPMCKMFC@d?3?2hosts?4txt?$AA@ (0000)
|
|
00287A10 002d:
|
|
??_C@_0CN@LBHCOBFK@ignoring?5redundant?5join?5request?5@ (0000)
|
|
00287A40 0062:
|
|
??_C@_0GC@IBIEHGFG@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00287AA8 004b:
|
|
??_C@_0EL@BFEPJEFO@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
00287AF8 0070:
|
|
??_C@_0HA@DHHGOECG@server?5failed?5to?5add?5a?5network?5p@ (0000)
|
|
00287B68 0072:
|
|
??_C@_0HC@LODCIJJJ@server?5failed?5to?5send?5pregame?5ga@ (0000)
|
|
00287BE0 0065:
|
|
??_C@_0GF@EKICMCEL@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00287C48 004e:
|
|
??_C@_0EO@JJCAGDLG@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
00287C98 0076:
|
|
??_C@_0HG@LNIKCCLC@server?5failed?5to?5remove?5a?5networ@ (0000)
|
|
00287D10 0075:
|
|
??_C@_0HF@KPAHGLDA@server?5failed?5to?5send?5pregame?5ga@ (0000)
|
|
00287D88 0058:
|
|
??_C@_0FI@HEMIANGD@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00287DE0 0041:
|
|
??_C@_0EB@EACNFEBF@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
00287E28 0075:
|
|
??_C@_0HF@CJBDOMOO@network_game_server_adjust_machi@ (0000)
|
|
00287EA0 0068:
|
|
??_C@_0GI@IHEHKNKB@server?5failed?5to?5send?5pregame?5ga@ (0000)
|
|
00287F08 0036:
|
|
??_C@_0DG@ECGMMKBK@server?5received?5machine?5settings@ (0000)
|
|
00287F40 005f:
|
|
??_C@_0FP@BKMGBKPC@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00287FA0 0048:
|
|
??_C@_0EI@NDLJFMCJ@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
00287FE8 006b:
|
|
??_C@_0GL@MGDAOAGD@network_game_update_player?$CI?$CJ?5fai@ (0000)
|
|
00288058 006f:
|
|
??_C@_0GP@IGEJNGEG@server?5failed?5to?5send?5pregame?5ga@ (0000)
|
|
002880C8 0028:
|
|
??_C@_0CI@NAMNDBJF@server?5received?5updated?5player?5s@ (0000)
|
|
002880F0 006a:
|
|
??_C@_0GK@PIEMGCFK@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00288160 004b:
|
|
??_C@_0EL@FGPNNAHJ@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
002881B0 0080:
|
|
??_C@_0IA@GMAELFDC@network_game_server_remove_machi@ (0000)
|
|
00288230 0072:
|
|
??_C@_0HC@JPOEIOK@server?5failed?5to?5send?5pregame?5ga@ (0000)
|
|
002882A8 0061:
|
|
??_C@_0GB@BICEKEIA@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
00288310 004d:
|
|
??_C@_0EN@FBHIPCPE@server?5failed?5to?5decode?5a?5messag@ (0000)
|
|
00288360 003a:
|
|
??_C@_0DK@HGNCDCGJ@server?5received?5a?5malformed?1dama@ (0000)
|
|
002883A0 0047:
|
|
??_C@_0EH@HDCJJGCF@server?5received?5low?9level?5error?5@ (0000)
|
|
002883E8 0046:
|
|
??_C@_0EG@NGIBKKFK@server?5received?5a?5bad?5message?5ty@ (0000)
|
|
00288430 003f:
|
|
??_C@_0DP@DJCDAEKP@failed?5to?5decode?5a?5message_clien@ (0000)
|
|
00288470 003e:
|
|
??_C@_0DO@EMEMPFEN@server?5failed?5to?5advertise?5game?5@ (0000)
|
|
002884B0 002e:
|
|
??_C@_0CO@IFNMPAGP@failed?5to?5decode?5a?5message_clien@ (0000)
|
|
002884E0 0026:
|
|
??_C@_0CG@PBGOHBCL@server?5failed?5to?5handle?5a?5client@ (0000)
|
|
00288508 0042:
|
|
??_C@_0EC@OIEIALKJ@ignoring?5a?5message_client_game_u@ (0000)
|
|
00288550 0062:
|
|
??_C@_0GC@EMAKPFP@failed?5to?5handle?5a?5message_clien@ (0000)
|
|
002885B4 0035:
|
|
??_C@_0DF@NPLDDCCG@failed?5to?5decode?5a?5message_clien@ (0000)
|
|
002885F0 0046:
|
|
??_C@_0EG@DEJFBBLL@server?5received?5datagram?5with?5an@ (0000)
|
|
00288638 004c:
|
|
??_C@_0EM@NPFBANHI@server?5received?5a?5datagram?5with?5@ (0000)
|
|
00288684 003c:
|
|
??_C@_0DM@HFFMPMPG@server?5received?5a?5datagram?5with?5@ (0000)
|
|
002886C0 0081:
|
|
??_C@_0IB@OPFACLKA@server?5?$CG?$CG?5message?5?$CG?$CG?5source_addr@ (0000)
|
|
00288744 003a:
|
|
??_C@_0DK@NEANDKEL@server?5received?5a?5malformed?1dama@ (0000)
|
|
00288780 0047:
|
|
??_C@_0EH@PCKBOEPO@server?5received?5low?9level?5error?5@ (0000)
|
|
002887C8 0046:
|
|
??_C@_0EG@GPPJDFA@server?5received?5a?5bad?5message?5ty@ (0000)
|
|
00288810 003e:
|
|
??_C@_0DO@LBJNGIAJ@bad?5or?5inappropriate?5packet?5type@ (0000)
|
|
00288850 004e:
|
|
??_C@_0EO@OPDINPHL@network_game_server_handle_messa@ (0000)
|
|
002888A0 0045:
|
|
??_C@_0EF@FHPBCOMI@network_game_server_handle_messa@ (0000)
|
|
002888E8 0052:
|
|
??_C@_0FC@HBMALEKE@network_game_server_handle_messa@ (0000)
|
|
00288940 0050:
|
|
??_C@_0FA@OHGBEJHJ@network_game_server_handle_messa@ (0000)
|
|
00288990 004d:
|
|
??_C@_0EN@IOBKAAMM@network_game_server_handle_messa@ (0000)
|
|
002889E0 003a:
|
|
??_C@_0DK@DJBGFGMF@network_game_server_handle_messa@ (0000)
|
|
00288A20 0046:
|
|
??_C@_0EG@KNLLMEKN@network_game_server_handle_messa@ (0000)
|
|
00288A68 004b:
|
|
??_C@_0EL@IBNDPBLB@network_game_server_handle_messa@ (0000)
|
|
00288AB8 0044:
|
|
??_C@_0EE@KLMGADOJ@network_game_server_handle_messa@ (0000)
|
|
00288B00 0051:
|
|
??_C@_0FB@PPCOJHGP@network_game_server_handle_messa@ (0000)
|
|
00288B58 004e:
|
|
??_C@_0EO@EEEMKLCF@network_game_server_handle_messa@ (0000)
|
|
00288BA8 0045:
|
|
??_C@_0EF@FHABLMKA@network_game_server_handle_messa@ (0000)
|
|
00288BF0 0048:
|
|
??_C@_0EI@INBBOEGP@an?5un?9validated?5client?5sent?5some@ (0000)
|
|
00288C38 0044:
|
|
??_C@_0EE@EPDDKPMF@server?5received?5a?5client?5message@ (0000)
|
|
00288C7C 0032:
|
|
??_C@_0DC@DAGHEIED@server?5received?5client?5message?5w@ (0000)
|
|
00288CB0 0054:
|
|
??_C@_0FE@BPJHKGCF@server?5?$CG?$CG?5machine?5?$CG?$CG?5message?5?$CG?$CG?5@ (0000)
|
|
*/
|
|
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "bungie_net/common/message_header.h"
|
|
#include "bungie_net/network/transport.h"
|
|
#include "bungie_net/network/transport_endpoint_winsock.h"
|
|
#include "game/game.h"
|
|
#include "game/game_engine.h"
|
|
#include "game/players.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_internal.h"
|
|
#include "networking/network_server_message_handler.h"
|
|
#include "text/unicode.h"
|
|
/* system_milliseconds(), for the settings update interval */
|
|
#include "cseries/cseries_windows.h"
|
|
|
|
/* port/linux/game/network_distributed.c's */
|
|
void network_distributed_handle_message(long machine_index, word const *message, word size);
|
|
void network_distributed_handle_stream_message(long machine_index, word const *message, word size);
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
/* the native builds' session limits and protocol
|
|
(port/linux/include/halo_port_limits.h) */
|
|
MAXIMUM_NETWORK_MACHINE_COUNT = HALO_PORT_MAXIMUM_NETWORK_MACHINES,
|
|
NETWORK_MESSAGE_BUFFER_SIZE = HALO_PORT_MAXIMUM_NETWORK_MESSAGE_SIZE,
|
|
NETWORK_GAME_MESSAGE_VERSION = HALO_PORT_NETWORK_GAME_MESSAGE_VERSION,
|
|
MAXIMUM_MACHINE_NAME_LENGTH = 32,
|
|
NETWORK_GAME_MAP_NAME_LENGTH = 0x100,
|
|
JOIN_GAME_TOKEN_LENGTH = 16,
|
|
TRANSPORT_ERROR_MESSAGE_TEXT_LENGTH = 0x80,
|
|
REMOVE_PLAYER_INGAME_GAME_TIME_DELAY = 33,
|
|
TRANSPORT_NONCE_LENGTH = 8,
|
|
NETWORK_GAME_NAME_LENGTH = 16,
|
|
MAXIMUM_NUMBER_OF_PLAYERS = HALO_PORT_MAXIMUM_NETWORK_PLAYERS,
|
|
NUMBER_OF_MULTIPLAYER_TEAMS = 2,
|
|
/* port: the least time between two advertisements of the game
|
|
(milliseconds: network_game_server_handle_message_client_broadcast_game_search) */
|
|
GAME_ADVERTISEMENT_INTERVAL = 250,
|
|
/* port: the least time between two answers to one address's pings, and
|
|
the addresses kept to tell, no more of them answered in that time
|
|
(network_game_server_handle_message_client_ping) */
|
|
PING_REPLY_INTERVAL = 250,
|
|
MAXIMUM_PING_REPLY_ADDRESSES = 32,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_game_advertisement_open_bit = 1,
|
|
_game_advertisement_has_teams_bit,
|
|
_game_advertisement_oddball_variant_bit,
|
|
};
|
|
|
|
#define MINIMUM_TRANSPORT_ERROR_MESSAGE_SIZE (sizeof(word) + TRANSPORT_ERROR_MESSAGE_TEXT_LENGTH + sizeof(byte))
|
|
|
|
enum
|
|
{
|
|
_message_type_error = 1,
|
|
_message_type_data = 2,
|
|
};
|
|
|
|
enum network_game_server_state
|
|
{
|
|
_network_game_server_state_pregame = 0,
|
|
_network_game_server_state_ingame,
|
|
_network_game_server_state_postgame,
|
|
};
|
|
|
|
enum network_game_packet_class
|
|
{
|
|
_network_game_packet_class_client_search = 0,
|
|
_network_game_packet_class_client_pregame = 3,
|
|
_network_game_packet_class_client_ingame = 5,
|
|
_network_game_packet_class_client_postgame = 7,
|
|
};
|
|
|
|
enum network_game_server_rejection_reason
|
|
{
|
|
_network_game_server_rejection_reason_bad_join_token = 2,
|
|
_network_game_server_rejection_reason_game_not_open = 5,
|
|
};
|
|
|
|
enum network_game_server_countdown_event
|
|
{
|
|
_network_game_server_countdown_event_player_left = 0,
|
|
_network_game_server_countdown_event_player_joined,
|
|
_network_game_server_countdown_event_stop,
|
|
_network_game_server_countdown_event_start_immediately,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
#define network_machine_is_valid(machine) \
|
|
((machine) && (machine)->machine_index >= 0 && \
|
|
(machine)->machine_index < MAXIMUM_NETWORK_MACHINE_COUNT)
|
|
|
|
/* ---------- structures */
|
|
|
|
struct network_connection;
|
|
struct network_game_server;
|
|
struct network_game_server_client_machine;
|
|
|
|
typedef char network_game_players_offset_assert[
|
|
offsetof(struct network_game, players) == HALO_PORT_NETWORK_GAME_PLAYERS_OFFSET ? 1 : -1];
|
|
typedef char network_game_size_assert[
|
|
sizeof(struct network_game) == HALO_PORT_NETWORK_GAME_SIZE ? 1 : -1];
|
|
|
|
struct network_message
|
|
{
|
|
word header;
|
|
};
|
|
|
|
struct message_client_broadcast_game_search
|
|
{
|
|
word port;
|
|
short version;
|
|
byte nonce[TRANSPORT_NONCE_LENGTH];
|
|
};
|
|
|
|
struct message_client_ping
|
|
{
|
|
long timestamp;
|
|
short port;
|
|
byte padding[2];
|
|
};
|
|
|
|
struct message_client_game_update
|
|
{
|
|
unsigned long update_number;
|
|
short unknown;
|
|
short player_count;
|
|
/* a machine's players, which 16 players / 4 machines only happened to give */
|
|
struct player_action actions[MAXIMUM_LOCAL_PLAYERS];
|
|
};
|
|
|
|
struct message_server_pong
|
|
{
|
|
long timestamp;
|
|
};
|
|
|
|
struct message_server_game_advertise
|
|
{
|
|
byte client_nonce[TRANSPORT_NONCE_LENGTH];
|
|
byte nonce[TRANSPORT_NONCE_LENGTH];
|
|
XNKID key_id;
|
|
XNKEY key;
|
|
XNADDR xnaddr;
|
|
word port;
|
|
word version;
|
|
word platform;
|
|
wchar_t game_name[NETWORK_GAME_NAME_LENGTH];
|
|
byte reserved[0x1A];
|
|
struct network_game_map map;
|
|
short engine_type;
|
|
short machine_count;
|
|
short player_count;
|
|
short maximum_player_count;
|
|
short variant_setting;
|
|
word flags;
|
|
byte join_game_token[JOIN_GAME_TOKEN_LENGTH];
|
|
};
|
|
|
|
struct message_client_join_game_request
|
|
{
|
|
wchar_t machine_name[MAXIMUM_MACHINE_NAME_LENGTH];
|
|
byte join_game_token[JOIN_GAME_TOKEN_LENGTH];
|
|
/* port: the machine's hardware id, as hex, as it tells it (p2p.c) */
|
|
char hardware_id[0x20];
|
|
};
|
|
|
|
struct message_client_settings_request
|
|
{
|
|
wchar_t machine_name[MAXIMUM_MACHINE_NAME_LENGTH];
|
|
char machine_index;
|
|
byte padding[3];
|
|
};
|
|
|
|
struct message_client_game_start_request
|
|
{
|
|
long countdown_time;
|
|
};
|
|
|
|
struct message_client_graceful_game_exit_pregame
|
|
{
|
|
long unused;
|
|
};
|
|
|
|
struct message_client_map_is_precached_pregame
|
|
{
|
|
char map_name[NETWORK_GAME_MAP_NAME_LENGTH];
|
|
};
|
|
|
|
struct message_client_loaded
|
|
{
|
|
long unused;
|
|
};
|
|
|
|
struct message_client_switch_to_pregame
|
|
{
|
|
long unused;
|
|
};
|
|
|
|
struct message_client_graceful_game_exit_postgame
|
|
{
|
|
long unused;
|
|
};
|
|
|
|
struct message_server_machine_accepted
|
|
{
|
|
long random_seed;
|
|
short machine_index;
|
|
byte padding[2];
|
|
};
|
|
|
|
struct message_server_machine_rejected
|
|
{
|
|
short reason;
|
|
};
|
|
|
|
/* the game settings record no longer fits one message: it goes out as
|
|
consecutive pieces, which the clients put back together */
|
|
struct message_server_game_settings_update
|
|
{
|
|
word total_size;
|
|
word offset;
|
|
word length;
|
|
word pad;
|
|
byte data[HALO_PORT_NETWORK_GAME_SETTINGS_FRAGMENT_SIZE];
|
|
};
|
|
|
|
struct message_server_remove_player_ingame
|
|
{
|
|
struct network_player player;
|
|
long game_time;
|
|
};
|
|
|
|
/* ---------- prototypes */
|
|
|
|
static boolean network_game_server_handle_message_client_broadcast_game_search(
|
|
struct network_game_server *server,
|
|
struct transport_address *source_address,
|
|
struct message_client_broadcast_game_search *client_message);
|
|
static boolean network_game_server_handle_message_client_ping(
|
|
struct network_game_server *server,
|
|
struct transport_address *source_address,
|
|
struct message_client_ping *client_message);
|
|
static boolean network_game_server_handle_message_client_join_game_request(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_add_player_request_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_remove_player_request_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_settings_request(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_player_settings_request(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_game_start_request(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_graceful_game_exit_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_map_is_precached_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_loaded(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_add_player_request_ingame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_remove_player_request_ingame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_remove_player_request_postgame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_switch_to_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
static boolean network_game_server_handle_message_client_graceful_game_exit_postgame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size);
|
|
|
|
/* ---------- globals */
|
|
|
|
/* ---------- private code */
|
|
|
|
/* port: a name a machine sends (its machine's, or a player's: they come
|
|
from the wire, from anyone joining by any link) kept to what draws as one
|
|
line of text (player_name_clean); one with nothing left that the host's
|
|
ban command can type the default given */
|
|
static void network_game_server_clean_name(
|
|
wchar_t *name,
|
|
long count,
|
|
wchar_t const *default_name)
|
|
{
|
|
if (!player_name_clean(name, count))
|
|
{
|
|
long index;
|
|
|
|
for (index = 0; index < count - 1 && default_name[index]; index++)
|
|
name[index] = default_name[index];
|
|
name[index] = 0;
|
|
}
|
|
}
|
|
|
|
/* port: a name as the host's ban command reads it: in ASCII
|
|
(player_name_character_ascii), in lower case */
|
|
static char network_game_server_name_character(
|
|
wchar_t character)
|
|
{
|
|
char ascii = player_name_character_ascii(character);
|
|
|
|
return ascii >= 'A' && ascii <= 'Z' ? (char)(ascii - 'A' + 'a') : ascii;
|
|
}
|
|
|
|
/* port: whether another player of the game than the one at own_index has a
|
|
name the ban command would read as the same */
|
|
static boolean network_game_server_player_name_taken(
|
|
struct network_game_server *server,
|
|
wchar_t const *name,
|
|
long own_index)
|
|
{
|
|
struct network_game *game = network_game_server_get_game(server);
|
|
long index;
|
|
|
|
for (index = 0; index < (long)NUMBEROF(game->players); index++)
|
|
{
|
|
wchar_t const *other = game->players[index].name;
|
|
long character;
|
|
|
|
if (index == own_index || !network_player_is_valid(&game->players[index]))
|
|
continue;
|
|
for (character = 0; character < (long)NUMBEROF(game->players[index].name); character++)
|
|
{
|
|
if (!name[character] || !other[character])
|
|
{
|
|
if (!name[character] && !other[character])
|
|
return TRUE;
|
|
break;
|
|
}
|
|
if (network_game_server_name_character(name[character]) != network_game_server_name_character(other[character]))
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* port: a player's name from the wire made one the host's ban command can
|
|
always single out: kept to text that draws, with something it can type
|
|
(else "Player"), and, the same as another player's, numbered ("Player 2") */
|
|
static void network_game_server_clean_player_name(
|
|
struct network_game_server *server,
|
|
struct network_player *player,
|
|
long own_index)
|
|
{
|
|
wchar_t base[NUMBEROF(player->name)];
|
|
long number;
|
|
|
|
network_game_server_clean_name(player->name, NUMBEROF(player->name), L"Player");
|
|
csmemcpy(base, player->name, sizeof(base));
|
|
for (number = 2; number < 100 && network_game_server_player_name_taken(server, player->name, own_index); number++)
|
|
{
|
|
wchar_t suffix[4];
|
|
long suffix_length = number < 10 ? 2 : 3;
|
|
long length = ustrlen(base);
|
|
|
|
suffix[0] = L' ';
|
|
suffix[1] = number < 10 ? (wchar_t)(L'0' + number) : (wchar_t)(L'0' + number / 10);
|
|
suffix[2] = (wchar_t)(L'0' + number % 10);
|
|
suffix[suffix_length] = 0;
|
|
if (length > (long)NUMBEROF(player->name) - 1 - suffix_length)
|
|
length = (long)NUMBEROF(player->name) - 1 - suffix_length;
|
|
csmemcpy(player->name, base, length * sizeof(wchar_t));
|
|
while (length > 0 && player->name[length - 1] == L' ')
|
|
length--;
|
|
csmemcpy(player->name + length, suffix, (suffix_length + 1) * sizeof(wchar_t));
|
|
}
|
|
}
|
|
|
|
static boolean network_game_server_write(
|
|
struct network_connection *connection,
|
|
void *message,
|
|
word buffer_size,
|
|
struct transport_address *destination_address,
|
|
boolean reliable)
|
|
{
|
|
return network_connection_write(
|
|
connection,
|
|
message,
|
|
buffer_size,
|
|
destination_address,
|
|
reliable);
|
|
}
|
|
|
|
/* port: whether a line about a stray datagram (which anyone may send, as
|
|
fast as they like) goes in the log: at most one a second, with how many were
|
|
left out (the host writes its log a line at a time) */
|
|
static boolean network_game_server_datagram_event_logged(
|
|
void)
|
|
{
|
|
static unsigned long last_time = 0;
|
|
static long left_out_count = 0;
|
|
unsigned long now = system_milliseconds();
|
|
|
|
if (last_time && now - last_time < MILLISECONDS_PER_SECOND)
|
|
{
|
|
left_out_count++;
|
|
return FALSE;
|
|
}
|
|
if (left_out_count)
|
|
network_event("(%ld more stray datagrams not logged)", left_out_count);
|
|
left_out_count = 0;
|
|
last_time = now ? now : 1;
|
|
return TRUE;
|
|
}
|
|
|
|
/* ---------- public code */
|
|
|
|
boolean network_game_server_send_message_to_machine(
|
|
struct network_game_server *server,
|
|
struct network_machine *machine,
|
|
struct network_message *message)
|
|
{
|
|
boolean result = FALSE;
|
|
struct network_connection *connection =
|
|
network_game_server_get_machine_connection(server, machine);
|
|
|
|
if (connection)
|
|
{
|
|
word message_size = GET_MESSAGE_SIZE(message->header);
|
|
|
|
result = network_game_server_write(
|
|
connection,
|
|
message,
|
|
message_size,
|
|
NULL,
|
|
1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* a machine that joined a distributed game in progress and has not loaded
|
|
it yet (it hears none of the game's messages until it has) */
|
|
static boolean network_game_server_machine_is_loading_late(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *machine)
|
|
{
|
|
return network_game_server_get_state(server, NULL) == _network_game_server_state_ingame &&
|
|
!network_game_server_client_machine_is_loaded(server, machine);
|
|
}
|
|
|
|
boolean network_game_server_send_message_to_client_machine(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *machine,
|
|
void *message)
|
|
{
|
|
byte buffer[NETWORK_MESSAGE_BUFFER_SIZE];
|
|
word size = GET_MESSAGE_SIZE(((struct network_message *)message)->header);
|
|
struct network_connection *connection = network_game_server_get_client_connection(machine);
|
|
|
|
(void)server;
|
|
if (!connection || !network_connection_active(connection) || size > sizeof(buffer))
|
|
return FALSE;
|
|
/* (the write swaps the header in place) */
|
|
csmemcpy(buffer, message, size);
|
|
return network_game_server_write(connection, buffer, size, NULL, 1);
|
|
}
|
|
|
|
boolean network_game_server_send_game_settings_to_client_machine(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *machine,
|
|
void const *game,
|
|
long game_size)
|
|
{
|
|
struct message_server_game_settings_update message;
|
|
long offset;
|
|
|
|
for (offset = 0; offset < game_size; offset += sizeof(message.data))
|
|
{
|
|
void *encoded_message;
|
|
|
|
message.total_size = (word)game_size;
|
|
message.offset = (word)offset;
|
|
message.length = (word)MIN((long)sizeof(message.data), game_size - offset);
|
|
message.pad = 0;
|
|
csmemset(message.data, 0, sizeof(message.data));
|
|
csmemcpy(message.data, (byte const *)game + offset, message.length);
|
|
encoded_message = create_network_game_message(_message_server_game_settings_update, &message, sizeof(message));
|
|
if (!encoded_message || !network_game_server_send_message_to_client_machine(server, machine, encoded_message))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* the client machines in the game, as the message handlers number them
|
|
(the distributed netcode sends each its own datagram); their count */
|
|
short network_distributed_server_machines(
|
|
long *machine_indices,
|
|
short maximum)
|
|
{
|
|
struct network_game_server *server = global_network_game_server_get();
|
|
short count = 0;
|
|
long client_index;
|
|
|
|
if (!server)
|
|
return 0;
|
|
for (client_index = 0; client_index < MAXIMUM_NETWORK_MACHINE_COUNT && count < maximum; client_index++)
|
|
{
|
|
struct network_game_server_client_machine *machine =
|
|
network_game_server_get_client_machine_at_index(server, client_index);
|
|
struct network_connection *connection;
|
|
long game_machine_index;
|
|
|
|
if (!network_game_server_client_machine_is_joined_to_game(server, machine) ||
|
|
network_game_server_machine_is_loading_late(server, machine))
|
|
{
|
|
continue;
|
|
}
|
|
connection = network_game_server_get_client_connection(machine);
|
|
if (!connection || !network_connection_active(connection))
|
|
continue;
|
|
/* (port: and not a machine without a slot) */
|
|
if (!network_game_server_get_client_machine(server, machine, &game_machine_index) ||
|
|
game_machine_index == NONE)
|
|
{
|
|
continue;
|
|
}
|
|
machine_indices[count++] = game_machine_index;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/* the client machine of a machine in the game, at its slot (NULL if none) */
|
|
static struct network_game_server_client_machine *network_game_server_client_machine_in_game(
|
|
struct network_game_server *server,
|
|
long machine_index)
|
|
{
|
|
struct network_game_server_client_machine *machine;
|
|
long game_machine_index;
|
|
|
|
if (!VALID_INDEX(machine_index, MAXIMUM_NETWORK_MACHINE_COUNT))
|
|
return NULL;
|
|
machine = network_game_server_get_client_machine_at_index(server, machine_index);
|
|
if (!network_game_server_client_machine_is_joined_to_game(server, machine))
|
|
return NULL;
|
|
network_game_server_get_client_machine(server, machine, &game_machine_index);
|
|
return game_machine_index == machine_index ? machine : NULL;
|
|
}
|
|
|
|
/* the distributed netcode's per-tick state (port/linux/game/network_distributed.c),
|
|
unreliably (a lost one is overtaken by the next) to one machine in the game */
|
|
boolean network_distributed_server_send_to_machine(
|
|
long machine_index,
|
|
void *message,
|
|
word size)
|
|
{
|
|
struct network_game_server *server = global_network_game_server_get();
|
|
byte buffer[NETWORK_MESSAGE_BUFFER_SIZE];
|
|
struct network_game_server_client_machine *machine;
|
|
struct network_connection *connection;
|
|
struct transport_address address;
|
|
|
|
if (!server || size > sizeof(buffer))
|
|
return FALSE;
|
|
/* (a machine's slot is its index, network_game_server_accept_client_machine_into_game) */
|
|
machine = network_game_server_client_machine_in_game(server, machine_index);
|
|
if (!machine)
|
|
return FALSE;
|
|
connection = network_game_server_get_client_connection(machine);
|
|
if (!connection || !network_connection_active(connection))
|
|
return FALSE;
|
|
/* from the game's public datagram endpoint (the one clients send
|
|
their game updates to) to the client's, at the address its
|
|
connection comes from (the write swaps the header in place) */
|
|
network_connection_get_address(connection, &address, NULL);
|
|
address.port = NETWORK_GAME_CLIENT_PORT;
|
|
csmemcpy(buffer, message, size);
|
|
return network_game_server_write(network_game_server_get_connection(server), buffer, size, &address, 0);
|
|
}
|
|
|
|
/* reliably to one machine in the game (the host's objects, to a client
|
|
that has loaded) */
|
|
boolean network_distributed_server_send_to_machine_reliably(
|
|
long machine_index,
|
|
void *message,
|
|
word size)
|
|
{
|
|
struct network_game_server *server = global_network_game_server_get();
|
|
byte buffer[NETWORK_MESSAGE_BUFFER_SIZE];
|
|
struct network_game_server_client_machine *machine;
|
|
struct network_connection *connection;
|
|
|
|
if (!server || size > sizeof(buffer))
|
|
return FALSE;
|
|
/* (machine_index is the game's machine, as the message handlers have
|
|
it: the client machine at that slot) */
|
|
machine = network_game_server_client_machine_in_game(server, machine_index);
|
|
if (!machine)
|
|
return FALSE;
|
|
connection = network_game_server_get_client_connection(machine);
|
|
if (!connection || !network_connection_active(connection))
|
|
return FALSE;
|
|
csmemcpy(buffer, message, size);
|
|
return network_game_server_write(connection, buffer, size, NULL, 1);
|
|
}
|
|
|
|
/* to every machine, reliably (the netcode's objects appearing and going
|
|
must not be lost) */
|
|
boolean network_distributed_server_send_to_all_reliably(
|
|
void *message,
|
|
word size)
|
|
{
|
|
struct network_game_server *server = global_network_game_server_get();
|
|
boolean result = TRUE;
|
|
long machine_index;
|
|
|
|
if (!server)
|
|
return FALSE;
|
|
for (machine_index = 0; machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; machine_index++)
|
|
{
|
|
struct network_game_server_client_machine *machine =
|
|
network_game_server_get_client_machine_at_index(server, machine_index);
|
|
|
|
if (network_game_server_client_machine_is_joined_to_game(server, machine) &&
|
|
!network_game_server_machine_is_loading_late(server, machine))
|
|
{
|
|
struct network_connection *connection = network_game_server_get_client_connection(machine);
|
|
byte buffer[NETWORK_MESSAGE_BUFFER_SIZE];
|
|
|
|
if (connection && network_connection_active(connection) && size <= sizeof(buffer))
|
|
{
|
|
csmemcpy(buffer, message, size);
|
|
result &= network_game_server_write(connection, buffer, size, NULL, 1);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
boolean network_game_server_send_message_to_all_machines(
|
|
struct network_game_server *server,
|
|
struct network_message *message)
|
|
{
|
|
byte message_buffer[NETWORK_MESSAGE_BUFFER_SIZE];
|
|
boolean result = TRUE;
|
|
word message_length;
|
|
long machine_index;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0x187,
|
|
server && message);
|
|
|
|
message_length = GET_MESSAGE_SIZE(message->header);
|
|
for (machine_index = 0; machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; machine_index++)
|
|
{
|
|
struct network_game_server_client_machine *machine =
|
|
network_game_server_get_client_machine_at_index(server, machine_index);
|
|
|
|
if (network_game_server_client_machine_is_joined_to_game(server, machine)
|
|
/* (not a machine still loading the game in progress: it takes
|
|
none of the game's messages until it has) */
|
|
&& !network_game_server_machine_is_loading_late(server, machine)
|
|
)
|
|
{
|
|
struct network_connection *connection =
|
|
network_game_server_get_client_connection(machine);
|
|
|
|
if (connection && network_connection_active(connection))
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0x19A,
|
|
message_length<=sizeof(message_buffer));
|
|
|
|
csmemcpy(message_buffer, message, message_length);
|
|
if (!network_game_server_write(
|
|
connection,
|
|
message_buffer,
|
|
message_length,
|
|
NULL,
|
|
1))
|
|
{
|
|
network_event(
|
|
"network_game_server_write() failed in network_game_server_send_message_to_all_machines()");
|
|
result = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean network_game_server_send_player_joined_info_ingame(
|
|
struct network_game_server *server,
|
|
struct network_player *player)
|
|
{
|
|
struct network_player message;
|
|
void *encoded_message;
|
|
boolean result;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0x1B0,
|
|
server && player);
|
|
|
|
message = *player;
|
|
encoded_message = create_network_game_message(
|
|
_message_server_add_player_ingame,
|
|
&message,
|
|
sizeof(message));
|
|
if (encoded_message)
|
|
{
|
|
result = network_game_server_send_message_to_all_machines(server, encoded_message);
|
|
if (!result)
|
|
{
|
|
network_event(
|
|
"network_game_server_send_message_to_all_machines() failed in network_game_server_send_player_joined_info_ingame()");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
network_event("failed to create a message_server_add_player_ingame message");
|
|
return FALSE;
|
|
}
|
|
|
|
/* With up to 128 machines, sending the whole settings record (13 KB) to every
|
|
machine on each lobby change would flood the network while a lobby fills, so
|
|
changes are collected and sent at most this often; the server's pregame idle
|
|
sends what is pending. */
|
|
enum
|
|
{
|
|
NETWORK_GAME_SETTINGS_UPDATE_INTERVAL = MILLISECONDS_PER_SECOND / 4,
|
|
};
|
|
|
|
static boolean network_game_settings_update_pending = FALSE;
|
|
static unsigned long network_game_settings_update_time = 0;
|
|
|
|
boolean network_game_server_send_game_settings_to_all_machines(
|
|
struct network_game_server *server,
|
|
void const *game,
|
|
long game_size)
|
|
{
|
|
struct message_server_game_settings_update message;
|
|
long offset;
|
|
boolean result = TRUE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0x1C8,
|
|
server);
|
|
|
|
/* every piece goes out even if one fails for a machine: the others
|
|
would otherwise keep the old settings (a machine whose connection failed
|
|
is closed, and is skipped when the update is sent again) */
|
|
for (offset = 0; offset < game_size; offset += sizeof(message.data))
|
|
{
|
|
void *encoded_message;
|
|
|
|
message.total_size = (word)game_size;
|
|
message.offset = (word)offset;
|
|
message.length = (word)MIN((long)sizeof(message.data), game_size - offset);
|
|
message.pad = 0;
|
|
csmemset(message.data, 0, sizeof(message.data));
|
|
csmemcpy(message.data, (byte const *)game + offset, message.length);
|
|
encoded_message = create_network_game_message(
|
|
_message_server_game_settings_update,
|
|
&message,
|
|
sizeof(message));
|
|
if (encoded_message)
|
|
{
|
|
/* (a piece that failed stays failed: the next is no retry of it) */
|
|
if (!network_game_server_send_message_to_all_machines(server, encoded_message))
|
|
{
|
|
network_event(
|
|
"failed to send message_server_game_settings_update message to all machines");
|
|
result = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_server_game_settings_update message");
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
network_game_settings_update_pending = !result;
|
|
network_game_settings_update_time = system_milliseconds();
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean network_game_server_flush_game_data_pregame(
|
|
struct network_game_server *server)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
if (network_game_settings_update_pending &&
|
|
system_milliseconds() - network_game_settings_update_time >= NETWORK_GAME_SETTINGS_UPDATE_INTERVAL)
|
|
{
|
|
struct network_game *game = network_game_server_get_game(server);
|
|
|
|
if (game)
|
|
{
|
|
result = network_game_server_send_game_settings_to_all_machines(server, game, sizeof(*game));
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_server_game_settings_update because their was no server game");
|
|
network_game_settings_update_pending = FALSE;
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean network_game_server_send_game_data_pregame(
|
|
struct network_game_server *server)
|
|
{
|
|
network_game_settings_update_pending = TRUE;
|
|
|
|
return network_game_server_flush_game_data_pregame(server);
|
|
}
|
|
|
|
boolean network_game_server_handle_client_message(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *machine,
|
|
word *message,
|
|
short message_buffer_size)
|
|
{
|
|
boolean result = TRUE;
|
|
word message_type;
|
|
byte packet_type;
|
|
|
|
/* port: the size comes from the wire, and an assert is no check (an
|
|
empty message read its packet type from before the buffer): the machine
|
|
that sent it is dropped */
|
|
if (message_buffer_size <= (short)sizeof(message_header) ||
|
|
message_buffer_size != GET_MESSAGE_SIZE(*message))
|
|
{
|
|
network_event("server received a client message of a bad size (#%d)", message_buffer_size);
|
|
return FALSE;
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0xCF,
|
|
server && machine && message && (message_buffer_size == GET_MESSAGE_SIZE(*message)));
|
|
|
|
message_type = (byte)GET_MESSAGE_TYPE(*message);
|
|
if (GET_MESSAGE_FLAGS(*message))
|
|
{
|
|
network_event("server received client message with invalid flags");
|
|
}
|
|
else
|
|
{
|
|
switch (message_type)
|
|
{
|
|
case _message_type_packet:
|
|
packet_type = ((byte *)message)[message_buffer_size - 1];
|
|
if (network_game_server_client_machine_is_joined_to_game(server, machine) ||
|
|
packet_type == _message_client_join_game_request)
|
|
{
|
|
switch (packet_type)
|
|
{
|
|
case _message_client_join_game_request:
|
|
result = network_game_server_handle_message_client_join_game_request(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_join_game_request() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_add_player_request_pregame:
|
|
result = network_game_server_handle_message_client_add_player_request_pregame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_add_player_request_pregame() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_remove_player_request_pregame:
|
|
result = network_game_server_handle_message_client_remove_player_request_pregame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_remove_player_request_pregame() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_settings_request:
|
|
result = network_game_server_handle_message_client_settings_request(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_settings_request() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_player_settings_request:
|
|
result = network_game_server_handle_message_client_player_settings_request(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_player_settings_request() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_game_start_request:
|
|
result = network_game_server_handle_message_client_game_start_request(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_game_start_request() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_graceful_game_exit_pregame:
|
|
result = network_game_server_handle_message_client_graceful_game_exit_pregame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_graceful_game_exit_pregame() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_map_is_precached_pregame:
|
|
result = network_game_server_handle_message_client_map_is_precached_pregame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_graceful_game_exit_pregame() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_loaded:
|
|
result = network_game_server_handle_message_client_loaded(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_loaded() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_add_player_request_ingame:
|
|
result = network_game_server_handle_message_client_add_player_request_ingame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_add_player_request_ingame() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_remove_player_request_ingame:
|
|
result = network_game_server_handle_message_client_remove_player_request_ingame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_remove_player_request_ingame() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_remove_player_request_postgame:
|
|
result = network_game_server_handle_message_client_remove_player_request_postgame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_remove_player_request_postgame() failed");
|
|
}
|
|
break;
|
|
|
|
case _message_client_switch_to_pregame:
|
|
result = network_game_server_handle_message_client_switch_to_pregame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_switch_to_pregame() failed");
|
|
}
|
|
break;
|
|
|
|
/* (port: its own handler, which decodes it as the
|
|
postgame message it is: the pregame's, which this
|
|
called, took none out of the pregame, nor could decode
|
|
one) */
|
|
case _message_client_graceful_game_exit_postgame:
|
|
result = network_game_server_handle_message_client_graceful_game_exit_postgame(
|
|
server,
|
|
machine,
|
|
message,
|
|
message_buffer_size);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_handle_message_client_graceful_game_exit_postgame() failed");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
network_event(
|
|
"bad or inappropriate packet type received from a client (#%d)",
|
|
packet_type);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("an un-validated client sent something other than a join request message");
|
|
}
|
|
break;
|
|
|
|
case _message_type_data:
|
|
/* the distributed netcode's messages (port/linux/NETCODE.md) */
|
|
/* (in game, from a machine that has loaded it, as a datagram
|
|
is: else dropped) */
|
|
if (network_game_server_client_machine_is_joined_to_game(server, machine) &&
|
|
network_game_server_get_state(server, NULL) == _network_game_server_state_ingame &&
|
|
network_game_server_client_machine_is_loaded(server, machine))
|
|
{
|
|
long machine_index;
|
|
|
|
network_game_server_get_client_machine(server, machine, &machine_index);
|
|
/* (port: from its stream, which only it can send on) */
|
|
network_distributed_handle_stream_message(machine_index, message, message_buffer_size);
|
|
}
|
|
break;
|
|
|
|
case _message_type_error:
|
|
if (message_buffer_size >= MINIMUM_TRANSPORT_ERROR_MESSAGE_SIZE)
|
|
{
|
|
byte *error_message = (byte *)(message + 1);
|
|
|
|
/* (the text need not end in the message) */
|
|
network_event(
|
|
"server received low-level error message from a client: error= #%d (%.*s)",
|
|
error_message[TRANSPORT_ERROR_MESSAGE_TEXT_LENGTH],
|
|
(int)TRANSPORT_ERROR_MESSAGE_TEXT_LENGTH,
|
|
error_message);
|
|
}
|
|
else
|
|
{
|
|
network_event("server received a malformed/damaged message from a client");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
network_event(
|
|
"server received a client message with an unknown message type (#%d)",
|
|
message_type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean network_game_server_handle_datagram(
|
|
struct network_game_server *server,
|
|
word *message,
|
|
short datagram_size,
|
|
struct transport_address *source_address)
|
|
{
|
|
word message_type;
|
|
|
|
/* port: the size comes from the wire, and an assert is no check: a
|
|
datagram of a bad size is dropped (anyone may send one) */
|
|
if (datagram_size <= (short)sizeof(message_header) ||
|
|
datagram_size != GET_MESSAGE_SIZE(*message))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0x40,
|
|
server && message && source_address &&
|
|
(datagram_size > sizeof(message_header)) &&
|
|
(datagram_size == GET_MESSAGE_SIZE(*message)));
|
|
|
|
message_type = (byte)GET_MESSAGE_TYPE(*message);
|
|
if (GET_MESSAGE_FLAGS(*message))
|
|
{
|
|
if (network_game_server_datagram_event_logged())
|
|
{
|
|
network_event(
|
|
"server received a datagram with invalid flags; sender= '%s'",
|
|
transport_address_to_string(source_address));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (message_type)
|
|
{
|
|
case _message_type_packet:
|
|
{
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
short packet_type = ((byte *)message)[datagram_size - 1];
|
|
|
|
datagram_size -= sizeof(word);
|
|
switch (packet_type)
|
|
{
|
|
case _message_client_broadcast_game_search:
|
|
if (network_game_should_accept_remote_connections())
|
|
{
|
|
struct message_client_broadcast_game_search game_search;
|
|
|
|
if (decode_network_game_message(
|
|
&game_search,
|
|
message + 1,
|
|
&datagram_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_search))
|
|
{
|
|
if (!network_game_server_handle_message_client_broadcast_game_search(
|
|
server,
|
|
source_address,
|
|
&game_search))
|
|
{
|
|
network_event(
|
|
"server failed to advertise game to prospective client at '%s'",
|
|
transport_address_to_string(source_address));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to decode a message_client_broadcast_game_search packet");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _message_client_ping:
|
|
if (network_game_should_accept_remote_connections())
|
|
{
|
|
struct message_client_ping ping;
|
|
|
|
if (decode_network_game_message(
|
|
&ping,
|
|
message + 1,
|
|
&datagram_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_search))
|
|
{
|
|
if (!network_game_server_handle_message_client_ping(
|
|
server,
|
|
source_address,
|
|
&ping))
|
|
{
|
|
network_event("server failed to handle a client ping");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to decode a message_client_ping packet");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _message_client_game_update:
|
|
/* (ten times a second from every machine in the game, and
|
|
from those still in it as it ends: one not in the game, or
|
|
not now, is dropped without a line in the log for each) */
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_ingame)
|
|
{
|
|
struct network_game_server_client_machine *client_machine =
|
|
network_game_server_get_client_machine_at_address(
|
|
server,
|
|
source_address->address.long_words[0]);
|
|
|
|
if (client_machine)
|
|
{
|
|
struct message_client_game_update game_update;
|
|
|
|
if (decode_network_game_message(
|
|
&game_update,
|
|
message + 1,
|
|
&datagram_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_ingame))
|
|
{
|
|
network_game_server_client_machine_heard(server, client_machine);
|
|
network_game_server_handle_client_update_packet(
|
|
server,
|
|
client_machine,
|
|
&game_update);
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to decode a message_client_game_update packet");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (network_game_server_datagram_event_logged())
|
|
{
|
|
network_event(
|
|
"server received datagram with an unexpected packet type; sender= '%s'",
|
|
transport_address_to_string(source_address));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _message_type_data:
|
|
/* the distributed netcode's messages (port/linux/NETCODE.md) */
|
|
/* (from a machine in the game, which the lookup finds only among
|
|
those that joined, and only in game: else dropped) */
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_ingame)
|
|
{
|
|
struct network_game_server_client_machine *client_machine =
|
|
network_game_server_get_client_machine_at_address(server, source_address->address.long_words[0]);
|
|
|
|
/* (and has loaded it, as for its reliable messages) */
|
|
if (client_machine && network_game_server_client_machine_is_loaded(server, client_machine))
|
|
{
|
|
long machine_index;
|
|
|
|
network_game_server_client_machine_heard(server, client_machine);
|
|
network_game_server_get_client_machine(server, client_machine, &machine_index);
|
|
network_distributed_handle_message(machine_index, message, datagram_size);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _message_type_error:
|
|
if (!network_game_server_datagram_event_logged())
|
|
{
|
|
/* (logged at most once a second) */
|
|
}
|
|
else if (datagram_size >= MINIMUM_TRANSPORT_ERROR_MESSAGE_SIZE)
|
|
{
|
|
byte *error_message = (byte *)(message + 1);
|
|
|
|
network_event(
|
|
"server received low-level error message: error= #%d (%.*s); sender= '%s'",
|
|
error_message[TRANSPORT_ERROR_MESSAGE_TEXT_LENGTH],
|
|
(int)TRANSPORT_ERROR_MESSAGE_TEXT_LENGTH,
|
|
error_message,
|
|
transport_address_to_string(source_address));
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"server received a malformed/damaged message; sender= '%s'",
|
|
transport_address_to_string(source_address));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (network_game_server_datagram_event_logged())
|
|
{
|
|
network_event(
|
|
"server received a datagram with an unknown message type (#%d); sender= '%s'",
|
|
message_type,
|
|
transport_address_to_string(source_address));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
/* a player a client machine asked to add in game, queued: only one of its
|
|
own (the queue takes the player's machine for the machine that asked) */
|
|
static void network_game_server_queue_client_player(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
struct network_player *player)
|
|
{
|
|
long machine_index;
|
|
|
|
network_game_server_get_client_machine(server, client_machine, &machine_index);
|
|
if (player->machine_index != machine_index)
|
|
{
|
|
network_event("client machine #%ld tried to add a player of another machine", machine_index);
|
|
return;
|
|
}
|
|
network_game_server_clean_player_name(server, player, NONE);
|
|
network_game_server_queue_player_for_addition(server, player);
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_broadcast_game_search(
|
|
struct network_game_server *server,
|
|
struct transport_address *source_address,
|
|
struct message_client_broadcast_game_search *client_message)
|
|
{
|
|
boolean result = TRUE;
|
|
/* port: the advertisement is broadcast (to every internet play peer
|
|
too): one each so often answers every machine searching, and a flood of
|
|
searches no more */
|
|
static unsigned long last_advertised_time = 0;
|
|
unsigned long now = system_milliseconds();
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0x21F,
|
|
server && source_address && client_message);
|
|
if (client_message->version == NETWORK_GAME_MESSAGE_VERSION)
|
|
{
|
|
struct network_game *game = network_game_server_get_game(server);
|
|
|
|
/* (the time taken only by a search answered) */
|
|
if (game && last_advertised_time && now - last_advertised_time < GAME_ADVERTISEMENT_INTERVAL)
|
|
return TRUE;
|
|
if (game)
|
|
{
|
|
struct message_server_game_advertise advertisement = {0};
|
|
struct transport_address address;
|
|
struct network_message *reply;
|
|
|
|
address.address_length = IPV4_ADDRESS_LENGTH;
|
|
address.address.long_words[0] = NONE;
|
|
address.port = NETWORK_GAME_CLIENT_PORT;
|
|
|
|
csmemcpy(
|
|
advertisement.client_nonce,
|
|
client_message->nonce,
|
|
sizeof(client_message->nonce));
|
|
transport_get_nonce(advertisement.nonce, TRANSPORT_NONCE_LENGTH);
|
|
advertisement.key_id = transport_get_key_id();
|
|
advertisement.key = transport_get_key();
|
|
|
|
{
|
|
XNADDR xnaddr;
|
|
|
|
advertisement.xnaddr = *transport_get_xnaddr(&xnaddr);
|
|
}
|
|
|
|
advertisement.port = NETWORK_GAME_SERVER_PORT;
|
|
advertisement.version = NETWORK_GAME_MESSAGE_VERSION;
|
|
advertisement.platform = 0;
|
|
ustrncpy(advertisement.game_name, game->name, NETWORK_GAME_NAME_LENGTH - 1);
|
|
advertisement.engine_type = (short)game->variant.game_engine_index;
|
|
csmemcpy(&advertisement.map, &game->map, sizeof(game->map));
|
|
advertisement.machine_count = game->machine_count;
|
|
advertisement.player_count = game->player_count;
|
|
advertisement.maximum_player_count = game->maximum_players;
|
|
advertisement.variant_setting = (short)game->variant.universal_variant.score_to_win;
|
|
advertisement.flags = 0;
|
|
if (game->variant.universal_variant.teams == TRUE)
|
|
{
|
|
advertisement.flags = FLAG(_game_advertisement_has_teams_bit);
|
|
}
|
|
|
|
if (game->variant.game_engine_index == game_engine_oddball &&
|
|
game->variant.game_engine_variant.oddball.oddball_ball_type == 2)
|
|
{
|
|
advertisement.flags |= FLAG(_game_advertisement_oddball_variant_bit);
|
|
}
|
|
|
|
/* the native builds' network version and netcode (a client
|
|
refuses a host of another version, or of the lockstep netcode
|
|
older builds had: network_client_manager.c) */
|
|
advertisement.reserved[HALO_PORT_ADVERTISED_VERSION_OFFSET] = (byte)(HALO_PORT_NETWORK_VERSION & 0xFF);
|
|
advertisement.reserved[HALO_PORT_ADVERTISED_VERSION_OFFSET + 1] = (byte)(HALO_PORT_NETWORK_VERSION >> 8);
|
|
advertisement.reserved[HALO_PORT_ADVERTISED_FLAGS_OFFSET] =
|
|
HALO_PORT_ADVERTISED_DISTRIBUTED_FLAG;
|
|
if (network_game_server_get_state(server, NULL) != _network_game_server_state_pregame ||
|
|
network_game_server_game_is_loading(server))
|
|
{
|
|
advertisement.reserved[HALO_PORT_ADVERTISED_FLAGS_OFFSET] |= HALO_PORT_ADVERTISED_IN_PROGRESS_FLAG;
|
|
}
|
|
/* (open to joins: not while the machines load the game, nor
|
|
once it is over, nor in progress when no player can join it) */
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_ingame
|
|
? network_game_server_accepts_late_joins(server)
|
|
: network_game_server_game_is_open(server) && network_game_has_free_player_slot(game))
|
|
{
|
|
advertisement.flags |= FLAG(_game_advertisement_open_bit);
|
|
}
|
|
|
|
network_game_generate_join_game_token(advertisement.join_game_token);
|
|
reply = create_network_game_message(
|
|
_message_server_game_advertise,
|
|
&advertisement,
|
|
sizeof(advertisement));
|
|
if (reply)
|
|
{
|
|
word message_size = GET_MESSAGE_SIZE(reply->header);
|
|
struct network_connection *connection =
|
|
network_game_server_get_connection(server);
|
|
|
|
last_advertised_time = now ? now : 1;
|
|
result = network_game_server_write(
|
|
connection,
|
|
reply,
|
|
message_size,
|
|
&address,
|
|
0);
|
|
if (!result)
|
|
{
|
|
network_event(
|
|
"network_game_server_write() failed in handle_message_client_broadcast_game_search()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_server_game_advertise message");
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_ping(
|
|
struct network_game_server *server,
|
|
struct transport_address *source_address,
|
|
struct message_client_ping *client_message)
|
|
{
|
|
struct message_server_pong pong;
|
|
struct network_message *reply;
|
|
boolean result = FALSE;
|
|
/* port: an address answered no more often than PING_REPLY_INTERVAL, and
|
|
no more than MAXIMUM_PING_REPLY_ADDRESSES in that time (the answer goes
|
|
to the port the ping names, at the address it came from, which anyone
|
|
can send one as: a flood of pings would be a flood of answers, at
|
|
another's machine). A searching client pings once a second */
|
|
static struct
|
|
{
|
|
unsigned long address;
|
|
unsigned long time;
|
|
} replied[MAXIMUM_PING_REPLY_ADDRESSES];
|
|
unsigned long now = system_milliseconds();
|
|
short index;
|
|
short slot = NONE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0x26A,
|
|
server && source_address && client_message);
|
|
|
|
for (index = 0; index < MAXIMUM_PING_REPLY_ADDRESSES; index++)
|
|
{
|
|
boolean recent = replied[index].address && now - replied[index].time < PING_REPLY_INTERVAL;
|
|
|
|
if (replied[index].address == source_address->address.long_words[0])
|
|
{
|
|
if (recent)
|
|
return TRUE;
|
|
slot = index;
|
|
break;
|
|
}
|
|
if (!recent && slot == NONE)
|
|
slot = index;
|
|
}
|
|
if (slot == NONE)
|
|
return TRUE;
|
|
replied[slot].address = source_address->address.long_words[0];
|
|
replied[slot].time = now;
|
|
|
|
pong.timestamp = client_message->timestamp;
|
|
reply = create_network_game_message(
|
|
_message_server_pong,
|
|
&pong,
|
|
sizeof(pong));
|
|
if (reply)
|
|
{
|
|
struct transport_address address;
|
|
address.address_length = IPV4_ADDRESS_LENGTH;
|
|
address.address.long_words[0] = source_address->address.long_words[0];
|
|
address.port = client_message->port;
|
|
result = network_game_server_write(
|
|
network_game_server_get_connection(server),
|
|
reply,
|
|
GET_MESSAGE_SIZE(reply->header),
|
|
&address,
|
|
0);
|
|
if (!result)
|
|
{
|
|
network_event(
|
|
"network_game_server_write() failed in handle_message_client_ping()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_server_pong message");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_join_game_request(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *server_client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
boolean result = TRUE;
|
|
/* (or a distributed game in progress: network_game_server_accepts_late_joins) */
|
|
boolean late_join = network_game_server_accepts_late_joins(server);
|
|
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_pregame || late_join)
|
|
{
|
|
struct message_client_join_game_request join_game_request;
|
|
short packet_type = _message_client_join_game_request;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (network_game_server_client_machine_is_joined_to_game(server, server_client_machine))
|
|
{
|
|
network_event("ignoring redundant join request from machine");
|
|
}
|
|
else if (decode_network_game_message(
|
|
&join_game_request,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
struct transport_address source_address;
|
|
wchar_t machine_name[MAXIMUM_MACHINE_NAME_LENGTH];
|
|
/* port: a lobby with no player slot free (its machines' players
|
|
fill it) is full: a machine that joined could add none, and held
|
|
the countdown for ever */
|
|
boolean full = network_game_server_get_state(server, NULL) == _network_game_server_state_pregame &&
|
|
!network_game_has_free_player_slot(network_game_server_get_game(server));
|
|
|
|
/* port: its hardware id, as it tells it: hex only (anything else
|
|
left out), no more than its field; the host logs it, and refuses
|
|
one it banned (network_game_server_accept_client_machine_into_game) */
|
|
{
|
|
char hardware_id[sizeof(join_game_request.hardware_id) + 1];
|
|
|
|
csmemcpy(hardware_id, join_game_request.hardware_id, sizeof(join_game_request.hardware_id));
|
|
hardware_id[sizeof(join_game_request.hardware_id)] = 0;
|
|
network_game_server_set_machine_hardware_id(server_client_machine, hardware_id);
|
|
}
|
|
/* (the name comes from the wire, and need not end, nor be text
|
|
that draws: kept to what does; then in ASCII, for the log) */
|
|
network_game_server_clean_name(join_game_request.machine_name, MAXIMUM_MACHINE_NAME_LENGTH, L"Machine");
|
|
csmemcpy(machine_name, join_game_request.machine_name, sizeof(machine_name));
|
|
wide_to_ascii(
|
|
join_game_request.machine_name,
|
|
(char *)join_game_request.machine_name,
|
|
sizeof(join_game_request.machine_name));
|
|
network_connection_get_address(
|
|
network_game_server_get_client_connection(server_client_machine),
|
|
&source_address,
|
|
FALSE);
|
|
if ((network_game_server_get_state(server, NULL) == _network_game_server_state_pregame || late_join) &&
|
|
network_game_server_game_is_open(server) &&
|
|
!full)
|
|
{
|
|
byte join_game_token[JOIN_GAME_TOKEN_LENGTH];
|
|
|
|
network_game_generate_join_game_token(join_game_token);
|
|
if (!csmemcmp(
|
|
join_game_request.join_game_token,
|
|
join_game_token,
|
|
sizeof(join_game_token)))
|
|
{
|
|
/* (the Xbox game refused a machine not named in the
|
|
development kit's d:\hosts.txt: a stray file of that name
|
|
on a PC refused machines) */
|
|
if (network_game_server_accept_client_machine_into_game(server, server_client_machine))
|
|
{
|
|
struct message_server_machine_accepted acceptance;
|
|
struct network_message *reply;
|
|
struct network_machine *client_machine;
|
|
long machine_index = NONE;
|
|
|
|
client_machine = network_game_server_get_client_machine(
|
|
server,
|
|
server_client_machine,
|
|
&machine_index);
|
|
network_game_server_get_game(server);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\networking\\network_server_message_handler.c",
|
|
0x2CE,
|
|
network_machine_is_valid(client_machine));
|
|
|
|
/* port: a machine joining the game in progress is named
|
|
by its join (its settings request, which names a machine
|
|
in the lobby, is refused in game) */
|
|
if (late_join && client_machine)
|
|
csmemcpy(client_machine->name, machine_name, sizeof(client_machine->name));
|
|
|
|
acceptance.machine_index = (short)machine_index;
|
|
acceptance.random_seed = network_game_get_random_seed();
|
|
reply = create_network_game_message(
|
|
_message_server_machine_accepted,
|
|
&acceptance,
|
|
sizeof(acceptance));
|
|
if (reply)
|
|
{
|
|
word message_size = GET_MESSAGE_SIZE(reply->header);
|
|
struct network_connection *connection =
|
|
network_game_server_get_client_connection(server_client_machine);
|
|
|
|
result = network_game_server_write(
|
|
connection,
|
|
reply,
|
|
message_size,
|
|
NULL,
|
|
1);
|
|
if (!result)
|
|
{
|
|
network_event(
|
|
"network_game_server_write() failed in network_game_server_handle_message_client_join_game_request()");
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"sent _message_type_server_machine_accepted message to %d",
|
|
machine_index);
|
|
}
|
|
|
|
if (result == TRUE)
|
|
{
|
|
/* (a machine joining the game in progress: to it alone,
|
|
the others are in game) */
|
|
if (late_join)
|
|
{
|
|
result = network_game_server_send_game_settings_to_client_machine(server,
|
|
server_client_machine, network_game_server_get_game(server),
|
|
sizeof(struct network_game));
|
|
}
|
|
/* (to every machine: one the write failed for is
|
|
closed, which is not this machine's failure) */
|
|
else if (!network_game_server_send_game_data_pregame(server))
|
|
{
|
|
network_event(
|
|
"network_game_server_send_game_data_pregame() failed in network_game_server_handle_message_client_join_game_request()");
|
|
}
|
|
if (!result)
|
|
{
|
|
network_event(
|
|
"network_game_server_send_game_settings_to_client_machine() failed in network_game_server_handle_message_client_join_game_request()");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct message_server_machine_rejected rejection;
|
|
struct network_message *reply;
|
|
|
|
rejection.reason = _network_game_server_rejection_reason_game_not_open;
|
|
network_event(
|
|
"server failed to accept valid client machine '%s' @%s into the game",
|
|
join_game_request.machine_name,
|
|
transport_address_to_string(&source_address));
|
|
reply = create_network_game_message(
|
|
_message_server_machine_rejected,
|
|
&rejection,
|
|
sizeof(rejection));
|
|
if (reply)
|
|
{
|
|
word message_size = GET_MESSAGE_SIZE(reply->header);
|
|
struct network_connection *connection =
|
|
network_game_server_get_client_connection(server_client_machine);
|
|
|
|
if (!network_game_server_write(
|
|
connection,
|
|
reply,
|
|
message_size,
|
|
NULL,
|
|
1))
|
|
{
|
|
network_event(
|
|
"network_game_server_write() failed while sending a rejection reply");
|
|
}
|
|
}
|
|
|
|
result = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct message_server_machine_rejected rejection;
|
|
struct network_message *reply;
|
|
|
|
rejection.reason = _network_game_server_rejection_reason_bad_join_token;
|
|
network_event(
|
|
"client machine '%s' @%s tried to join game with a bad join token",
|
|
join_game_request.machine_name,
|
|
transport_address_to_string(&source_address));
|
|
reply = create_network_game_message(
|
|
_message_server_machine_rejected,
|
|
&rejection,
|
|
sizeof(rejection));
|
|
if (reply)
|
|
{
|
|
word message_size = GET_MESSAGE_SIZE(reply->header);
|
|
struct network_connection *connection =
|
|
network_game_server_get_client_connection(server_client_machine);
|
|
|
|
if (!network_game_server_write(
|
|
connection,
|
|
reply,
|
|
message_size,
|
|
NULL,
|
|
1))
|
|
{
|
|
network_event(
|
|
"network_game_server_write() failed while sending a rejection reply");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_server_machine_rejected message");
|
|
}
|
|
|
|
result = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct message_server_machine_rejected rejection;
|
|
struct network_message *reply;
|
|
|
|
rejection.reason = full ? _rejection_code_game_is_full : _network_game_server_rejection_reason_game_not_open;
|
|
network_event(
|
|
"client machine '%s' @%s tried to join game when they should not be",
|
|
join_game_request.machine_name,
|
|
transport_address_to_string(&source_address));
|
|
reply = create_network_game_message(
|
|
_message_server_machine_rejected,
|
|
&rejection,
|
|
sizeof(rejection));
|
|
if (reply)
|
|
{
|
|
word message_size = GET_MESSAGE_SIZE(reply->header);
|
|
struct network_connection *connection =
|
|
network_game_server_get_client_connection(server_client_machine);
|
|
|
|
if (!network_game_server_write(
|
|
connection,
|
|
reply,
|
|
message_size,
|
|
NULL,
|
|
1))
|
|
{
|
|
network_event(
|
|
"network_game_server_write() failed while sending a rejection reply");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("failed to create a message_server_machine_rejected message");
|
|
}
|
|
|
|
result = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_join_game_request packet");
|
|
result = FALSE;
|
|
}
|
|
}
|
|
else if (!network_game_server_client_machine_is_joined_to_game(server, server_client_machine))
|
|
{
|
|
/* port: a game that cannot be joined now (full, loading, over) says
|
|
so, rather than leaving the machine to time out */
|
|
struct message_server_machine_rejected rejection;
|
|
struct network_message *reply;
|
|
|
|
rejection.reason = network_game_server_get_state(server, NULL) == _network_game_server_state_ingame &&
|
|
network_game_server_game_is_open(server) ?
|
|
_rejection_code_game_is_full : _network_game_server_rejection_reason_game_not_open;
|
|
reply = create_network_game_message(_message_server_machine_rejected, &rejection, sizeof(rejection));
|
|
if (reply)
|
|
{
|
|
network_game_server_write(network_game_server_get_client_connection(server_client_machine), reply,
|
|
GET_MESSAGE_SIZE(reply->header), NULL, 1);
|
|
}
|
|
result = FALSE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_add_player_request_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
/* (port: not once the game has started: every machine loads it from
|
|
the settings sent with the start) */
|
|
if (network_game_server_lobby_is_open(server))
|
|
{
|
|
struct network_player player;
|
|
short packet_type = _message_client_add_player_request_pregame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&player,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
/* (port: its name kept to text that draws, as every name from
|
|
the wire, and told apart from the other players') */
|
|
network_game_server_clean_player_name(server, &player, NONE);
|
|
if (network_game_server_add_player_to_game(server, client_machine, &player))
|
|
{
|
|
if (!network_game_server_send_game_data_pregame(server))
|
|
{
|
|
network_event(
|
|
"server failed to send pregame game data in network_game_server_handle_message_client_add_player_request_pregame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"server failed to add a network player in network_game_server_handle_message_client_add_player_request_pregame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_add_player_request_pregame packet");
|
|
}
|
|
}
|
|
/* a machine joining the game in progress: its players are added as in
|
|
game (network_game_server_start_late_joiner then starts it; one that
|
|
cannot, the game full, is refused there) */
|
|
else if (network_game_server_get_state(server, NULL) == _network_game_server_state_ingame &&
|
|
!network_game_server_client_machine_is_loaded(server, client_machine))
|
|
{
|
|
struct network_player player;
|
|
short packet_type = _message_client_add_player_request_pregame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&player,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
network_game_server_queue_client_player(server, client_machine, &player);
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_add_player_request_pregame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_add_player_request_pregame because the server is not in pregame");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_remove_player_request_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
/* (port: not once the game has started: every machine loads it from
|
|
the settings sent with the start) */
|
|
if (network_game_server_lobby_is_open(server))
|
|
{
|
|
struct network_player player;
|
|
short packet_type = _message_client_remove_player_request_pregame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&player,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
if (network_game_server_remove_player_from_game(server, client_machine, &player))
|
|
{
|
|
if (!network_game_server_send_game_data_pregame(server))
|
|
{
|
|
network_event(
|
|
"server failed to send pregame game data in network_game_server_handle_message_client_remove_player_request_pregame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"server failed to remove a network player in network_game_server_handle_message_client_remove_player_request_pregame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_remove_player_request_pregame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_remove_player_request_pregame because the server is not in pregame");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_settings_request(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
/* (port: not once the game has started: every machine loads it from
|
|
the settings sent with the start) */
|
|
if (network_game_server_lobby_is_open(server))
|
|
{
|
|
struct network_machine machine_settings;
|
|
short packet_type = _message_client_settings_request;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&machine_settings,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
/* (the name comes from the wire, and need not end, nor be text
|
|
that draws: kept to what does) */
|
|
network_game_server_clean_name(machine_settings.name, NUMBEROF(machine_settings.name), L"Machine");
|
|
if (network_game_server_adjust_machine_settings(server, client_machine, &machine_settings))
|
|
{
|
|
network_event(
|
|
"server received machine settings for machine #%d/'%s'",
|
|
machine_settings.machine_index,
|
|
wide_to_ascii(
|
|
machine_settings.name,
|
|
(char *)machine_settings.name,
|
|
sizeof(machine_settings.name)));
|
|
if (!network_game_server_send_game_data_pregame(server))
|
|
{
|
|
network_event(
|
|
"server failed to send pregame game data in network_game_server_handle_message_client_settings_request()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"network_game_server_adjust_machine_settings() failed in network_game_server_handle_message_client_settings_request()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_settings_request packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_settings_request because the server is not in pregame");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_player_settings_request(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
/* (port: not once the game has started: every machine loads it from
|
|
the settings sent with the start) */
|
|
if (network_game_server_lobby_is_open(server))
|
|
{
|
|
struct network_player player;
|
|
short packet_type = _message_client_player_settings_request;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&player,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
struct network_game *game = network_game_server_get_game(server);
|
|
long machine_index;
|
|
|
|
/* port: a machine's own player, of its slot (the settings come
|
|
from the wire, and the Xbox game took any machine's, and wrote a
|
|
slot out of the list over the machines), with a name that ends,
|
|
and on a team of the game's: else the one the host has it on */
|
|
network_game_server_get_client_machine(server, client_machine, &machine_index);
|
|
network_game_server_clean_player_name(server, &player,
|
|
VALID_INDEX(player.player_list_index, MAXIMUM_NUMBER_OF_PLAYERS) ? player.player_list_index : NONE);
|
|
if (VALID_INDEX(player.player_list_index, MAXIMUM_NUMBER_OF_PLAYERS) &&
|
|
(!game->variant.universal_variant.teams ||
|
|
!VALID_INDEX(player.team_index, NUMBER_OF_MULTIPLAYER_TEAMS)))
|
|
{
|
|
player.team_index = game->players[player.player_list_index].team_index;
|
|
}
|
|
if (player.machine_index != machine_index)
|
|
{
|
|
network_event(
|
|
"client machine #%ld tried to update a player of another machine",
|
|
machine_index);
|
|
}
|
|
else if (network_game_update_player(game, &player))
|
|
{
|
|
network_event("server received updated player settings");
|
|
if (!network_game_server_send_game_data_pregame(server))
|
|
{
|
|
network_event(
|
|
"server failed to send pregame game data in network_game_server_handle_message_client_player_settings_request()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"network_game_update_player() failed in network_game_server_handle_message_client_player_settings_request()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_player_settings_request packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_player_settings_request because the server is not in pregame");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_game_start_request(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_pregame)
|
|
{
|
|
struct message_client_game_start_request game_start_request;
|
|
short packet_type = _message_client_game_start_request;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&game_start_request,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
/* (a short on the wire: the rest of the long is not written) */
|
|
short countdown_event = (short)game_start_request.countdown_time;
|
|
|
|
/* port: the pregame screen's faster and slower, from any machine;
|
|
to stop the countdown or start at once (a script's), the host's own
|
|
(the Xbox game took any event from any machine, and a machine
|
|
could hold the lobby or skip the countdown) */
|
|
if ((countdown_event == _network_game_server_countdown_event_player_left &&
|
|
network_game_server_client_machine_may_slow_countdown(server, client_machine)) ||
|
|
countdown_event == _network_game_server_countdown_event_player_joined ||
|
|
((countdown_event == _network_game_server_countdown_event_stop ||
|
|
countdown_event == _network_game_server_countdown_event_start_immediately) &&
|
|
network_game_server_client_machine_is_local(server, client_machine)))
|
|
{
|
|
network_game_server_update_countdown(server, countdown_event);
|
|
}
|
|
else
|
|
{
|
|
network_event("ignoring a game start request (#%d) from a client machine", countdown_event);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_game_start_request packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_game_start_request because the server is not in pregame");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_graceful_game_exit_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_pregame)
|
|
{
|
|
struct message_client_graceful_game_exit_pregame graceful_game_exit;
|
|
short packet_type = _message_client_graceful_game_exit_pregame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&graceful_game_exit,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
struct network_machine *machine = network_game_server_get_client_machine(server, client_machine, NULL);
|
|
|
|
/* (port: none for a machine without a slot) */
|
|
if (machine && network_game_server_remove_machine_from_game(server, machine))
|
|
{
|
|
/* (not to machines loading the game) */
|
|
if (network_game_server_lobby_is_open(server) &&
|
|
!network_game_server_send_game_data_pregame(server))
|
|
{
|
|
network_event(
|
|
"server failed to send pregame game data in network_game_server_handle_message_client_graceful_game_exit_pregame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"network_game_server_remove_machine_from_game() failed in network_game_server_handle_message_client_graceful_game_exit_pregame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_graceful_game_exit_pregame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_graceful_game_exit_pregame message because the server is not in pregame");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_map_is_precached_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_pregame)
|
|
{
|
|
struct message_client_map_is_precached_pregame map_is_precached;
|
|
short packet_type = _message_client_map_is_precached_pregame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&map_is_precached,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_pregame))
|
|
{
|
|
network_game_server_client_machine_is_precached(
|
|
server,
|
|
client_machine,
|
|
map_is_precached.map_name);
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_type_client_map_is_precached_pregame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_type_client_map_is_precached_pregame because the server is not in pregame");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_loaded(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_pregame)
|
|
{
|
|
struct message_client_loaded loaded;
|
|
short packet_type = _message_client_loaded;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&loaded,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_ingame))
|
|
{
|
|
network_game_server_client_machine_game_loading_complete(server, client_machine);
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_loaded packet");
|
|
result = FALSE;
|
|
}
|
|
}
|
|
/* a machine that joined the game in progress has loaded it (or it
|
|
loaded it as the game ended: not a reason to drop it) */
|
|
else if ((network_game_server_get_state(server, NULL) == _network_game_server_state_ingame &&
|
|
!network_game_server_client_machine_is_loaded(server, client_machine)) ||
|
|
network_game_server_get_state(server, NULL) == _network_game_server_state_postgame)
|
|
{
|
|
network_game_server_late_joiner_loaded(server, client_machine);
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_loaded message because the server is not in pregame");
|
|
result = FALSE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_add_player_request_ingame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_ingame)
|
|
{
|
|
struct network_player player;
|
|
short packet_type = _message_client_add_player_request_ingame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&player,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_ingame))
|
|
{
|
|
network_game_server_queue_client_player(server, client_machine, &player);
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_add_player_request_ingame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_add_player_request_ingame because the server is not in game");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_remove_player_request_ingame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_ingame)
|
|
{
|
|
struct network_player player;
|
|
short packet_type = _message_client_remove_player_request_ingame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&player,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_ingame))
|
|
{
|
|
/* port: only a player of the machine that asks (the Xbox game
|
|
removed any machine's) */
|
|
player.name[NUMBEROF(player.name) - 1] = 0;
|
|
if (network_game_server_remove_player_from_game(server, client_machine, &player))
|
|
{
|
|
struct message_server_remove_player_ingame remove_player;
|
|
void *encoded_message;
|
|
|
|
remove_player.player = player;
|
|
remove_player.game_time = game_time_get() + REMOVE_PLAYER_INGAME_GAME_TIME_DELAY;
|
|
encoded_message = create_network_game_message(
|
|
_message_server_remove_player_ingame,
|
|
&remove_player,
|
|
sizeof(remove_player));
|
|
/* (to every machine: one the write failed for is closed,
|
|
which is not this machine's failure) */
|
|
if (encoded_message &&
|
|
!network_game_server_send_message_to_all_machines(server, encoded_message))
|
|
{
|
|
network_event(
|
|
"network_game_server_send_message_to_all_machines() failed in network_game_server_handle_message_client_remove_player_request_ingame()");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_remove_player_request_ingame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_remove_player_request_ingame because the server is not in game");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_remove_player_request_postgame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_postgame)
|
|
{
|
|
struct network_player player;
|
|
short packet_type = _message_client_remove_player_request_postgame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&player,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_postgame))
|
|
{
|
|
if (!network_game_server_remove_player_from_game(server, client_machine, &player))
|
|
{
|
|
network_event("server failed to remove a network player post-game");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_remove_player_request_postgame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_remove_player_request_postgame because the server is not in post-game");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean network_game_server_handle_message_client_switch_to_pregame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_postgame)
|
|
{
|
|
struct message_client_switch_to_pregame switch_to_pregame;
|
|
short packet_type = _message_client_switch_to_pregame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&switch_to_pregame,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_postgame))
|
|
{
|
|
result = network_game_server_switch_machine_from_postgame_to_pregame(server, client_machine);
|
|
if (!result)
|
|
{
|
|
network_event("network_game_server_switch_machine_from_postgame_to_pregame() failed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_remove_player_request_postgame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_switch_to_pregame because the server is not in post-game");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* port: a machine leaving the game's scores, as one leaving the lobby does
|
|
(network_game_server_handle_message_client_graceful_game_exit_pregame) */
|
|
static boolean network_game_server_handle_message_client_graceful_game_exit_postgame(
|
|
struct network_game_server *server,
|
|
struct network_game_server_client_machine *client_machine,
|
|
word *message,
|
|
short message_size)
|
|
{
|
|
if (network_game_server_get_state(server, NULL) == _network_game_server_state_postgame)
|
|
{
|
|
struct message_client_graceful_game_exit_postgame graceful_game_exit;
|
|
short packet_type = _message_client_graceful_game_exit_postgame;
|
|
short packet_version = NETWORK_GAME_MESSAGE_VERSION;
|
|
|
|
message_size -= sizeof(word);
|
|
if (decode_network_game_message(
|
|
&graceful_game_exit,
|
|
message + 1,
|
|
&message_size,
|
|
&packet_type,
|
|
&packet_version,
|
|
_network_game_packet_class_client_postgame))
|
|
{
|
|
struct network_machine *machine = network_game_server_get_client_machine(server, client_machine, NULL);
|
|
|
|
if (!machine || !network_game_server_remove_machine_from_game(server, machine))
|
|
{
|
|
network_event(
|
|
"network_game_server_remove_machine_from_game() failed in network_game_server_handle_message_client_graceful_game_exit_postgame()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event("server failed to decode a message_client_graceful_game_exit_postgame packet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
network_event(
|
|
"failed to handle a message_client_graceful_game_exit_postgame message because the server is not in post-game");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|