X-Git-Url: http://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=blobdiff_plain;f=src%2Fgame%2Fg_netmsg.pas;h=55b99b3705acbc0ddbe05e00b04ba3657ea2c9fd;hp=a5bd880c0e817a6c91756c18377b6d94699c26f7;hb=4204edd3c7df01198a2289af4896be0575fff15c;hpb=2257ac7163318bab560e4351f2b139c9ca4ed9e5 diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas index a5bd880..55b99b3 100644 --- a/src/game/g_netmsg.pas +++ b/src/game/g_netmsg.pas @@ -1,9 +1,8 @@ -(* Copyright (C) DooM 2D:Forever Developers +(* Copyright (C) Doom 2D: Forever Developers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * the Free Software Foundation, version 3 of the License ONLY. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -13,12 +12,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *) -{$MODE DELPHI} +{$INCLUDE ../shared/a_modes.inc} unit g_netmsg; interface -uses g_net, g_triggers, Classes, SysUtils, md5; +uses e_msg, g_net, g_triggers, Classes, SysUtils, md5; const NET_MSG_INFO = 100; @@ -32,6 +31,7 @@ const NET_MSG_FLAG = 107; NET_MSG_REQFST = 108; NET_MSG_GSET = 109; + NET_MSG_FLAGPOS= 110; NET_MSG_PLR = 111; NET_MSG_PLRPOS = 112; @@ -45,6 +45,7 @@ const NET_MSG_ISPAWN = 121; NET_MSG_IDEL = 122; + NET_MSG_IPOS = 123; NET_MSG_MSPAWN = 131; NET_MSG_MPOS = 132; @@ -67,10 +68,12 @@ const NET_MSG_TIME_SYNC = 194; NET_MSG_VOTE_EVENT = 195; + { NET_MSG_MAP_REQUEST = 201; NET_MSG_MAP_RESPONSE = 202; NET_MSG_RES_REQUEST = 203; NET_MSG_RES_RESPONSE = 204; + } NET_CHAT_SYSTEM = 0; NET_CHAT_PLAYER = 1; @@ -109,6 +112,9 @@ const NET_EV_LMS_DRAW = 16; NET_EV_KILLCOMBO = 17; NET_EV_PLAYER_TOUCH = 18; + NET_EV_SECRET = 19; + NET_EV_INTER_READY = 20; + NET_EV_LMS_NOSPAWN = 21; NET_VE_STARTED = 1; NET_VE_PASSED = 2; @@ -124,25 +130,30 @@ const NET_CHEAT_SUICIDE = 1; NET_CHEAT_SPECTATE = 2; + NET_CHEAT_READY = 3; + NET_CHEAT_DROPFLAG = 4; NET_MAX_DIFFTIME = 5000 div 36; // HOST MESSAGES -procedure MH_RECV_Info(C: pTNetClient; P: Pointer); -procedure MH_RECV_Chat(C: pTNetClient; P: Pointer); -procedure MH_RECV_FullStateRequest(C: pTNetClient; P: Pointer); -function MH_RECV_PlayerPos(C: pTNetClient; P: Pointer): Word; -procedure MH_RECV_PlayerSettings(C: pTNetClient; P: Pointer); -procedure MH_RECV_CheatRequest(C: pTNetClient; P: Pointer); -procedure MH_RECV_RCONPassword(C: pTNetClient; P: Pointer); -procedure MH_RECV_RCONCommand(C: pTNetClient; P: Pointer); -procedure MH_RECV_MapRequest(C: pTNetClient; P: Pointer); -procedure MH_RECV_ResRequest(C: pTNetClient; P: Pointer); -procedure MH_RECV_Vote(C: pTNetClient; P: Pointer); +procedure MH_MalformedPacket(C: pTNetClient); +procedure MH_ProcessFirstSpawn (C: pTNetClient); + +procedure MH_RECV_Info(C: pTNetClient; var M: TMsg); +procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg); +procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg); +function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word; +procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg); +procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg); +procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg); +procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg); +//procedure MH_RECV_MapRequest(C: pTNetClient; var M: TMsg); +//procedure MH_RECV_ResRequest(C: pTNetClient; var M: TMsg); +procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg); // GAME -procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE); +procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NET_EVERYONE}); procedure MH_SEND_Info(ID: Byte); procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE); procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE); @@ -154,6 +165,7 @@ procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE); procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE); procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE); procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE); +procedure MH_SEND_FlagPos(Flag: Byte; ID: Integer = NET_EVERYONE); procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE); // PLAYER procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE); @@ -167,9 +179,10 @@ procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_ // ITEM procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE); procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE); +procedure MH_SEND_ItemPos(IID: Word; ID: Integer = NET_EVERYONE); // PANEL -procedure MH_SEND_PanelTexture(PType: Word; PID: LongWord; AnimLoop: Byte; ID: Integer = NET_EVERYONE); -procedure MH_SEND_PanelState(PType: Word; PID: LongWord; ID: Integer = NET_EVERYONE); +procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE); +procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE); // MONSTER procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE); procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE); @@ -189,45 +202,47 @@ procedure MH_SEND_VoteEvent(EvType: Byte; // CLIENT MESSAGES // // GAME -procedure MC_RECV_Chat(P: Pointer); -procedure MC_RECV_Effect(P: Pointer); -procedure MC_RECV_Sound(P: Pointer); -procedure MC_RECV_GameStats(P: Pointer); -procedure MC_RECV_CoopStats(P: Pointer); -procedure MC_RECV_GameEvent(P: Pointer); -procedure MC_RECV_FlagEvent(P: Pointer); -procedure MC_RECV_GameSettings(P: Pointer); +procedure MC_RECV_Chat(var M: TMsg); +procedure MC_RECV_Effect(var M: TMsg); +procedure MC_RECV_Sound(var M: TMsg); +procedure MC_RECV_GameStats(var M: TMsg); +procedure MC_RECV_CoopStats(var M: TMsg); +procedure MC_RECV_GameEvent(var M: TMsg); +procedure MC_RECV_FlagEvent(var M: TMsg); +procedure MC_RECV_FlagPos(var M: TMsg); +procedure MC_RECV_GameSettings(var M: TMsg); // PLAYER -function MC_RECV_PlayerCreate(P: Pointer): Word; -function MC_RECV_PlayerPos(P: Pointer): Word; -function MC_RECV_PlayerStats(P: Pointer): Word; -function MC_RECV_PlayerDelete(P: Pointer): Word; -function MC_RECV_PlayerDamage(P: Pointer): Word; -function MC_RECV_PlayerDeath(P: Pointer): Word; -function MC_RECV_PlayerFire(P: Pointer): Word; -procedure MC_RECV_PlayerSettings(P: Pointer); +function MC_RECV_PlayerCreate(var M: TMsg): Word; +function MC_RECV_PlayerPos(var M: TMsg): Word; +function MC_RECV_PlayerStats(var M: TMsg): Word; +function MC_RECV_PlayerDelete(var M: TMsg): Word; +function MC_RECV_PlayerDamage(var M: TMsg): Word; +function MC_RECV_PlayerDeath(var M: TMsg): Word; +function MC_RECV_PlayerFire(var M: TMsg): Word; +procedure MC_RECV_PlayerSettings(var M: TMsg); // ITEM -procedure MC_RECV_ItemSpawn(P: Pointer); -procedure MC_RECV_ItemDestroy(P: Pointer); +procedure MC_RECV_ItemSpawn(var M: TMsg); +procedure MC_RECV_ItemDestroy(var M: TMsg); +procedure MC_RECV_ItemPos(var M: TMsg); // PANEL -procedure MC_RECV_PanelTexture(P: Pointer); -procedure MC_RECV_PanelState(P: Pointer); +procedure MC_RECV_PanelTexture(var M: TMsg); +procedure MC_RECV_PanelState(var M: TMsg); // MONSTER -procedure MC_RECV_MonsterSpawn(P: Pointer); -procedure MC_RECV_MonsterPos(P: Pointer); -procedure MC_RECV_MonsterState(P: Pointer); -procedure MC_RECV_MonsterShot(P: Pointer); -procedure MC_RECV_MonsterDelete(P: Pointer); +procedure MC_RECV_MonsterSpawn(var M: TMsg); +procedure MC_RECV_MonsterPos(var M: TMsg); +procedure MC_RECV_MonsterState(var M: TMsg); +procedure MC_RECV_MonsterShot(var M: TMsg); +procedure MC_RECV_MonsterDelete(var M: TMsg); // SHOT -procedure MC_RECV_CreateShot(P: Pointer); -procedure MC_RECV_UpdateShot(P: Pointer); -procedure MC_RECV_DeleteShot(P: Pointer); +procedure MC_RECV_CreateShot(var M: TMsg); +procedure MC_RECV_UpdateShot(var M: TMsg); +procedure MC_RECV_DeleteShot(var M: TMsg); // TRIGGER -procedure MC_RECV_TriggerSound(P: Pointer); -procedure MC_RECV_TriggerMusic(P: Pointer); +procedure MC_RECV_TriggerSound(var M: TMsg); +procedure MC_RECV_TriggerMusic(var M: TMsg); // MISC -procedure MC_RECV_TimeSync(P: Pointer); -procedure MC_RECV_VoteEvent(P: Pointer); +procedure MC_RECV_TimeSync(var M: TMsg); +procedure MC_RECV_VoteEvent(var M: TMsg); // SERVICE procedure MC_SEND_Info(Password: string); procedure MC_SEND_Chat(Txt: string; Mode: Byte); @@ -239,8 +254,9 @@ procedure MC_SEND_RCONPassword(Password: string); procedure MC_SEND_RCONCommand(Cmd: string); procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a'); // DOWNLOAD -procedure MC_SEND_MapRequest(); -procedure MC_SEND_ResRequest(const resName: AnsiString); +//procedure MC_SEND_MapRequest(); +//procedure MC_SEND_ResRequest(const resName: AnsiString); + type TExternalResourceInfo = record @@ -261,52 +277,94 @@ type ExternalResources: array of TExternalResourceInfo; end; -function MapDataFromMsgStream(msgStream: TMemoryStream):TMapDataMsg; -function ResDataFromMsgStream(msgStream: TMemoryStream):TResDataMsg; +function IsValidFileName(const S: String): Boolean; +function IsValidFilePath(const S: String): Boolean; + implementation uses - Math, ENet, e_input, e_fixedbuffer, e_graphics, e_log, + Math, ENet, e_input, e_graphics, e_log, g_textures, g_gfx, g_sound, g_console, g_basic, g_options, g_main, g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys, g_gui, g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF; const - NET_KEY_LEFT = 1; - NET_KEY_RIGHT = 2; - NET_KEY_UP = 4; - NET_KEY_DOWN = 8; - NET_KEY_JUMP = 16; - NET_KEY_FIRE = 32; - NET_KEY_OPEN = 64; - NET_KEY_NW = 256; - NET_KEY_PW = 512; - NET_KEY_CHAT = 2048; - NET_KEY_FORCEDIR = 4096; + NET_KEY_LEFT = 1 shl 0; + NET_KEY_RIGHT = 1 shl 1; + NET_KEY_UP = 1 shl 2; + NET_KEY_DOWN = 1 shl 3; + NET_KEY_JUMP = 1 shl 4; + NET_KEY_FIRE = 1 shl 5; + NET_KEY_OPEN = 1 shl 6; + NET_KEY_CHAT = 1 shl 7; + NET_KEY_FORCEDIR = 1 shl 8; //var //kBytePrev: Word = 0; //kDirPrev: TDirection = D_LEFT; //HostGameTime: Word = 0; + +function IsValidFileName(const S: String): Boolean; +const + Forbidden: set of Char = ['<', '>', '|', '"', ':', '*', '?']; +var + I: Integer; +begin + Result := S <> ''; + for I := 1 to Length(S) do + Result := Result and (not(S[I] in Forbidden)); +end; + +function IsValidFilePath(const S: String): Boolean; +var + I: Integer; +begin + Result := False; + if not IsValidFileName(S) then exit; + if FileExists(S) then exit; + I := LastDelimiter('\/', S); + if (I > 0) then + if (not DirectoryExists(Copy(S, 1, I-1))) then + exit; + Result := True; +end; + + // HOST MESSAGES // // GAME -procedure MH_RECV_Chat(C: pTNetClient; P: Pointer); +procedure MH_MalformedPacket(C: pTNetClient); +begin + g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] + + _lc[I_NET_DISC_PROTOCOL]); + enet_peer_disconnect(C^.Peer, NET_DISC_PROTOCOL); +end; + +procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg); var Txt: string; Mode: Byte; PID: Word; Pl: TPlayer; + Err: Boolean; begin PID := C^.Player; Pl := g_Player_Get(PID); - Txt := e_Raw_Read_String(P); - Mode := e_Raw_Read_Byte(P); + Err := False; + try + Txt := M.ReadString(); + Mode := M.ReadByte(); + except + Err := True; + end; + + if Err then begin MH_MalformedPacket(C); Exit; end; + if (Mode = NET_CHAT_SYSTEM) then Mode := NET_CHAT_PLAYER; // prevent sending system messages from clients if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then @@ -318,22 +376,38 @@ begin MH_SEND_Chat(Pl.Name + ': ' + Txt, Mode, IfThen(Mode = NET_CHAT_TEAM, Pl.Team, NET_EVERYONE)); end; -procedure MH_RECV_Info(C: pTNetClient; P: Pointer); +procedure MH_RECV_Info(C: pTNetClient; var M: TMsg); var Ver, PName, Model, Pw: string; R, G, B, T: Byte; + WeapSwitch: Byte; + TmpPrefArray: Array [WP_FIRST .. WP_LAST + 1] of Byte; + SwitchEmpty: Byte; PID: Word; Color: TRGB; I: Integer; + Err: Boolean; begin - Ver := e_Raw_Read_String(P); - Pw := e_Raw_Read_String(P); - PName := e_Raw_Read_String(P); - Model := e_Raw_Read_String(P); - R := e_Raw_Read_Byte(P); - G := e_Raw_Read_Byte(P); - B := e_Raw_Read_Byte(P); - T := e_Raw_Read_Byte(P); + Err := False; + try + Ver := M.ReadString(); + Pw := M.ReadString(); + PName := M.ReadString(); + Model := M.ReadString(); + R := M.ReadByte(); + G := M.ReadByte(); + B := M.ReadByte(); + T := M.ReadByte(); + WeapSwitch := M.ReadByte(); + if (WeapSwitch = 2) then + for I := WP_FIRST to WP_LAST + 1 do + TmpPrefArray[I] := M.ReadByte(); + SwitchEmpty := M.ReadByte(); + except + Err := True; + end; + + if Err then begin MH_MalformedPacket(C); Exit; end; if Ver <> GAME_VERSION then begin @@ -369,6 +443,13 @@ begin Exit; end; + if (C^.Player <> 0) then + begin + // already received info + g_Net_Penalize(C, 'client info spam'); + Exit; + end; + Color.R := R; Color.B := B; Color.G := G; @@ -377,14 +458,20 @@ begin with g_Player_Get(PID) do begin Name := PName; + WeapSwitchMode := WeapSwitch; + if (WeapSwitch = 2) then + SetWeaponPrefs(TmpPrefArray); + SwitchToEmpty := SwitchEmpty; Reset(True); end; C^.Player := PID; + C^.WaitForFirstSpawn := false; + C^.AuthTime := 0; g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True); e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) + - '] connected. Assigned player #' + IntToStr(PID) + '.', MSG_NOTIFY); + '] connected. Assigned player #' + IntToStr(PID) + '.', TMsgType.Notify); MH_SEND_Info(C^.ID); @@ -393,54 +480,115 @@ begin Name := PName; FClientID := C^.ID; // round in progress, don't spawn - if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then - begin - Lives := 0; - FNoRespawn := True; - Spectate; - FWantsInGame := True; // TODO: look into this later - end - else - Respawn(gGameSettings.GameType = GT_SINGLE); + e_LogWritefln('*** client #%u (cid #%u) authenticated...', [C.ID, C.Player]); + //e_LogWritefln('spawning player with pid #%u...', [PID]); + //Respawn(gGameSettings.GameType = GT_SINGLE); + //k8: no, do not spawn a player yet, wait for "request full state" packet + Lives := 0; + Spectate; + FNoRespawn := True; + // `FWantsInGame` seems to mean "spawn the player on the next occasion". + // that is, if we'll set it to `true`, the player can be spawned after + // warmup time ran out, for example, regardless of the real player state. + // also, this seems to work only for the initial connection. further + // map changes could initiate resource downloading, but the player will + // be spawned immediately. + // the proper solution will require another player state, "ephemeral". + // the player should start any map in "ephemeral" state, and turned into + // real mobj only when they sent a special "i am ready" packet. this packet + // must be sent after receiving the full state, so the player will get a full + // map view before going into game. + FWantsInGame := false; + C^.WaitForFirstSpawn := true; end; - for I := Low(NetClients) to High(NetClients) do + //if not C^.WaitForFirstSpawn then begin - if NetClients[I].ID = C^.ID then Continue; - MH_SEND_PlayerCreate(PID, NetClients[I].ID); - MH_SEND_PlayerPos(True, PID, NetClients[I].ID); - MH_SEND_PlayerStats(PID, NetClients[I].ID); + for I := Low(NetClients) to High(NetClients) do + begin + if NetClients[I].ID = C^.ID then Continue; + MH_SEND_PlayerCreate(PID, NetClients[I].ID); + MH_SEND_PlayerPos(True, PID, NetClients[I].ID); + MH_SEND_PlayerStats(PID, NetClients[I].ID); + end; end; if gState in [STATE_INTERCUSTOM, STATE_FOLD] then MH_SEND_GameEvent(NET_EV_MAPEND, 0, 'N', C^.ID); - if NetUseMaster then g_Net_Slist_Update; + if NetUseMaster then + begin + //g_Net_Slist_Update; + g_Net_Slist_Pulse(); + end; end; -procedure MH_RECV_FullStateRequest(C: pTNetClient; P: Pointer); + +procedure MH_ProcessFirstSpawn (C: pTNetClient); +var + plr: TPlayer; +begin + if not C.WaitForFirstSpawn then exit; + plr := g_Player_Get(C^.Player); + if not assigned(plr) then exit; + g_Net_Slist_ServerPlayerComes(); + e_LogWritefln('*** client #%u (cid #%u) first spawn', [C.ID, C.Player]); + C.WaitForFirstSpawn := false; + plr.FNoRespawn := false; + plr.FWantsInGame := true; // TODO: look into this later + + if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then + begin + plr.Spectate; + MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, 0, 'N', C.ID); + end + else + begin + plr.Respawn(False); + if gLMSRespawn > LMS_RESPAWN_NONE then + MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime, 'N', C.ID); + end; +end; + + +procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg); begin + //e_LogWritefln('*** client #%u (cid #%u) full state request', [C.ID, C.Player]); if gGameOn then + begin MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID) + end else + begin C^.RequestedFullUpdate := True; + end; end; // PLAYER -function MH_RECV_PlayerPos(C: pTNetClient; P: Pointer): Word; +function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word; var Dir, i: Byte; + WeaponAct: Byte; WeaponSelect: Word; PID: Word; kByte: Word; Pl: TPlayer; GT: LongWord; + Err: Boolean; begin Result := 0; + Err := False; if not gGameOn then Exit; - GT := e_Raw_Read_LongWord(P); + try + GT := M.ReadLongWord(); + except + Err := True; + end; + + if Err then begin MH_MalformedPacket(C); Exit; end; + PID := C^.Player; Pl := g_Player_Get(PID); if Pl = nil then @@ -451,9 +599,17 @@ begin with Pl do begin NetTime := GT; - kByte := e_Raw_Read_Word(P); - Dir := e_Raw_Read_Byte(P); - WeaponSelect := e_Raw_Read_Word(P); + try + kByte := M.ReadWord(); + Dir := M.ReadByte(); + WeaponAct := M.ReadByte(); + WeaponSelect := M.ReadWord(); + except + Err := True; + end; + + if Err then begin MH_MalformedPacket(C); Exit; end; + //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING); if Direction <> TDirection(Dir) then JustTeleported := False; @@ -474,8 +630,15 @@ begin if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000); if LongBool(kByte and NET_KEY_FIRE) then PressKey(KEY_FIRE, 10000); if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000); - if LongBool(kByte and NET_KEY_NW) then PressKey(KEY_NEXTWEAPON, 10000); - if LongBool(kByte and NET_KEY_PW) then PressKey(KEY_PREVWEAPON, 10000); + + for i := 0 to 7 do + begin + if (WeaponAct and Byte(1 shl i)) <> 0 then + begin + //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING); + ProcessWeaponAction(i); + end; + end; for i := 0 to 15 do begin @@ -490,15 +653,23 @@ begin // MH_SEND_PlayerPos(False, PID, C^.ID); end; -procedure MH_RECV_CheatRequest(C: pTNetClient; P: Pointer); +procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg); var CheatKind: Byte; Pl: TPlayer; + Err: Boolean; begin + Err := False; Pl := g_Player_Get(C^.Player); if Pl = nil then Exit; - CheatKind := e_Raw_Read_Byte(P); + try + CheatKind := M.ReadByte(); + except + Err := True; + end; + + if Err then begin MH_MalformedPacket(C); Exit; end; case CheatKind of NET_CHEAT_SUICIDE: @@ -506,27 +677,66 @@ begin NET_CHEAT_SPECTATE: begin if Pl.FSpectator then - Pl.Respawn(False) + begin + if (gGameSettings.MaxLives = 0) or (gLMSRespawn > LMS_RESPAWN_NONE) then + Pl.Respawn(False) + else + MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, Pl.UID); + end else Pl.Spectate; end; + NET_CHEAT_READY: + begin + if gState <> STATE_INTERCUSTOM then Exit; + Pl.FReady := not Pl.FReady; + if Pl.FReady then + begin + MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'Y'); + Inc(gInterReadyCount); + end + else + begin + MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'N'); + Dec(gInterReadyCount); + end; + end; + NET_CHEAT_DROPFLAG: + Pl.TryDropFlag(); end; end; -procedure MH_RECV_PlayerSettings(C: pTNetClient; P: Pointer); +procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg); var TmpName: string; TmpModel: string; TmpColor: TRGB; TmpTeam: Byte; + TmpWeapSwitch: Byte; + TmpPrefArray: Array [WP_FIRST .. WP_LAST + 1] of Byte; + TmpSwEmpty: Byte; + I: Integer; Pl: TPlayer; + Err: Boolean; begin - TmpName := e_Raw_Read_String(P); - TmpModel := e_Raw_Read_String(P); - TmpColor.R := e_Raw_Read_Byte(P); - TmpColor.G := e_Raw_Read_Byte(P); - TmpColor.B := e_Raw_Read_Byte(P); - TmpTeam := e_Raw_Read_Byte(P); + Err := False; + try + TmpName := M.ReadString(); + TmpModel := M.ReadString(); + TmpColor.R := M.ReadByte(); + TmpColor.G := M.ReadByte(); + TmpColor.B := M.ReadByte(); + TmpTeam := M.ReadByte(); + TmpWeapSwitch := M.ReadByte(); + if (TmpWeapSwitch = 2) then + for I := WP_FIRST to WP_LAST + 1 do + TmpPrefArray[I] := M.ReadByte(); + TmpSwEmpty := M.ReadByte(); + except + Err := True; + end; + + if Err then begin MH_MalformedPacket(C); Exit; end; Pl := g_Player_Get(C^.Player); if Pl = nil then Exit; @@ -545,16 +755,31 @@ begin if TmpModel <> Pl.Model.Name then Pl.SetModel(TmpModel); + if (TmpWeapSwitch <> Pl.WeapSwitchMode) then + Pl.WeapSwitchMode := TmpWeapSwitch; + + if (TmpWeapSwitch = 2) then + Pl.SetWeaponPrefs(TmpPrefArray); + + if (TmpSwEmpty <> Pl.SwitchToEmpty) then + Pl.SwitchToEmpty := TmpSwEmpty; MH_SEND_PlayerSettings(Pl.UID, TmpModel); end; // RCON -procedure MH_RECV_RCONPassword(C: pTNetClient; P: Pointer); +procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg); var Pwd: string; + Err: Boolean; begin - Pwd := e_Raw_Read_String(P); + Err := False; + try + Pwd := M.ReadString(); + except + Err := True; + end; + if Err then begin MH_MalformedPacket(C); Exit; end; if not NetAllowRCON then Exit; if Pwd = NetRCONPassword then begin @@ -565,11 +790,18 @@ begin MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWBAD, 'N', C^.ID); end; -procedure MH_RECV_RCONCommand(C: pTNetClient; P: Pointer); +procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg); var Cmd: string; + Err: Boolean; begin - Cmd := e_Raw_Read_String(P); + Err := False; + try + Cmd := M.ReadString(); + except + Err := True; + end; + if Err then begin MH_MalformedPacket(C); Exit; end; if not NetAllowRCON then Exit; if not C^.RCONAuth then begin @@ -581,15 +813,23 @@ end; // MISC -procedure MH_RECV_Vote(C: pTNetClient; P: Pointer); +procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg); var Start: Boolean; Name, Command: string; Need: Integer; Pl: TPlayer; + Err: Boolean; begin - Start := e_Raw_Read_Byte(P) <> 0; - Command := e_Raw_Read_String(P); + Err := False; + try + Start := M.ReadByte() <> 0; + Command := M.ReadString(); + except + Err := True; + end; + + if Err then begin MH_MalformedPacket(C); Exit; end; Pl := g_Player_Get(C^.Player); if Pl = nil then Exit; @@ -626,12 +866,40 @@ end; // GAME (SEND) -procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE); +procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NET_EVERYONE}); + + function sendItemRespawn (it: PItem): Boolean; + begin + result := false; // don't stop + MH_SEND_ItemSpawn(True, it.myid, ID); + end; + + function sendMonSpawn (mon: TMonster): Boolean; + begin + result := false; // don't stop + MH_SEND_MonsterSpawn(mon.UID, ID); + end; + + function sendPanelState (pan: TPanel): Boolean; + begin + result := false; // don't stop + MH_SEND_PanelState(pan.guid, ID); // anyway, to sync mplats + if (pan.CanChangeTexture) then MH_SEND_PanelTexture(pan.guid, pan.LastAnimLoop, ID); + end; + var I: Integer; begin + if (ID >= 0) and (ID < length(NetClients)) then + begin + e_LogWritefln('*** client #%u (cid #%u) will get everything', [ID, NetClients[ID].Player]); + MH_ProcessFirstSpawn(@NetClients[ID]); + end; + if gPlayers <> nil then + begin for I := Low(gPlayers) to High(gPlayers) do + begin if gPlayers[I] <> nil then begin if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID); @@ -639,93 +907,38 @@ begin MH_SEND_PlayerStats(gPlayers[I].UID, ID); if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then + begin MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID); + end; end; - - if gItems <> nil then - begin - for I := High(gItems) downto Low(gItems) do - if gItems[I].Live then - MH_SEND_ItemSpawn(True, I, ID); + end; end; - if gMonsters <> nil then - for I := 0 to High(gMonsters) do - if gMonsters[I] <> nil then - MH_SEND_MonsterSpawn(gMonsters[I].UID, ID); - - if gWalls <> nil then - for I := Low(gWalls) to High(gWalls) do - if gWalls[I] <> nil then - with gWalls[I] do - begin - if Door then - MH_SEND_PanelState(PanelType, I, ID); - - if GetTextureCount > 1 then - MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); - end; - - if gLifts <> nil then - for I := Low(gLifts) to High(gLifts) do - if gLifts[I] <> nil then - with gLifts[I] do - MH_SEND_PanelState(PanelType, I, ID); - - if gRenderForegrounds <> nil then - for I := Low(gRenderForegrounds) to High(gRenderForegrounds) do - if gRenderForegrounds[I] <> nil then - with gRenderForegrounds[I] do - begin - if (GetTextureCount > 1) then - MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); - if Moved then - MH_SEND_PanelState(PanelType, I, ID); - end; - if gRenderBackgrounds <> nil then - for I := Low(gRenderBackgrounds) to High(gRenderBackgrounds) do - if gRenderBackgrounds[I] <> nil then - with gRenderBackgrounds[I] do - begin - if (GetTextureCount > 1) then - MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); - if Moved then - MH_SEND_PanelState(PanelType, I, ID); - end; - if gWater <> nil then - for I := Low(gWater) to High(gWater) do - if gWater[I] <> nil then - with gWater[I] do - if GetTextureCount > 1 then - MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); - if gAcid1 <> nil then - for I := Low(gAcid1) to High(gAcid1) do - if gAcid1[I] <> nil then - with gAcid1[I] do - if GetTextureCount > 1 then - MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); - if gAcid2 <> nil then - for I := Low(gAcid2) to High(gAcid2) do - if gAcid2[I] <> nil then - with gAcid2[I] do - if GetTextureCount > 1 then - MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); - if gSteps <> nil then - for I := Low(gSteps) to High(gSteps) do - if gSteps[I] <> nil then - with gSteps[I] do - if GetTextureCount > 1 then - MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); + g_Items_ForEachAlive(sendItemRespawn, true); // backwards + g_Mons_ForEach(sendMonSpawn); + g_Map_ForEachPanel(sendPanelState); if gTriggers <> nil then + begin for I := Low(gTriggers) to High(gTriggers) do + begin if gTriggers[I].TriggerType = TRIGGER_SOUND then + begin MH_SEND_TriggerSound(gTriggers[I], ID); + end; + end; + end; if Shots <> nil then + begin for I := Low(Shots) to High(Shots) do + begin if Shots[i].ShotType in [6, 7, 8] then + begin MH_SEND_CreateShot(i, ID); + end; + end; + end; MH_SEND_TriggerMusic(ID); @@ -734,38 +947,31 @@ begin if gGameSettings.GameMode = GM_CTF then begin - if gFlags[FLAG_RED].State <> FLAG_STATE_CAPTURED then - MH_SEND_FlagEvent(gFlags[FLAG_RED].State, FLAG_RED, 0, True, ID); - if gFlags[FLAG_BLUE].State <> FLAG_STATE_CAPTURED then - MH_SEND_FlagEvent(gFlags[FLAG_BLUE].State, FLAG_BLUE, 0, True, ID); + if gFlags[FLAG_RED].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_RED].State, FLAG_RED, 0, True, ID); + if gFlags[FLAG_BLUE].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_BLUE].State, FLAG_BLUE, 0, True, ID); end; if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME; - if gLMSRespawn > LMS_RESPAWN_NONE then - MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000, 'N', ID); + g_Net_Flush(); end; procedure MH_SEND_Info(ID: Byte); -var - Map: string; begin - Map := g_ExtractFileName(gMapInfo.Map); - - e_Buffer_Clear(@NetOut); - - e_Buffer_Write(@NetOut, Byte(NET_MSG_INFO)); - e_Buffer_Write(@NetOut, ID); - e_Buffer_Write(@NetOut, NetClients[ID].Player); - e_Buffer_Write(@NetOut, gGameSettings.WAD); - e_Buffer_Write(@NetOut, Map); - e_Buffer_Write(@NetOut, gWADHash); - e_Buffer_Write(@NetOut, gGameSettings.GameMode); - e_Buffer_Write(@NetOut, gGameSettings.GoalLimit); - e_Buffer_Write(@NetOut, gGameSettings.TimeLimit); - e_Buffer_Write(@NetOut, gGameSettings.MaxLives); - e_Buffer_Write(@NetOut, gGameSettings.Options); - e_Buffer_Write(@NetOut, gTime); + NetOut.Clear(); + + NetOut.Write(Byte(NET_MSG_INFO)); + NetOut.Write(ID); + NetOut.Write(NetClients[ID].Player); + NetOut.Write(ExtractFileName(gGameSettings.WAD)); + NetOut.Write(g_ExtractFileName(gMapInfo.Map)); + NetOut.Write(gWADHash); + NetOut.Write(gGameSettings.GameMode); + NetOut.Write(gGameSettings.GoalLimit); + NetOut.Write(gGameSettings.TimeLimit); + NetOut.Write(gGameSettings.MaxLives); + NetOut.Write(gGameSettings.Options); + NetOut.Write(gTime); g_Net_Host_Send(ID, True, NET_CHAN_SERVICE); end; @@ -786,9 +992,9 @@ begin if (gPlayers[i] <> nil) and (gPlayers[i].FClientID >= 0) and (gPlayers[i].Team = ID) then begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT)); - e_Buffer_Write(@NetOut, Txt); - e_Buffer_Write(@NetOut, Mode); + NetOut.Write(Byte(NET_MSG_CHAT)); + NetOut.Write(Txt); + NetOut.Write(Mode); g_Net_Host_Send(gPlayers[i].FClientID, True, NET_CHAN_CHAT); end; Team := ID; @@ -796,9 +1002,9 @@ begin end else begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT)); - e_Buffer_Write(@NetOut, Txt); - e_Buffer_Write(@NetOut, Mode); + NetOut.Write(Byte(NET_MSG_CHAT)); + NetOut.Write(Txt); + NetOut.Write(Mode); g_Net_Host_Send(ID, True, NET_CHAN_CHAT); end; @@ -810,8 +1016,8 @@ begin if Mode = NET_CHAT_PLAYER then begin g_Console_Add(Txt, True); - e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY); - g_Sound_PlayEx('SOUND_GAME_RADIO'); + e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify); + g_Game_ChatSound(b_Text_Unformat(Txt)); end else if Mode = NET_CHAT_TEAM then @@ -820,14 +1026,14 @@ begin if (gPlayer1.Team = TEAM_RED) and (Team = TEAM_RED) then begin g_Console_Add(#18'[Team] '#2 + Txt, True); - e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY); - g_Sound_PlayEx('SOUND_GAME_RADIO'); + e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify); + g_Game_ChatSound(b_Text_Unformat(Txt)); end else if (gPlayer1.Team = TEAM_BLUE) and (Team = TEAM_BLUE) then begin g_Console_Add(#20'[Team] '#2 + Txt, True); - e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY); - g_Sound_PlayEx('SOUND_GAME_RADIO'); + e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify); + g_Game_ChatSound(b_Text_Unformat(Txt)); end; end; end @@ -835,34 +1041,34 @@ begin begin Name := g_Net_ClientName_ByID(ID); g_Console_Add('-> ' + Name + ': ' + Txt, True); - e_WriteLog('[Tell ' + Name + '] ' + b_Text_Unformat(Txt), MSG_NOTIFY); - g_Sound_PlayEx('SOUND_GAME_RADIO'); + e_WriteLog('[Tell ' + Name + '] ' + b_Text_Unformat(Txt), TMsgType.Notify); + g_Game_ChatSound(b_Text_Unformat(Txt), False); end; end; procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_GFX)); - e_Buffer_Write(@NetOut, Kind); - e_Buffer_Write(@NetOut, X); - e_Buffer_Write(@NetOut, Y); - e_Buffer_Write(@NetOut, Ang); + NetOut.Write(Byte(NET_MSG_GFX)); + NetOut.Write(Kind); + NetOut.Write(X); + NetOut.Write(Y); + NetOut.Write(Ang); g_Net_Host_Send(ID, False, NET_CHAN_GAME); end; procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_SND)); - e_Buffer_Write(@NetOut, Name); + NetOut.Write(Byte(NET_MSG_SND)); + NetOut.Write(Name); if Pos then begin - e_Buffer_Write(@NetOut, Byte(1)); - e_Buffer_Write(@NetOut, X); - e_Buffer_Write(@NetOut, Y); + NetOut.Write(Byte(1)); + NetOut.Write(X); + NetOut.Write(Y); end else - e_Buffer_Write(@NetOut, Byte(0)); + NetOut.Write(Byte(0)); g_Net_Host_Send(ID, False, NET_CHAN_GAME); end; @@ -871,16 +1077,16 @@ procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE); begin if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_SHADD)); - e_Buffer_Write(@NetOut, Proj); - e_Buffer_Write(@NetOut, Shots[Proj].ShotType); - e_Buffer_Write(@NetOut, Shots[Proj].Target); - e_Buffer_Write(@NetOut, Shots[Proj].SpawnerUID); - e_Buffer_Write(@NetOut, Shots[Proj].Timeout); - e_Buffer_Write(@NetOut, Shots[Proj].Obj.X); - e_Buffer_Write(@NetOut, Shots[Proj].Obj.Y); - e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.X); - e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.Y); + NetOut.Write(Byte(NET_MSG_SHADD)); + NetOut.Write(Proj); + NetOut.Write(Shots[Proj].ShotType); + NetOut.Write(Shots[Proj].Target); + NetOut.Write(Shots[Proj].SpawnerUID); + NetOut.Write(Shots[Proj].Timeout); + NetOut.Write(Shots[Proj].Obj.X); + NetOut.Write(Shots[Proj].Obj.Y); + NetOut.Write(Shots[Proj].Obj.Vel.X); + NetOut.Write(Shots[Proj].Obj.Vel.Y); g_Net_Host_Send(ID, True, NET_CHAN_SHOTS); end; @@ -889,40 +1095,40 @@ procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE); begin if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_SHPOS)); - e_Buffer_Write(@NetOut, Proj); - e_Buffer_Write(@NetOut, Shots[Proj].Obj.X); - e_Buffer_Write(@NetOut, Shots[Proj].Obj.Y); - e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.X); - e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.Y); + NetOut.Write(Byte(NET_MSG_SHPOS)); + NetOut.Write(Proj); + NetOut.Write(Shots[Proj].Obj.X); + NetOut.Write(Shots[Proj].Obj.Y); + NetOut.Write(Shots[Proj].Obj.Vel.X); + NetOut.Write(Shots[Proj].Obj.Vel.Y); g_Net_Host_Send(ID, False, NET_CHAN_SHOTS); end; procedure MH_Send_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_SHDEL)); - e_Buffer_Write(@NetOut, Proj); - e_Buffer_Write(@NetOut, Byte(Loud)); - e_Buffer_Write(@NetOut, X); - e_Buffer_Write(@NetOut, Y); + NetOut.Write(Byte(NET_MSG_SHDEL)); + NetOut.Write(Proj); + NetOut.Write(Byte(Loud)); + NetOut.Write(X); + NetOut.Write(Y); g_Net_Host_Send(ID, True, NET_CHAN_SHOTS); end; procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_SCORE)); + NetOut.Write(Byte(NET_MSG_SCORE)); if gGameSettings.GameMode in [GM_TDM, GM_CTF] then begin - e_Buffer_Write(@NetOut, gTeamStat[TEAM_RED].Goals); - e_Buffer_Write(@NetOut, gTeamStat[TEAM_BLUE].Goals); + NetOut.Write(gTeamStat[TEAM_RED].Goals); + NetOut.Write(gTeamStat[TEAM_BLUE].Goals); end else if gGameSettings.GameMode = GM_COOP then begin - e_Buffer_Write(@NetOut, gCoopMonstersKilled); - e_Buffer_Write(@NetOut, gCoopSecretsFound); + NetOut.Write(gCoopMonstersKilled); + NetOut.Write(gCoopSecretsFound); end; g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT); @@ -930,58 +1136,71 @@ end; procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_COOP)); - e_Buffer_Write(@NetOut, gTotalMonsters); - e_Buffer_Write(@NetOut, gSecretsCount); - e_Buffer_Write(@NetOut, gCoopTotalMonstersKilled); - e_Buffer_Write(@NetOut, gCoopTotalSecretsFound); - e_Buffer_Write(@NetOut, gCoopTotalMonsters); - e_Buffer_Write(@NetOut, gCoopTotalSecrets); + NetOut.Write(Byte(NET_MSG_COOP)); + NetOut.Write(gTotalMonsters); + NetOut.Write(gSecretsCount); + NetOut.Write(gCoopTotalMonstersKilled); + NetOut.Write(gCoopTotalSecretsFound); + NetOut.Write(gCoopTotalMonsters); + NetOut.Write(gCoopTotalSecrets); end; procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_GEVENT)); - e_Buffer_Write(@NetOut, EvType); - e_Buffer_Write(@NetOut, EvNum); - e_Buffer_Write(@NetOut, EvStr); - e_Buffer_Write(@NetOut, Byte(gLastMap)); - e_Buffer_Write(@NetOut, gTime); - if (EvType = NET_EV_MAPSTART) and (Pos(':\', EvStr) > 0) then + NetOut.Write(Byte(NET_MSG_GEVENT)); + NetOut.Write(EvType); + NetOut.Write(EvNum); + NetOut.Write(EvStr); + NetOut.Write(Byte(gLastMap)); + NetOut.Write(gTime); + if (EvType = NET_EV_MAPSTART) and isWadPath(EvStr) then begin - e_Buffer_Write(@NetOut, Byte(1)); - e_Buffer_Write(@NetOut, gWADHash); + NetOut.Write(Byte(1)); + NetOut.Write(gWADHash); end else - e_Buffer_Write(@NetOut, Byte(0)); + NetOut.Write(Byte(0)); g_Net_Host_Send(ID, True, NET_CHAN_SERVICE); end; procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_FLAG)); - e_Buffer_Write(@NetOut, EvType); - e_Buffer_Write(@NetOut, Flag); - e_Buffer_Write(@NetOut, Byte(Quiet)); - e_Buffer_Write(@NetOut, PID); - e_Buffer_Write(@NetOut, gFlags[Flag].State); - e_Buffer_Write(@NetOut, gFlags[Flag].CaptureTime); - e_Buffer_Write(@NetOut, gFlags[Flag].Obj.X); - e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Y); - e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Vel.X); - e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Vel.Y); + NetOut.Write(Byte(NET_MSG_FLAG)); + NetOut.Write(EvType); + NetOut.Write(Flag); + NetOut.Write(Byte(Quiet)); + NetOut.Write(PID); + NetOut.Write(gFlags[Flag].State); + NetOut.Write(gFlags[Flag].CaptureTime); + NetOut.Write(gFlags[Flag].Obj.X); + NetOut.Write(gFlags[Flag].Obj.Y); + NetOut.Write(gFlags[Flag].Obj.Vel.X); + NetOut.Write(gFlags[Flag].Obj.Vel.Y); + NetOut.Write(Byte(gFlags[Flag].Direction)); g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT); end; +procedure MH_SEND_FlagPos(Flag: Byte; ID: Integer = NET_EVERYONE); +begin + NetOut.Write(Byte(NET_MSG_FLAGPOS)); + NetOut.Write(Flag); + NetOut.Write(gFlags[Flag].Obj.X); + NetOut.Write(gFlags[Flag].Obj.Y); + NetOut.Write(gFlags[Flag].Obj.Vel.X); + NetOut.Write(gFlags[Flag].Obj.Vel.Y); + + g_Net_Host_Send(ID, False, NET_CHAN_IMPORTANT); +end; + procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_GSET)); - e_Buffer_Write(@NetOut, gGameSettings.GameMode); - e_Buffer_Write(@NetOut, gGameSettings.GoalLimit); - e_Buffer_Write(@NetOut, gGameSettings.TimeLimit); - e_Buffer_Write(@NetOut, gGameSettings.MaxLives); - e_Buffer_Write(@NetOut, gGameSettings.Options); + NetOut.Write(Byte(NET_MSG_GSET)); + NetOut.Write(gGameSettings.GameMode); + NetOut.Write(gGameSettings.GoalLimit); + NetOut.Write(gGameSettings.TimeLimit); + NetOut.Write(gGameSettings.MaxLives); + NetOut.Write(gGameSettings.Options); g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT); end; @@ -995,15 +1214,15 @@ begin P := g_Player_Get(PID); if P = nil then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLR)); - e_Buffer_Write(@NetOut, PID); - e_Buffer_Write(@NetOut, P.Name); + NetOut.Write(Byte(NET_MSG_PLR)); + NetOut.Write(PID); + NetOut.Write(P.Name); - e_Buffer_Write(@NetOut, P.FActualModelName); - e_Buffer_Write(@NetOut, P.FColor.R); - e_Buffer_Write(@NetOut, P.FColor.G); - e_Buffer_Write(@NetOut, P.FColor.B); - e_Buffer_Write(@NetOut, P.Team); + NetOut.Write(P.FActualModelName); + NetOut.Write(P.FColor.R); + NetOut.Write(P.FColor.G); + NetOut.Write(P.FColor.B); + NetOut.Write(P.Team); g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT) end; @@ -1017,16 +1236,16 @@ begin if Pl = nil then Exit; if Pl.FDummy then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRPOS)); - e_Buffer_Write(@NetOut, gTime); - e_Buffer_Write(@NetOut, PID); + NetOut.Write(Byte(NET_MSG_PLRPOS)); + NetOut.Write(gTime); + NetOut.Write(PID); kByte := 0; with Pl do begin - e_Buffer_Write(@NetOut, FPing); - e_Buffer_Write(@NetOut, FLoss); + NetOut.Write(FPing); + NetOut.Write(FLoss); if IsKeyPressed(KEY_CHAT) then kByte := NET_KEY_CHAT else @@ -1036,17 +1255,18 @@ begin if IsKeyPressed(KEY_UP) then kByte := kByte or NET_KEY_UP; if IsKeyPressed(KEY_DOWN) then kByte := kByte or NET_KEY_DOWN; if IsKeyPressed(KEY_JUMP) then kByte := kByte or NET_KEY_JUMP; - if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR; end; - e_Buffer_Write(@NetOut, kByte); - if Direction = D_LEFT then e_Buffer_Write(@NetOut, Byte(0)) else e_Buffer_Write(@NetOut, Byte(1)); - e_Buffer_Write(@NetOut, GameX); - e_Buffer_Write(@NetOut, GameY); - e_Buffer_Write(@NetOut, GameVelX); - e_Buffer_Write(@NetOut, GameVelY); - e_Buffer_Write(@NetOut, GameAccelX); - e_Buffer_Write(@NetOut, GameAccelY); + if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR; + + NetOut.Write(kByte); + if Direction = TDirection.D_LEFT then NetOut.Write(Byte(0)) else NetOut.Write(Byte(1)); + NetOut.Write(GameX); + NetOut.Write(GameY); + NetOut.Write(GameVelX); + NetOut.Write(GameVelY); + NetOut.Write(GameAccelX); + NetOut.Write(GameAccelY); end; g_Net_Host_Send(ID, Reliable, NET_CHAN_PLAYERPOS); @@ -1060,49 +1280,51 @@ begin P := g_Player_Get(PID); if P = nil then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSTA)); - e_Buffer_Write(@NetOut, PID); + NetOut.Write(Byte(NET_MSG_PLRSTA)); + NetOut.Write(PID); with P do begin - e_Buffer_Write(@NetOut, Byte(Live)); - e_Buffer_Write(@NetOut, Byte(GodMode)); - e_Buffer_Write(@NetOut, Health); - e_Buffer_Write(@NetOut, Armor); - e_Buffer_Write(@NetOut, Air); - e_Buffer_Write(@NetOut, JetFuel); - e_Buffer_Write(@NetOut, Lives); - e_Buffer_Write(@NetOut, Team); + NetOut.Write(Byte(alive)); + NetOut.Write(Byte(GodMode)); + NetOut.Write(Health); + NetOut.Write(Armor); + NetOut.Write(Air); + NetOut.Write(JetFuel); + NetOut.Write(Lives); + NetOut.Write(Team); for I := WP_FIRST to WP_LAST do - e_Buffer_Write(@NetOut, Byte(FWeapon[I])); + NetOut.Write(Byte(FWeapon[I])); for I := A_BULLETS to A_HIGH do - e_Buffer_Write(@NetOut, FAmmo[I]); + NetOut.Write(FAmmo[I]); for I := A_BULLETS to A_HIGH do - e_Buffer_Write(@NetOut, FMaxAmmo[I]); + NetOut.Write(FMaxAmmo[I]); for I := MR_SUIT to MR_MAX do - e_Buffer_Write(@NetOut, LongWord(FMegaRulez[I])); - - e_Buffer_Write(@NetOut, Byte(R_ITEM_BACKPACK in FRulez)); - e_Buffer_Write(@NetOut, Byte(R_KEY_RED in FRulez)); - e_Buffer_Write(@NetOut, Byte(R_KEY_GREEN in FRulez)); - e_Buffer_Write(@NetOut, Byte(R_KEY_BLUE in FRulez)); - e_Buffer_Write(@NetOut, Byte(R_BERSERK in FRulez)); - - e_Buffer_Write(@NetOut, Frags); - e_Buffer_Write(@NetOut, Death); - - e_Buffer_Write(@NetOut, CurrWeap); - - e_Buffer_Write(@NetOut, Byte(FSpectator)); - e_Buffer_Write(@NetOut, Byte(FGhost)); - e_Buffer_Write(@NetOut, Byte(FPhysics)); - e_Buffer_Write(@NetOut, Byte(FNoRespawn)); - e_Buffer_Write(@NetOut, Byte(FJetpack)); - e_Buffer_Write(@NetOut, FFireTime); + NetOut.Write(LongWord(FMegaRulez[I])); + + NetOut.Write(Byte(R_ITEM_BACKPACK in FRulez)); + NetOut.Write(Byte(R_KEY_RED in FRulez)); + NetOut.Write(Byte(R_KEY_GREEN in FRulez)); + NetOut.Write(Byte(R_KEY_BLUE in FRulez)); + NetOut.Write(Byte(R_BERSERK in FRulez)); + + NetOut.Write(Frags); + NetOut.Write(Death); + + NetOut.Write(CurrWeap); + + NetOut.Write(Byte(FSpectator)); + NetOut.Write(Byte(FGhost)); + NetOut.Write(Byte(FPhysics)); + NetOut.Write(Byte(FNoRespawn)); + NetOut.Write(Byte(FJetpack)); + NetOut.Write(FFireTime); + NetOut.Write(Byte(FFlaming)); + NetOut.Write(FSpawnInvul); end; g_Net_Host_Send(ID, True, NET_CHAN_PLAYER); @@ -1110,46 +1332,46 @@ end; procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDMG)); - e_Buffer_Write(@NetOut, PID); - e_Buffer_Write(@NetOut, Kind); - e_Buffer_Write(@NetOut, Attacker); - e_Buffer_Write(@NetOut, Value); - e_Buffer_Write(@NetOut, VX); - e_Buffer_Write(@NetOut, VY); + NetOut.Write(Byte(NET_MSG_PLRDMG)); + NetOut.Write(PID); + NetOut.Write(Kind); + NetOut.Write(Attacker); + NetOut.Write(Value); + NetOut.Write(VX); + NetOut.Write(VY); g_Net_Host_Send(ID, False, NET_CHAN_PLAYER); end; procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDIE)); - e_Buffer_Write(@NetOut, PID); - e_Buffer_Write(@NetOut, KillType); - e_Buffer_Write(@NetOut, DeathType); - e_Buffer_Write(@NetOut, Attacker); + NetOut.Write(Byte(NET_MSG_PLRDIE)); + NetOut.Write(PID); + NetOut.Write(KillType); + NetOut.Write(DeathType); + NetOut.Write(Attacker); g_Net_Host_Send(ID, True, NET_CHAN_PLAYER); end; procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRFIRE)); - e_Buffer_Write(@NetOut, PID); - e_Buffer_Write(@NetOut, Weapon); - e_Buffer_Write(@NetOut, X); - e_Buffer_Write(@NetOut, Y); - e_Buffer_Write(@NetOut, AX); - e_Buffer_Write(@NetOut, AY); - e_Buffer_Write(@NetOut, ShotID); + NetOut.Write(Byte(NET_MSG_PLRFIRE)); + NetOut.Write(PID); + NetOut.Write(Weapon); + NetOut.Write(X); + NetOut.Write(Y); + NetOut.Write(AX); + NetOut.Write(AY); + NetOut.Write(ShotID); g_Net_Host_Send(ID, True, NET_CHAN_SHOTS); end; procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDEL)); - e_Buffer_Write(@NetOut, PID); + NetOut.Write(Byte(NET_MSG_PLRDEL)); + NetOut.Write(PID); g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT); end; @@ -1161,17 +1383,17 @@ begin Pl := g_Player_Get(PID); if Pl = nil then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSET)); - e_Buffer_Write(@NetOut, PID); - e_Buffer_Write(@NetOut, Pl.Name); + NetOut.Write(Byte(NET_MSG_PLRSET)); + NetOut.Write(PID); + NetOut.Write(Pl.Name); if Mdl = '' then - e_Buffer_Write(@NetOut, Pl.Model.Name) + NetOut.Write(Pl.Model.Name) else - e_Buffer_Write(@NetOut, Mdl); - e_Buffer_Write(@NetOut, Pl.FColor.R); - e_Buffer_Write(@NetOut, Pl.FColor.G); - e_Buffer_Write(@NetOut, Pl.FColor.B); - e_Buffer_Write(@NetOut, Pl.Team); + NetOut.Write(Mdl); + NetOut.Write(Pl.FColor.R); + NetOut.Write(Pl.FColor.G); + NetOut.Write(Pl.FColor.B); + NetOut.Write(Pl.Team); g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT); end; @@ -1179,99 +1401,105 @@ end; // ITEM (SEND) procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE); -begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_ISPAWN)); - e_Buffer_Write(@NetOut, IID); - e_Buffer_Write(@NetOut, Byte(Quiet)); - e_Buffer_Write(@NetOut, gItems[IID].ItemType); - e_Buffer_Write(@NetOut, Byte(gItems[IID].Fall)); - e_Buffer_Write(@NetOut, Byte(gItems[IID].Respawnable)); - e_Buffer_Write(@NetOut, gItems[IID].Obj.X); - e_Buffer_Write(@NetOut, gItems[IID].Obj.Y); - e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.X); - e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.Y); +var + it: PItem; + tt: Byte; +begin + it := g_Items_ByIdx(IID); + + NetOut.Write(Byte(NET_MSG_ISPAWN)); + NetOut.Write(IID); + NetOut.Write(Byte(Quiet)); + tt := it.ItemType; + if it.dropped then tt := tt or $80; + NetOut.Write(tt); + NetOut.Write(Byte(it.Fall)); + NetOut.Write(Byte(it.Respawnable)); + NetOut.Write(it.Obj.X); + NetOut.Write(it.Obj.Y); + NetOut.Write(it.Obj.Vel.X); + NetOut.Write(it.Obj.Vel.Y); g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA); end; procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_IDEL)); - e_Buffer_Write(@NetOut, IID); - e_Buffer_Write(@NetOut, Byte(Quiet)); + NetOut.Write(Byte(NET_MSG_IDEL)); + NetOut.Write(IID); + NetOut.Write(Byte(Quiet)); g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA); end; +procedure MH_SEND_ItemPos(IID: Word; ID: Integer = NET_EVERYONE); +var + it: PItem; +begin + it := g_Items_ByIdx(IID); + + NetOut.Write(Byte(NET_MSG_IPOS)); + NetOut.Write(IID); + NetOut.Write(it.Obj.X); + NetOut.Write(it.Obj.Y); + NetOut.Write(it.Obj.Vel.X); + NetOut.Write(it.Obj.Vel.Y); + + g_Net_Host_Send(ID, False, NET_CHAN_LARGEDATA); +end; + // PANEL -procedure MH_SEND_PanelTexture(PType: Word; PID: LongWord; AnimLoop: Byte; ID: Integer = NET_EVERYONE); +procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE); var TP: TPanel; begin - case PType of - PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: - TP := gWalls[PID]; - PANEL_FORE: - TP := gRenderForegrounds[PID]; - PANEL_BACK: - TP := gRenderBackgrounds[PID]; - PANEL_WATER: - TP := gWater[PID]; - PANEL_ACID1: - TP := gAcid1[PID]; - PANEL_ACID2: - TP := gAcid2[PID]; - PANEL_STEP: - TP := gSteps[PID]; - else - Exit; - end; + TP := g_Map_PanelByGUID(PGUID); + if (TP = nil) then exit; with TP do begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_PTEX)); - e_Buffer_Write(@NetOut, PType); - e_Buffer_Write(@NetOut, PID); - e_Buffer_Write(@NetOut, FCurTexture); - e_Buffer_Write(@NetOut, FCurFrame); - e_Buffer_Write(@NetOut, FCurFrameCount); - e_Buffer_Write(@NetOut, AnimLoop); + NetOut.Write(Byte(NET_MSG_PTEX)); + NetOut.Write(LongWord(PGUID)); + NetOut.Write(FCurTexture); + NetOut.Write(FCurFrame); + NetOut.Write(FCurFrameCount); + NetOut.Write(AnimLoop); end; g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA); end; -procedure MH_SEND_PanelState(PType: Word; PID: LongWord; ID: Integer = NET_EVERYONE); +procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE); var TP: TPanel; -begin - case PType of - PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: - TP := gWalls[PID]; - PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: - TP := gLifts[PID]; - PANEL_BACK: - begin - TP := gRenderBackgrounds[PID]; - TP.Moved := True; - end; - PANEL_FORE: - begin - TP := gRenderForegrounds[PID]; - TP.Moved := True; - end; - else - Exit; - end; - - e_Buffer_Write(@NetOut, Byte(NET_MSG_PSTATE)); - e_Buffer_Write(@NetOut, PType); - e_Buffer_Write(@NetOut, PID); - e_Buffer_Write(@NetOut, Byte(TP.Enabled)); - e_Buffer_Write(@NetOut, TP.LiftType); - e_Buffer_Write(@NetOut, TP.X); - e_Buffer_Write(@NetOut, TP.Y); + mpflags: Byte = 0; +begin + TP := g_Map_PanelByGUID(PGUID); + if (TP = nil) then exit; + + NetOut.Write(Byte(NET_MSG_PSTATE)); + NetOut.Write(LongWord(PGUID)); + NetOut.Write(Byte(TP.Enabled)); + NetOut.Write(TP.LiftType); + NetOut.Write(TP.X); + NetOut.Write(TP.Y); + NetOut.Write(Word(TP.Width)); + NetOut.Write(Word(TP.Height)); + // mplats + NetOut.Write(LongInt(TP.movingSpeedX)); + NetOut.Write(LongInt(TP.movingSpeedY)); + NetOut.Write(LongInt(TP.movingStartX)); + NetOut.Write(LongInt(TP.movingStartY)); + NetOut.Write(LongInt(TP.movingEndX)); + NetOut.Write(LongInt(TP.movingEndY)); + NetOut.Write(LongInt(TP.sizeSpeedX)); + NetOut.Write(LongInt(TP.sizeSpeedY)); + NetOut.Write(LongInt(TP.sizeEndX)); + NetOut.Write(LongInt(TP.sizeEndY)); + if TP.movingActive then mpflags := mpflags or 1; + if TP.moveOnce then mpflags := mpflags or 2; + NetOut.Write(Byte(mpflags)); g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA); end; @@ -1283,22 +1511,22 @@ begin if gTriggers = nil then Exit; if T.Sound = nil then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_TSOUND)); - e_Buffer_Write(@NetOut, T.ClientID); - e_Buffer_Write(@NetOut, Byte(T.Sound.IsPlaying)); - e_Buffer_Write(@NetOut, LongWord(T.Sound.GetPosition)); - e_Buffer_Write(@NetOut, T.SoundPlayCount); + NetOut.Write(Byte(NET_MSG_TSOUND)); + NetOut.Write(T.ClientID); + NetOut.Write(Byte(T.Sound.IsPlaying)); + NetOut.Write(LongWord(T.Sound.GetPosition)); + NetOut.Write(T.SoundPlayCount); g_Net_Host_Send(ID, True); end; procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_TMUSIC)); - e_Buffer_Write(@NetOut, gMusic.Name); - e_Buffer_Write(@NetOut, Byte(gMusic.IsPlaying)); - e_Buffer_Write(@NetOut, LongWord(gMusic.GetPosition)); - e_Buffer_Write(@NetOut, Byte(gMusic.SpecPause or gMusic.IsPaused)); + NetOut.Write(Byte(NET_MSG_TMUSIC)); + NetOut.Write(gMusic.Name); + NetOut.Write(Byte(gMusic.IsPlaying)); + NetOut.Write(LongWord(gMusic.GetPosition)); + NetOut.Write(Byte(gMusic.SpecPause or gMusic.IsPaused)); g_Net_Host_Send(ID, True); end; @@ -1309,28 +1537,28 @@ procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE); var M: TMonster; begin - M := g_Monsters_Get(UID); + M := g_Monsters_ByUID(UID); if M = nil then Exit; with M do begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_MSPAWN)); - e_Buffer_Write(@NetOut, UID); - e_Buffer_Write(@NetOut, MonsterType); - e_Buffer_Write(@NetOut, MonsterState); - e_Buffer_Write(@NetOut, MonsterAnim); - e_Buffer_Write(@NetOut, MonsterTargetUID); - e_Buffer_Write(@NetOut, MonsterTargetTime); - e_Buffer_Write(@NetOut, MonsterBehaviour); - e_Buffer_Write(@NetOut, MonsterSleep); - e_Buffer_Write(@NetOut, MonsterHealth); - e_Buffer_Write(@NetOut, MonsterAmmo); - e_Buffer_Write(@NetOut, GameX); - e_Buffer_Write(@NetOut, GameY); - e_Buffer_Write(@NetOut, GameVelX); - e_Buffer_Write(@NetOut, GameVelY); - e_Buffer_Write(@NetOut, Byte(GameDirection)); + NetOut.Write(Byte(NET_MSG_MSPAWN)); + NetOut.Write(UID); + NetOut.Write(MonsterType); + NetOut.Write(MonsterState); + NetOut.Write(MonsterAnim); + NetOut.Write(MonsterTargetUID); + NetOut.Write(MonsterTargetTime); + NetOut.Write(MonsterBehaviour); + NetOut.Write(MonsterSleep); + NetOut.Write(MonsterHealth); + NetOut.Write(MonsterAmmo); + NetOut.Write(GameX); + NetOut.Write(GameY); + NetOut.Write(GameVelX); + NetOut.Write(GameVelY); + NetOut.Write(Byte(GameDirection)); end; g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA); @@ -1340,19 +1568,19 @@ procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE); var M: TMonster; begin - M := g_Monsters_Get(UID); + M := g_Monsters_ByUID(UID); if M = nil then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_MPOS)); - e_Buffer_Write(@NetOut, UID); + NetOut.Write(Byte(NET_MSG_MPOS)); + NetOut.Write(UID); with M do begin - e_Buffer_Write(@NetOut, GameX); - e_Buffer_Write(@NetOut, GameY); - e_Buffer_Write(@NetOut, GameVelX); - e_Buffer_Write(@NetOut, GameVelY); - e_Buffer_Write(@NetOut, Byte(GameDirection)); + NetOut.Write(GameX); + NetOut.Write(GameY); + NetOut.Write(GameVelX); + NetOut.Write(GameVelY); + NetOut.Write(Byte(GameDirection)); end; g_Net_Host_Send(ID, False, NET_CHAN_MONSTERPOS); @@ -1362,24 +1590,24 @@ procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = var M: TMonster; begin - M := g_Monsters_Get(UID); + M := g_Monsters_ByUID(UID); if M = nil then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_MSTATE)); - e_Buffer_Write(@NetOut, UID); + NetOut.Write(Byte(NET_MSG_MSTATE)); + NetOut.Write(UID); with M do begin - e_Buffer_Write(@NetOut, MonsterState); - e_Buffer_Write(@NetOut, ForcedAnim); - e_Buffer_Write(@NetOut, MonsterTargetUID); - e_Buffer_Write(@NetOut, MonsterTargetTime); - e_Buffer_Write(@NetOut, MonsterSleep); - e_Buffer_Write(@NetOut, MonsterHealth); - e_Buffer_Write(@NetOut, MonsterAmmo); - e_Buffer_Write(@NetOut, MonsterPain); - e_Buffer_Write(@NetOut, Byte(AnimIsReverse)); - e_Buffer_Write(@NetOut, FFireTime); + NetOut.Write(MonsterState); + NetOut.Write(ForcedAnim); + NetOut.Write(MonsterTargetUID); + NetOut.Write(MonsterTargetTime); + NetOut.Write(MonsterSleep); + NetOut.Write(MonsterHealth); + NetOut.Write(MonsterAmmo); + NetOut.Write(MonsterPain); + NetOut.Write(Byte(AnimIsReverse)); + NetOut.Write(FFireTime); end; g_Net_Host_Send(ID, True, NET_CHAN_MONSTER); @@ -1387,12 +1615,12 @@ end; procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_MSHOT)); - e_Buffer_Write(@NetOut, UID); - e_Buffer_Write(@NetOut, X); - e_Buffer_Write(@NetOut, Y); - e_Buffer_Write(@NetOut, VX); - e_Buffer_Write(@NetOut, VY); + NetOut.Write(Byte(NET_MSG_MSHOT)); + NetOut.Write(UID); + NetOut.Write(X); + NetOut.Write(Y); + NetOut.Write(VX); + NetOut.Write(VY); g_Net_Host_Send(ID, True, NET_CHAN_MONSTER); end; @@ -1401,11 +1629,11 @@ procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE); var M: TMonster; begin - M := g_Monsters_Get(UID); + M := g_Monsters_ByUID(UID); if M = nil then Exit; - e_Buffer_Write(@NetOut, Byte(NET_MSG_MDEL)); - e_Buffer_Write(@NetOut, UID); + NetOut.Write(Byte(NET_MSG_MDEL)); + NetOut.Write(UID); g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA); end; @@ -1414,8 +1642,8 @@ end; procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_TIME_SYNC)); - e_Buffer_Write(@NetOut, Time); + NetOut.Write(Byte(NET_MSG_TIME_SYNC)); + NetOut.Write(Time); g_Net_Host_Send(ID, False, NET_CHAN_SERVICE); end; @@ -1425,12 +1653,12 @@ procedure MH_SEND_VoteEvent(EvType: Byte; IntArg1: SmallInt = 0; IntArg2: SmallInt = 0; ID: Integer = NET_EVERYONE); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_VOTE_EVENT)); - e_Buffer_Write(@NetOut, EvType); - e_Buffer_Write(@NetOut, IntArg1); - e_Buffer_Write(@NetOut, IntArg2); - e_Buffer_Write(@NetOut, StrArg1); - e_Buffer_Write(@NetOut, StrArg2); + NetOut.Write(Byte(NET_MSG_VOTE_EVENT)); + NetOut.Write(EvType); + NetOut.Write(IntArg1); + NetOut.Write(IntArg2); + NetOut.Write(StrArg1); + NetOut.Write(StrArg2); g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT); end; @@ -1439,36 +1667,39 @@ end; // GAME -procedure MC_RECV_Chat(P: Pointer); +procedure MC_RECV_Chat(var M: TMsg); var Txt: string; Mode: Byte; begin - Txt := e_Raw_Read_String(P); - Mode := e_Raw_Read_Byte(P); + Txt := M.ReadString(); + Mode := M.ReadByte(); if Mode <> NET_CHAT_SYSTEM then begin - if Mode = NET_CHAT_PLAYER then - begin - g_Console_Add(Txt, True); - e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY); - g_Sound_PlayEx('SOUND_GAME_RADIO'); - end else - if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then + if NetDeafLevel = 0 then begin - if gPlayer1.Team = TEAM_RED then - g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True); - if gPlayer1.Team = TEAM_BLUE then - g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True); - e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY); - g_Sound_PlayEx('SOUND_GAME_RADIO'); + if Mode = NET_CHAT_PLAYER then + begin + g_Console_Add(Txt, True); + e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify); + g_Game_ChatSound(b_Text_Unformat(Txt)); + end else + if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then + begin + if gPlayer1.Team = TEAM_RED then + g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True); + if gPlayer1.Team = TEAM_BLUE then + g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True); + e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify); + g_Game_ChatSound(b_Text_Unformat(Txt)); + end; end; - end else + end else if (NetDeafLevel < 2) then g_Console_Add(Txt, True); end; -procedure MC_RECV_Effect(P: Pointer); +procedure MC_RECV_Effect(var M: TMsg); var Kind: Byte; X, Y: Integer; @@ -1477,10 +1708,10 @@ var ID: LongWord; begin if not gGameOn then Exit; - Kind := e_Raw_Read_Byte(P); - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); - Ang := e_Raw_Read_SmallInt(P); + Kind := M.ReadByte(); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + Ang := M.ReadSmallInt(); case Kind of NET_GFX_SPARK: @@ -1572,40 +1803,40 @@ begin end; end; -procedure MC_RECV_Sound(P: Pointer); +procedure MC_RECV_Sound(var M: TMsg); var Name: string; X, Y: Integer; Pos: Boolean; begin - Name := e_Raw_Read_String(P); - Pos := e_Raw_Read_Byte(P) <> 0; + Name := M.ReadString(); + Pos := M.ReadByte() <> 0; if Pos then begin - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); g_Sound_PlayExAt(Name, X, Y); end else g_Sound_PlayEx(Name); end; -procedure MC_RECV_CreateShot(P: Pointer); +procedure MC_RECV_CreateShot(var M: TMsg); var I, X, Y, XV, YV: Integer; Timeout: LongWord; Target, Spawner: Word; ShType: Byte; begin - I := e_Raw_Read_LongInt(P); - ShType := e_Raw_Read_Byte(P); - Target := e_Raw_Read_Word(P); - Spawner := e_Raw_Read_Word(P); - Timeout := e_Raw_Read_LongWord(P); - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); - XV := e_Raw_Read_LongInt(P); - YV := e_Raw_Read_LongInt(P); + I := M.ReadLongInt(); + ShType := M.ReadByte(); + Target := M.ReadWord(); + Spawner := M.ReadWord(); + Timeout := M.ReadLongWord(); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + XV := M.ReadLongInt(); + YV := M.ReadLongInt(); I := g_Weapon_CreateShot(I, ShType, Spawner, Target, X, Y, XV, YV); if (Shots <> nil) and (I <= High(Shots)) then @@ -1615,15 +1846,15 @@ begin end; end; -procedure MC_RECV_UpdateShot(P: Pointer); +procedure MC_RECV_UpdateShot(var M: TMsg); var I, TX, TY, TXV, TYV: Integer; begin - I := e_Raw_Read_LongInt(P); - TX := e_Raw_Read_LongInt(P); - TY := e_Raw_Read_LongInt(P); - TXV := e_Raw_Read_LongInt(P); - TYV := e_Raw_Read_LongInt(P); + I := M.ReadLongInt(); + TX := M.ReadLongInt(); + TY := M.ReadLongInt(); + TXV := M.ReadLongInt(); + TYV := M.ReadLongInt(); if (Shots <> nil) and (I <= High(Shots)) then with (Shots[i]) do @@ -1635,46 +1866,46 @@ begin end; end; -procedure MC_RECV_DeleteShot(P: Pointer); +procedure MC_RECV_DeleteShot(var M: TMsg); var I, X, Y: Integer; L: Boolean; begin if not gGameOn then Exit; - I := e_Raw_Read_LongInt(P); - L := (e_Raw_Read_Byte(P) <> 0); - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); + I := M.ReadLongInt(); + L := (M.ReadByte() <> 0); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); g_Weapon_DestroyShot(I, X, Y, L); end; -procedure MC_RECV_GameStats(P: Pointer); +procedure MC_RECV_GameStats(var M: TMsg); begin if gGameSettings.GameMode in [GM_TDM, GM_CTF] then begin - gTeamStat[TEAM_RED].Goals := e_Raw_Read_SmallInt(P); - gTeamStat[TEAM_BLUE].Goals := e_Raw_Read_SmallInt(P); + gTeamStat[TEAM_RED].Goals := M.ReadSmallInt(); + gTeamStat[TEAM_BLUE].Goals := M.ReadSmallInt(); end else if gGameSettings.GameMode = GM_COOP then begin - gCoopMonstersKilled := e_Raw_Read_Word(P); - gCoopSecretsFound := e_Raw_Read_Word(P); + gCoopMonstersKilled := M.ReadWord(); + gCoopSecretsFound := M.ReadWord(); end; end; -procedure MC_RECV_CoopStats(P: Pointer); +procedure MC_RECV_CoopStats(var M: TMsg); begin - gTotalMonsters := e_Raw_Read_LongInt(P); - gSecretsCount := e_Raw_Read_LongInt(P); - gCoopTotalMonstersKilled := e_Raw_Read_Word(P); - gCoopTotalSecretsFound := e_Raw_Read_Word(P); - gCoopTotalMonsters := e_Raw_Read_Word(P); - gCoopTotalSecrets := e_Raw_Read_Word(P); + gTotalMonsters := M.ReadLongInt(); + gSecretsCount := M.ReadLongInt(); + gCoopTotalMonstersKilled := M.ReadWord(); + gCoopTotalSecretsFound := M.ReadWord(); + gCoopTotalMonsters := M.ReadWord(); + gCoopTotalSecrets := M.ReadWord(); end; -procedure MC_RECV_GameEvent(P: Pointer); +procedure MC_RECV_GameEvent(var M: TMsg); var EvType: Byte; EvNum: Integer; @@ -1686,48 +1917,79 @@ var i1, i2: TStrings_Locale; pln: String; cnt: Byte; + goodCmd: Boolean = true; begin FillChar(EvHash, Sizeof(EvHash), 0); - EvType := e_Raw_Read_Byte(P); - EvNum := e_Raw_Read_LongInt(P); - EvStr := e_Raw_Read_String(P); - gLastMap := e_Raw_Read_Byte(P) <> 0; + EvType := M.ReadByte(); + EvNum := M.ReadLongInt(); + EvStr := M.ReadString(); + gLastMap := M.ReadByte() <> 0; if gLastMap and (gGameSettings.GameMode = GM_COOP) then gStatsOff := True; gStatsPressed := True; - EvTime := e_Raw_Read_LongWord(P); - BHash := e_Raw_Read_Byte(P) <> 0; + EvTime := M.ReadLongWord(); + BHash := M.ReadByte() <> 0; if BHash then - EvHash := e_Raw_Read_MD5(P); + EvHash := M.ReadMD5(); gTime := EvTime; + if (g_Res_received_map_start <> 0) then + begin + if (g_Res_received_map_start < 0) then exit; + goodCmd := false; + case EvType of + NET_EV_MAPSTART: goodCmd := true; + NET_EV_MAPEND: goodCmd := true; + NET_EV_PLAYER_KICK: goodCmd := true; + NET_EV_PLAYER_BAN: goodCmd := true; + NET_EV_LMS_WARMUP: goodCmd := true; + end; + if not goodCmd then exit; + end; + case EvType of NET_EV_MAPSTART: begin - gGameOn := False; - g_Game_ClearLoading(); - g_Game_StopAllSounds(True); + if (g_Res_received_map_start <> 0) then + begin + g_Res_received_map_start := -1; + end + else + begin + gGameOn := False; + g_Game_ClearLoading(); + g_Game_StopAllSounds(True); - gSwitchGameMode := Byte(EvNum); - gGameSettings.GameMode := gSwitchGameMode; + gSwitchGameMode := Byte(EvNum); + gGameSettings.GameMode := gSwitchGameMode; - gWADHash := EvHash; - if not g_Game_StartMap(EvStr, True) then - begin - if Pos(':\', EvStr) = 0 then - g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + EvStr])) - else - g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [EvStr])); - Exit; - end; + gWADHash := EvHash; + if not g_Game_StartMap(false{asMegawad}, EvStr, True) then + begin + if not isWadPath(EvStr) then + g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + EvStr])) + else + g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [EvStr])); + Exit; + end; - MC_SEND_FullStateRequest; + MC_SEND_FullStateRequest; + end; end; NET_EV_MAPEND: begin - gMissionFailed := EvNum <> 0; - gExit := EXIT_ENDLEVELCUSTOM; + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + if (g_Res_received_map_start <> 0) then + begin + g_Res_received_map_start := -1; + end + else + begin + gMissionFailed := EvNum <> 0; + gExit := EXIT_ENDLEVELCUSTOM; + end; end; NET_EV_RCON: @@ -1744,26 +2006,46 @@ begin NET_EV_CHANGE_TEAM: begin - if EvNum = TEAM_RED then - g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True); - if EvNum = TEAM_BLUE then - g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True); + if NetDeafLevel < 2 then + begin + if EvNum = TEAM_RED then + g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True); + if EvNum = TEAM_BLUE then + g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True); + end; end; NET_EV_PLAYER_KICK: - g_Console_Add(Format(_lc[I_PLAYER_KICK], [EvStr]), True); + begin + g_Console_Add(Format(_lc[I_PLAYER_KICK], [EvStr]), True); + if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1; + end; NET_EV_PLAYER_BAN: - g_Console_Add(Format(_lc[I_PLAYER_BAN], [EvStr]), True); + begin + g_Console_Add(Format(_lc[I_PLAYER_BAN], [EvStr]), True); + if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1; + end; NET_EV_LMS_WARMUP: - g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [EvNum]), True); + begin + if EvNum > 0 then + begin + gLMSRespawn := LMS_RESPAWN_WARMUP; + gLMSRespawnTime := gTime + EvNum; + g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [EvNum div 1000]), True); + end + else if gPlayer1 = nil then + begin + g_Console_Add(_lc[I_PLAYER_SPECT4], True); + end; + end; NET_EV_LMS_SURVIVOR: g_Console_Add('*** ' + _lc[I_MESSAGE_LMS_SURVIVOR] + ' ***', True); NET_EV_BIGTEXT: - g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum)); + if NetDeafLevel < 2 then g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum)); NET_EV_SCORE: begin @@ -1843,6 +2125,7 @@ begin NET_EV_LMS_START: begin g_Player_RemoveAllCorpses; + gLMSRespawn := LMS_RESPAWN_NONE; g_Game_Message(_lc[I_MESSAGE_LMS_START], 144); end; @@ -1863,6 +2146,9 @@ begin NET_EV_LMS_DRAW: g_Game_Message(_lc[I_GAME_WIN_DRAW], 144); + NET_EV_LMS_NOSPAWN: + g_Console_Add(_lc[I_PLAYER_SPECT4], True); + NET_EV_KILLCOMBO: g_Game_Announce_KillCombo(EvNum); @@ -1873,32 +2159,60 @@ begin pl.Touch(); end; + NET_EV_SECRET: + begin + pl := g_Player_Get(EvNum); + if pl <> nil then + begin + g_Console_Add(Format(_lc[I_PLAYER_SECRET], [pl.Name]), True); + g_Sound_PlayEx('SOUND_GAME_SECRET'); + end; + end; + + NET_EV_INTER_READY: + begin + pl := g_Player_Get(EvNum); + if pl <> nil then pl.FReady := (EvStr = 'Y'); + end; end; end; -procedure MC_RECV_FlagEvent(P: Pointer); +procedure MC_RECV_FlagPos(var M: TMsg); +var + Fl: Byte; +begin + Fl := M.ReadByte(); + if Fl = FLAG_NONE then Exit; + gFlags[Fl].Obj.X := M.ReadLongInt(); + gFlags[Fl].Obj.Y := M.ReadLongInt(); + gFlags[Fl].Obj.Vel.X := M.ReadLongInt(); + gFlags[Fl].Obj.Vel.Y := M.ReadLongInt(); +end; + +procedure MC_RECV_FlagEvent(var M: TMsg); var PID: Word; Pl: TPlayer; EvType: Byte; - Fl: Byte; + Fl, a: Byte; Quiet: Boolean; s, ts: string; begin - EvType := e_Raw_Read_Byte(P); - Fl := e_Raw_Read_Byte(P); + EvType := M.ReadByte(); + Fl := M.ReadByte(); if Fl = FLAG_NONE then Exit; - Quiet := (e_Raw_Read_Byte(P) <> 0); - PID := e_Raw_Read_Word(P); + Quiet := (M.ReadByte() <> 0); + PID := M.ReadWord(); - gFlags[Fl].State := e_Raw_Read_Byte(P); - gFlags[Fl].CaptureTime := e_Raw_Read_LongWord(P); - gFlags[Fl].Obj.X := e_Raw_Read_LongInt(P); - gFlags[Fl].Obj.Y := e_Raw_Read_LongInt(P); - gFlags[Fl].Obj.Vel.X := e_Raw_Read_LongInt(P); - gFlags[Fl].Obj.Vel.Y := e_Raw_Read_LongInt(P); + gFlags[Fl].State := M.ReadByte(); + gFlags[Fl].CaptureTime := M.ReadLongWord(); + gFlags[Fl].Obj.X := M.ReadLongInt(); + gFlags[Fl].Obj.Y := M.ReadLongInt(); + gFlags[Fl].Obj.Vel.X := M.ReadLongInt(); + gFlags[Fl].Obj.Vel.Y := M.ReadLongInt(); + gFlags[Fl].Direction := TDirection(M.ReadByte()); Pl := g_Player_Get(PID); if (Pl = nil) and @@ -1918,6 +2232,16 @@ begin s := _lc[I_PLAYER_FLAG_BLUE]; g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144); + + if ((Pl = gPlayer1) or (Pl = gPlayer2) + or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team)) + or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then + a := 0 + else + a := 1; + + if not sound_ret_flag[a].IsPlaying() then + sound_ret_flag[a].Play(); end; FLAG_STATE_CAPTURED: @@ -1933,6 +2257,16 @@ begin g_Console_Add(Format(_lc[I_PLAYER_FLAG_GET], [Pl.Name, s]), True); g_Game_Message(Format(_lc[I_MESSAGE_FLAG_GET], [AnsiUpperCase(s)]), 144); + + if ((Pl = gPlayer1) or (Pl = gPlayer2) + or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team)) + or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then + a := 0 + else + a := 1; + + if not sound_get_flag[a].IsPlaying() then + sound_get_flag[a].Play(); end; FLAG_STATE_DROPPED: @@ -1948,6 +2282,16 @@ begin g_Console_Add(Format(_lc[I_PLAYER_FLAG_DROP], [Pl.Name, s]), True); g_Game_Message(Format(_lc[I_MESSAGE_FLAG_DROP], [AnsiUpperCase(s)]), 144); + + if ((Pl = gPlayer1) or (Pl = gPlayer2) + or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team)) + or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then + a := 0 + else + a := 1; + + if not sound_lost_flag[a].IsPlaying() then + sound_lost_flag[a].Play(); end; FLAG_STATE_SCORED: @@ -1966,6 +2310,16 @@ begin Insert('.', ts, Length(ts) + 1 - 3); g_Console_Add(Format(_lc[I_PLAYER_FLAG_CAPTURE], [Pl.Name, s, ts]), True); g_Game_Message(Format(_lc[I_MESSAGE_FLAG_CAPTURE], [AnsiUpperCase(s)]), 144); + + if ((Pl = gPlayer1) or (Pl = gPlayer2) + or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team)) + or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then + a := 0 + else + a := 1; + + if not sound_cap_flag[a].IsPlaying() then + sound_cap_flag[a].Play(); end; FLAG_STATE_RETURNED: @@ -1979,22 +2333,32 @@ begin s := _lc[I_PLAYER_FLAG_BLUE]; g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144); + + if ((Pl = gPlayer1) or (Pl = gPlayer2) + or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team)) + or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then + a := 0 + else + a := 1; + + if not sound_ret_flag[a].IsPlaying() then + sound_ret_flag[a].Play(); end; end; end; -procedure MC_RECV_GameSettings(P: Pointer); +procedure MC_RECV_GameSettings(var M: TMsg); begin - gGameSettings.GameMode := e_Raw_Read_Byte(P); - gGameSettings.GoalLimit := e_Raw_Read_Word(P); - gGameSettings.TimeLimit := e_Raw_Read_Word(P); - gGameSettings.MaxLives := e_Raw_Read_Byte(P); - gGameSettings.Options := e_Raw_Read_LongWord(P); + gGameSettings.GameMode := M.ReadByte(); + gGameSettings.GoalLimit := M.ReadWord(); + gGameSettings.TimeLimit := M.ReadWord(); + gGameSettings.MaxLives := M.ReadByte(); + gGameSettings.Options := M.ReadLongWord(); end; // PLAYER -function MC_RECV_PlayerCreate(P: Pointer): Word; +function MC_RECV_PlayerCreate(var M: TMsg): Word; var PID, DID: Word; PName, Model: string; @@ -2002,15 +2366,15 @@ var T: Byte; Pl: TPlayer; begin - PID := e_Raw_Read_Word(P); + PID := M.ReadWord(); Pl := g_Player_Get(PID); - PName := e_Raw_Read_String(P); - Model := e_Raw_Read_String(P); - Color.R := e_Raw_Read_Byte(P); - Color.G := e_Raw_Read_Byte(P); - Color.B := e_Raw_Read_Byte(P); - T := e_Raw_Read_Byte(P); + PName := M.ReadString(); + Model := M.ReadString(); + Color.R := M.ReadByte(); + Color.G := M.ReadByte(); + Color.B := M.ReadByte(); + T := M.ReadByte(); Result := 0; if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then @@ -2038,12 +2402,13 @@ begin end; end; - g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True); - e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', MSG_NOTIFY); + if NetDeafLevel < 3 then + g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True); + e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', TMsgType.Notify); Result := PID; end; -function MC_RECV_PlayerPos(P: Pointer): Word; +function MC_RECV_PlayerPos(var M: TMsg): Word; var GT: LongWord; PID: Word; @@ -2054,7 +2419,7 @@ var begin Result := 0; - GT := e_Raw_Read_LongWord(P); + GT := M.ReadLongWord(); if GT < gTime - NET_MAX_DIFFTIME then begin gTime := GT; @@ -2062,7 +2427,7 @@ begin end; gTime := GT; - PID := e_Raw_Read_Word(P); + PID := M.ReadWord(); Pl := g_Player_Get(PID); if Pl = nil then Exit; @@ -2071,17 +2436,17 @@ begin with Pl do begin - FPing := e_Raw_Read_Word(P); - FLoss := e_Raw_Read_Byte(P); - kByte := e_Raw_Read_Word(P); - Dir := e_Raw_Read_Byte(P); + FPing := M.ReadWord(); + FLoss := M.ReadByte(); + kByte := M.ReadWord(); + Dir := M.ReadByte(); - TmpX := e_Raw_Read_LongInt(P); - TmpY := e_Raw_Read_LongInt(P); + TmpX := M.ReadLongInt(); + TmpY := M.ReadLongInt(); ReleaseKeys; - if (kByte = NET_KEY_CHAT) then + if LongBool(kByte and NET_KEY_CHAT) then PressKey(KEY_CHAT, 10000) else begin @@ -2092,27 +2457,29 @@ begin if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000); end; - if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or LongBool(kByte and NET_KEY_FORCEDIR) then + JustTeleported := LongBool(kByte and NET_KEY_FORCEDIR); + + if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or JustTeleported then SetDirection(TDirection(Dir)); - GameVelX := e_Raw_Read_LongInt(P); - GameVelY := e_Raw_Read_LongInt(P); - GameAccelX := e_Raw_Read_LongInt(P); - GameAccelY := e_Raw_Read_LongInt(P); + GameVelX := M.ReadLongInt(); + GameVelY := M.ReadLongInt(); + GameAccelX := M.ReadLongInt(); + GameAccelY := M.ReadLongInt(); SetLerp(TmpX, TmpY); if NetForcePlayerUpdate then Update(); end; end; -function MC_RECV_PlayerStats(P: Pointer): Word; +function MC_RECV_PlayerStats(var M: TMsg): Word; var PID: Word; Pl: TPlayer; - I: Integer; - OldJet: Boolean; + I, OldFire: Integer; + OldJet, Flam: Boolean; NewTeam: Byte; begin - PID := e_Raw_Read_Word(P); + PID := M.ReadWord(); Pl := g_Player_Get(PID); Result := 0; if Pl = nil then @@ -2120,75 +2487,88 @@ begin with Pl do begin - Live := (e_Raw_Read_Byte(P) <> 0); - GodMode := (e_Raw_Read_Byte(P) <> 0); - Health := e_Raw_Read_LongInt(P); - Armor := e_Raw_Read_LongInt(P); - Air := e_Raw_Read_LongInt(P); - JetFuel := e_Raw_Read_LongInt(P); - Lives := e_Raw_Read_Byte(P); - NewTeam := e_Raw_Read_Byte(P); + alive := (M.ReadByte() <> 0); + GodMode := (M.ReadByte() <> 0); + Health := M.ReadLongInt(); + Armor := M.ReadLongInt(); + Air := M.ReadLongInt(); + JetFuel := M.ReadLongInt(); + Lives := M.ReadByte(); + NewTeam := M.ReadByte(); for I := WP_FIRST to WP_LAST do - FWeapon[I] := (e_Raw_Read_Byte(P) <> 0); + FWeapon[I] := (M.ReadByte() <> 0); for I := A_BULLETS to A_HIGH do - FAmmo[I] := e_Raw_Read_Word(P); + FAmmo[I] := M.ReadWord(); for I := A_BULLETS to A_HIGH do - FMaxAmmo[I] := e_Raw_Read_Word(P); + FMaxAmmo[I] := M.ReadWord(); for I := MR_SUIT to MR_MAX do - FMegaRulez[I] := e_Raw_Read_LongWord(P); + FMegaRulez[I] := M.ReadLongWord(); FRulez := []; - if (e_Raw_Read_Byte(P) <> 0) then + if (M.ReadByte() <> 0) then FRulez := FRulez + [R_ITEM_BACKPACK]; - if (e_Raw_Read_Byte(P) <> 0) then + if (M.ReadByte() <> 0) then FRulez := FRulez + [R_KEY_RED]; - if (e_Raw_Read_Byte(P) <> 0) then + if (M.ReadByte() <> 0) then FRulez := FRulez + [R_KEY_GREEN]; - if (e_Raw_Read_Byte(P) <> 0) then + if (M.ReadByte() <> 0) then FRulez := FRulez + [R_KEY_BLUE]; - if (e_Raw_Read_Byte(P) <> 0) then + if (M.ReadByte() <> 0) then FRulez := FRulez + [R_BERSERK]; - Frags := e_Raw_Read_LongInt(P); - Death := e_Raw_Read_LongInt(P); + Frags := M.ReadLongInt(); + Death := M.ReadLongInt(); - SetWeapon(e_Raw_Read_Byte(P)); + SetWeapon(M.ReadByte()); - FSpectator := e_Raw_Read_Byte(P) <> 0; + FSpectator := M.ReadByte() <> 0; if FSpectator then begin - if Pl = gPlayer1 then + if UID = NetPlrUID1 then begin - gLMSPID1 := UID; + gSpectLatchPID1 := UID; gPlayer1 := nil; end; - if Pl = gPlayer2 then + if UID = NetPlrUID2 then begin - gLMSPID2 := UID; + gSpectLatchPID2 := UID; gPlayer2 := nil; end; end else begin - if (gPlayer1 = nil) and (gLMSPID1 > 0) then - gPlayer1 := g_Player_Get(gLMSPID1); - if (gPlayer2 = nil) and (gLMSPID2 > 0) then - gPlayer2 := g_Player_Get(gLMSPID2); + if (gPlayer1 = nil) and (gSpectLatchPID1 > 0) and (UID = gSpectLatchPID1) then + begin + gPlayer1 := Pl; + gSpectLatchPID1 := 0; + end; + if (gPlayer2 = nil) and (gSpectLatchPID2 > 0) and (UID = gSpectLatchPID2) then + begin + gPlayer2 := Pl; + gSpectLatchPID2 := 0; + end; end; - FGhost := e_Raw_Read_Byte(P) <> 0; - FPhysics := e_Raw_Read_Byte(P) <> 0; - FNoRespawn := e_Raw_Read_Byte(P) <> 0; + FGhost := M.ReadByte() <> 0; + FPhysics := M.ReadByte() <> 0; + FNoRespawn := M.ReadByte() <> 0; OldJet := FJetpack; - FJetpack := e_Raw_Read_Byte(P) <> 0; - FFireTime := e_Raw_Read_LongInt(P); + FJetpack := M.ReadByte() <> 0; + OldFire := FFireTime; + FFireTime := M.ReadLongInt(); + if (OldFire <= 0) and (FFireTime > 0) then + g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y); + Flam := M.ReadByte() <> 0; + FSpawnInvul := M.ReadLongInt(); if OldJet and not FJetpack then JetpackOff else if not OldJet and FJetpack then JetpackOn; + if FFlaming and not Flam then + FlamerOff; if Team <> NewTeam then Pl.ChangeTeam(NewTeam); end; @@ -2196,7 +2576,7 @@ begin Result := PID; end; -function MC_RECV_PlayerDamage(P: Pointer): Word; +function MC_RECV_PlayerDamage(var M: TMsg): Word; var PID: Word; Pl: TPlayer; @@ -2206,15 +2586,15 @@ var begin Result := 0; if not gGameOn then Exit; - PID := e_Raw_Read_Word(P); + PID := M.ReadWord(); Pl := g_Player_Get(PID); if Pl = nil then Exit; - Kind := e_Raw_Read_Byte(P); - Attacker := e_Raw_Read_Word(P); - Value := e_Raw_Read_Word(P); - VX := e_Raw_Read_Word(P); - VY := e_Raw_Read_Word(P); + Kind := M.ReadByte(); + Attacker := M.ReadWord(); + Value := M.ReadWord(); + VX := M.ReadWord(); + VY := M.ReadWord(); with Pl do Damage(Value, Attacker, VX, VY, Kind); @@ -2222,7 +2602,7 @@ begin Result := PID; end; -function MC_RECV_PlayerDeath(P: Pointer): Word; +function MC_RECV_PlayerDeath(var M: TMsg): Word; var PID: Word; Pl: TPlayer; @@ -2231,13 +2611,13 @@ var begin Result := 0; if not gGameOn then Exit; - PID := e_Raw_Read_Word(P); + PID := M.ReadWord(); Pl := g_Player_Get(PID); if Pl = nil then Exit; - KillType := e_Raw_Read_Byte(P); - DeathType := e_Raw_Read_Byte(P); - Attacker := e_Raw_Read_Word(P); + KillType := M.ReadByte(); + DeathType := M.ReadByte(); + Attacker := M.ReadWord(); with Pl do begin @@ -2246,25 +2626,26 @@ begin end; end; -function MC_RECV_PlayerDelete(P: Pointer): Word; +function MC_RECV_PlayerDelete(var M: TMsg): Word; var PID: Word; Pl: TPlayer; begin - PID := e_Raw_Read_Word(P); + PID := M.ReadWord(); Pl := g_Player_Get(PID); Result := 0; if Pl = nil then Exit; - g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True); - e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', MSG_NOTIFY); + if NetDeafLevel < 3 then + g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True); + e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', TMsgType.Notify); g_Player_Remove(PID); Result := PID; end; -function MC_RECV_PlayerFire(P: Pointer): Word; +function MC_RECV_PlayerFire(var M: TMsg): Word; var PID: Word; Weap: Byte; @@ -2274,40 +2655,41 @@ var begin Result := 0; if not gGameOn then Exit; - PID := e_Raw_Read_Word(P); + PID := M.ReadWord(); Pl := g_Player_Get(PID); if Pl = nil then Exit; - Weap := e_Raw_Read_Byte(P); - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); - AX := e_Raw_Read_LongInt(P); - AY := e_Raw_Read_LongInt(P); - SHID := e_Raw_Read_LongInt(P); + Weap := M.ReadByte(); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + AX := M.ReadLongInt(); + AY := M.ReadLongInt(); + SHID := M.ReadLongInt(); with Pl do - if Live then NetFire(Weap, X, Y, AX, AY, SHID); + if alive then NetFire(Weap, X, Y, AX, AY, SHID); end; -procedure MC_RECV_PlayerSettings(P: Pointer); +procedure MC_RECV_PlayerSettings(var M: TMsg); var TmpName: string; TmpModel: string; TmpColor: TRGB; TmpTeam: Byte; + i: Integer; Pl: TPlayer; PID: Word; begin - PID := e_Raw_Read_Word(P); + PID := M.ReadWord(); Pl := g_Player_Get(PID); if Pl = nil then Exit; - TmpName := e_Raw_Read_String(P); - TmpModel := e_Raw_Read_String(P); - TmpColor.R := e_Raw_Read_Byte(P); - TmpColor.G := e_Raw_Read_Byte(P); - TmpColor.B := e_Raw_Read_Byte(P); - TmpTeam := e_Raw_Read_Byte(P); + TmpName := M.ReadString(); + TmpModel := M.ReadString(); + TmpColor.R := M.ReadByte(); + TmpColor.G := M.ReadByte(); + TmpColor.B := M.ReadByte(); + TmpTeam := M.ReadByte(); if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then begin @@ -2321,7 +2703,8 @@ begin if Pl.Name <> TmpName then begin - g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True); + if NetDeafLevel < 3 then + g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True); Pl.Name := TmpName; end; @@ -2331,7 +2714,7 @@ end; // ITEM -procedure MC_RECV_ItemSpawn(P: Pointer); +procedure MC_RECV_ItemSpawn(var M: TMsg); var ID: Word; AID: DWord; @@ -2339,21 +2722,25 @@ var T: Byte; Quiet, Fall{, Resp}: Boolean; Anim: TAnimation; + it: PItem; begin if not gGameOn then Exit; - ID := e_Raw_Read_Word(P); - Quiet := e_Raw_Read_Byte(P) <> 0; - T := e_Raw_Read_Byte(P); - Fall := e_Raw_Read_Byte(P) <> 0; - {Resp :=} e_Raw_Read_Byte(P); - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); - VX := e_Raw_Read_LongInt(P); - VY := e_Raw_Read_LongInt(P); - - g_Items_Create(X, Y, T, Fall, False, False, ID); - gItems[ID].Obj.Vel.X := VX; - gItems[ID].Obj.Vel.Y := VY; + ID := M.ReadWord(); + Quiet := M.ReadByte() <> 0; + T := M.ReadByte(); + Fall := M.ReadByte() <> 0; + {Resp :=} M.ReadByte(); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + VX := M.ReadLongInt(); + VY := M.ReadLongInt(); + + g_Items_Create(X, Y, T and $7F, Fall, False, False, ID); + if ((T and $80) <> 0) then g_Items_SetDrop(ID); + + it := g_Items_ByIdx(ID); + it.Obj.Vel.X := VX; + it.Obj.Vel.Y := VY; if not Quiet then begin @@ -2361,159 +2748,146 @@ begin if g_Frames_Get(AID, 'FRAMES_ITEM_RESPAWN') then begin Anim := TAnimation.Create(AID, False, 4); - g_GFX_OnceAnim(X+(gItems[ID].Obj.Rect.Width div 2)-16, Y+(gItems[ID].Obj.Rect.Height div 2)-16, Anim); + g_GFX_OnceAnim(X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16, Anim); Anim.Free(); end; end; end; -procedure MC_RECV_ItemDestroy(P: Pointer); +procedure MC_RECV_ItemDestroy(var M: TMsg); var ID: Word; Quiet: Boolean; begin if not gGameOn then Exit; - ID := e_Raw_Read_Word(P); - Quiet := e_Raw_Read_Byte(P) <> 0; - if gItems = nil then Exit; - if (ID > High(gItems)) then Exit; + ID := M.ReadWord(); + Quiet := M.ReadByte() <> 0; - if not Quiet then - if gSoundEffectsDF then - begin - if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_INVUL, - ITEM_INVIS, ITEM_MEDKIT_BLACK, ITEM_JETPACK] then - g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ', - gItems[ID].Obj.X, gItems[ID].Obj.Y) - else - if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2, - ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA, - ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_WEAPON_FLAMETHROWER, - ITEM_AMMO_BACKPACK] then - g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON', - gItems[ID].Obj.X, gItems[ID].Obj.Y) - else - g_Sound_PlayExAt('SOUND_ITEM_GETITEM', - gItems[ID].Obj.X, gItems[ID].Obj.Y); - end - else - begin - if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_SUIT, - ITEM_MEDKIT_BLACK, ITEM_INVUL, ITEM_INVIS, ITEM_JETPACK] then - g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ', - gItems[ID].Obj.X, gItems[ID].Obj.Y) - else - if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2, - ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA, - ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_WEAPON_FLAMETHROWER] then - g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON', - gItems[ID].Obj.X, gItems[ID].Obj.Y) - else - g_Sound_PlayExAt('SOUND_ITEM_GETITEM', - gItems[ID].Obj.X, gItems[ID].Obj.Y); - end; + if not g_Items_ValidId(ID) then exit; + + if not Quiet then g_Items_EmitPickupSound(ID); g_Items_Remove(ID); end; +procedure MC_RECV_ItemPos(var M: TMsg); +var + ID: Word; + X, Y, VX, VY: Integer; + it: PItem; +begin + if not gGameOn then Exit; + + ID := M.ReadWord(); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + VX := M.ReadLongInt(); + VY := M.ReadLongInt(); + + if g_Items_ValidId(ID) then + begin + it := g_Items_ByIdx(ID); + it.Obj.X := X; + it.Obj.Y := Y; + it.Obj.Vel.X := VX; + it.Obj.Vel.Y := VY; + it.positionChanged(); + end; +end; + // PANEL -procedure MC_RECV_PanelTexture(P: Pointer); +procedure MC_RECV_PanelTexture(var M: TMsg); var TP: TPanel; - PType: Word; - ID: LongWord; + PGUID: Integer; Tex, Fr: Integer; Loop, Cnt: Byte; begin if not gGameOn then Exit; - PType := e_Raw_Read_Word(P); - ID := e_Raw_Read_LongWord(P); - Tex := e_Raw_Read_LongInt(P); - Fr := e_Raw_Read_LongInt(P); - Cnt := e_Raw_Read_Byte(P); - Loop := e_Raw_Read_Byte(P); - - TP := nil; - - case PType of - PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: - if gWalls <> nil then - TP := gWalls[ID]; - PANEL_FORE: - if gRenderForegrounds <> nil then - TP := gRenderForegrounds[ID]; - PANEL_BACK: - if gRenderBackgrounds <> nil then - TP := gRenderBackgrounds[ID]; - PANEL_WATER: - if gWater <> nil then - TP := gWater[ID]; - PANEL_ACID1: - if gAcid1 <> nil then - TP := gAcid1[ID]; - PANEL_ACID2: - if gAcid2 <> nil then - TP := gAcid2[ID]; - PANEL_STEP: - if gSteps <> nil then - TP := gSteps[ID]; - else - Exit; - end; - if TP <> nil then - if Loop = 0 then - begin // switch texture - TP.SetTexture(Tex, Loop); - TP.SetFrame(Fr, Cnt); - end else // looped or non-looped animation - TP.NextTexture(Loop); + PGUID := Integer(M.ReadLongWord()); + Tex := M.ReadLongInt(); + Fr := M.ReadLongInt(); + Cnt := M.ReadByte(); + Loop := M.ReadByte(); + + TP := g_Map_PanelByGUID(PGUID); + if (TP <> nil) then + begin + // switch texture + TP.SetTexture(Tex, Loop); + TP.SetFrame(Fr, Cnt); + end; end; -procedure MC_RECV_PanelState(P: Pointer); +procedure MC_RECV_PanelState(var M: TMsg); var - ID: LongWord; + PGUID: Integer; E: Boolean; Lift: Byte; - PType: Word; - X, Y: Integer; + X, Y, W, H: Integer; + TP: TPanel; + speedX, speedY, startX, startY, endX, endY: Integer; + sizeSpX, sizeSpY, sizeEX, sizeEY: Integer; + mpflags: Byte; begin if not gGameOn then Exit; - PType := e_Raw_Read_Word(P); - ID := e_Raw_Read_LongWord(P); - E := (e_Raw_Read_Byte(P) <> 0); - Lift := e_Raw_Read_Byte(P); - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); - - case PType of - PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: - if E then - g_Map_EnableWall(ID) - else - g_Map_DisableWall(ID); - - PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: - g_Map_SetLift(ID, Lift); - - PANEL_BACK: - begin - gRenderBackgrounds[ID].X := X; - gRenderBackgrounds[ID].Y := Y; - end; - PANEL_FORE: - begin - gRenderForegrounds[ID].X := X; - gRenderForegrounds[ID].Y := Y; - end; - end; + PGUID := Integer(M.ReadLongWord()); + E := (M.ReadByte() <> 0); + Lift := M.ReadByte(); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + W := M.ReadWord(); + H := M.ReadWord(); + // mplats + speedX := M.ReadLongInt(); + speedY := M.ReadLongInt(); + startX := M.ReadLongInt(); + startY := M.ReadLongInt(); + endX := M.ReadLongInt(); + endY := M.ReadLongInt(); + sizeSpX := M.ReadLongInt(); + sizeSpY := M.ReadLongInt(); + sizeEX := M.ReadLongInt(); + sizeEY := M.ReadLongInt(); + mpflags := M.ReadByte(); // bit0: TP.movingActive; bit1: TP.moveOnce + + TP := g_Map_PanelByGUID(PGUID); + if (TP = nil) then exit; + + // update lifts state + if TP.isGLift then g_Map_SetLiftGUID(PGUID, Lift); + + // update enabled/disabled state for all panels + if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID); + + // update panel position, as it can be moved (mplat) + TP.X := X; + TP.Y := Y; + TP.Width := W; + TP.Height := H; + // update mplat state + TP.movingSpeedX := speedX; + TP.movingSpeedY := speedY; + TP.movingStartX := startX; + TP.movingStartY := startY; + TP.movingEndX := endX; + TP.movingEndY := endY; + TP.sizeSpeedX := sizeSpX; + TP.sizeSpeedY := sizeSpY; + TP.sizeEndX := sizeEX; + TP.sizeEndY := sizeEY; + TP.movingActive := ((mpflags and 1) <> 0); + TP.moveOnce := ((mpflags and 2) <> 0); + // notify panel of it's position/size change, so it can fix other internal structures + TP.positionChanged(); end; // TRIGGERS -procedure MC_RECV_TriggerSound(P: Pointer); +procedure MC_RECV_TriggerSound(var M: TMsg); var SPlaying: Boolean; SPos, SID: LongWord; @@ -2523,32 +2897,35 @@ begin if not gGameOn then Exit; if gTriggers = nil then Exit; - SID := e_Raw_Read_LongWord(P); - SPlaying := e_Raw_Read_Byte(P) <> 0; - SPos := e_Raw_Read_LongWord(P); - SCount := e_Raw_Read_LongInt(P); + SID := M.ReadLongWord(); + SPlaying := M.ReadByte() <> 0; + SPos := M.ReadLongWord(); + SCount := M.ReadLongInt(); for I := Low(gTriggers) to High(gTriggers) do if gTriggers[I].TriggerType = TRIGGER_SOUND then if gTriggers[I].ClientID = SID then with gTriggers[I] do begin - if SPlaying then + if Sound <> nil then begin - if Data.Local then - Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0) + if SPlaying then + begin + if tgcLocal then + Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0) + else + Sound.PlayPanVolume((tgcPan-127.0)/128.0, tgcVolume/255.0); + Sound.SetPosition(SPos); + end else - Sound.PlayPanVolume((Data.Pan-127.0)/128.0, Data.Volume/255.0); - Sound.SetPosition(SPos); - end - else - if Sound.IsPlaying then Sound.Stop; + if Sound.IsPlaying then Sound.Stop; + end; SoundPlayCount := SCount; end; end; -procedure MC_RECV_TriggerMusic(P: Pointer); +procedure MC_RECV_TriggerMusic(var M: TMsg); var MName: string; MPlaying: Boolean; @@ -2557,16 +2934,17 @@ var begin if not gGameOn then Exit; - MName := e_Raw_Read_String(P); - MPlaying := e_Raw_Read_Byte(P) <> 0; - MPos := e_Raw_Read_LongWord(P); - MPaused := e_Raw_Read_Byte(P) <> 0; + MName := M.ReadString(); + MPlaying := M.ReadByte() <> 0; + MPos := M.ReadLongWord(); + MPaused := M.ReadByte() <> 0; + MPos := MPos+1; //k8: stfu, fpc! if MPlaying then begin gMusic.SetByName(MName); gMusic.Play(True); - gMusic.SetPosition(MPos); + // gMusic.SetPosition(MPos); gMusic.SpecPause := MPaused; end else @@ -2575,46 +2953,42 @@ end; // MONSTERS -procedure MC_RECV_MonsterSpawn(P: Pointer); +procedure MC_RECV_MonsterSpawn(var M: TMsg); var ID: Word; MType, MState, MDir, MAnim, MBehav: Byte; X, Y, VX, VY, MTargTime, MHealth, MAmmo, MSleep: Integer; MTarg: Word; - M: TMonster; + Mon: TMonster; begin - ID := e_Raw_Read_Word(P); - M := g_Monsters_Get(ID); - if M <> nil then + ID := M.ReadWord(); + Mon := g_Monsters_ByUID(ID); + if Mon <> nil then Exit; - MType := e_Raw_Read_Byte(P); - MState := e_Raw_Read_Byte(P); - MAnim := e_Raw_Read_Byte(P); - MTarg := e_Raw_Read_Word(P); - MTargTime := e_Raw_Read_LongInt(P); - MBehav := e_Raw_Read_Byte(P); - MSleep := e_Raw_Read_LongInt(P); - MHealth := e_Raw_Read_LongInt(P); - MAmmo := e_Raw_Read_LongInt(P); - - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); - VX := e_Raw_Read_LongInt(P); - VY := e_Raw_Read_LongInt(P); - MDir := e_Raw_Read_Byte(P); + MType := M.ReadByte(); + MState := M.ReadByte(); + MAnim := M.ReadByte(); + MTarg := M.ReadWord(); + MTargTime := M.ReadLongInt(); + MBehav := M.ReadByte(); + MSleep := M.ReadLongInt(); + MHealth := M.ReadLongInt(); + MAmmo := M.ReadLongInt(); + + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + VX := M.ReadLongInt(); + VY := M.ReadLongInt(); + MDir := M.ReadByte(); g_Monsters_Create(MType, X, Y, TDirection(MDir), False, ID); - M := g_Monsters_Get(ID); - if M = nil then + Mon := g_Monsters_ByUID(ID); + if Mon = nil then Exit; - with M do + with Mon do begin - GameX := X; - GameY := Y; - GameVelX := VX; - GameVelY := VY; MonsterAnim := MAnim; MonsterTargetUID := MTarg; @@ -2625,183 +2999,187 @@ begin SetHealth(MHealth); SetState(MState); + + setPosition(X, Y); // this will call positionChanged(); + GameVelX := VX; + GameVelY := VY; end; end; -procedure MC_RECV_MonsterPos(P: Pointer); +procedure MC_RECV_MonsterPos(var M: TMsg); var - M: TMonster; + Mon: TMonster; ID: Word; + X, Y: Integer; begin - ID := e_Raw_Read_Word(P); - M := g_Monsters_Get(ID); - if M = nil then + ID := M.ReadWord(); + Mon := g_Monsters_ByUID(ID); + if Mon = nil then Exit; - with M do + with Mon do begin - GameX := e_Raw_Read_LongInt(P); - GameY := e_Raw_Read_LongInt(P); - GameVelX := e_Raw_Read_LongInt(P); - GameVelY := e_Raw_Read_LongInt(P); - GameDirection := TDirection(e_Raw_Read_Byte(P)); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + Mon.setPosition(X, Y); // this will call `positionChanged()` + GameVelX := M.ReadLongInt(); + GameVelY := M.ReadLongInt(); + GameDirection := TDirection(M.ReadByte()); end; end; -procedure MC_RECV_MonsterState(P: Pointer); +procedure MC_RECV_MonsterState(var M: TMsg); var - ID: Integer; + ID, OldFire: Integer; MState, MFAnm: Byte; - M: TMonster; + Mon: TMonster; AnimRevert: Boolean; begin - ID := e_Raw_Read_Word(P); - M := g_Monsters_Get(ID); - if M = nil then Exit; + ID := M.ReadWord(); + Mon := g_Monsters_ByUID(ID); + if Mon = nil then Exit; - MState := e_Raw_Read_Byte(P); - MFAnm := e_Raw_Read_Byte(P); + MState := M.ReadByte(); + MFAnm := M.ReadByte(); - with M do + with Mon do begin - MonsterTargetUID := e_Raw_Read_Word(P); - MonsterTargetTime := e_Raw_Read_LongInt(P); - MonsterSleep := e_Raw_Read_LongInt(P); - MonsterHealth := e_Raw_Read_LongInt(P); - MonsterAmmo := e_Raw_Read_LongInt(P); - MonsterPain := e_Raw_Read_LongInt(P); - AnimRevert := e_Raw_Read_Byte(P) <> 0; - FFireTime := e_Raw_Read_LongInt(P); + MonsterTargetUID := M.ReadWord(); + MonsterTargetTime := M.ReadLongInt(); + MonsterSleep := M.ReadLongInt(); + MonsterHealth := M.ReadLongInt(); + MonsterAmmo := M.ReadLongInt(); + MonsterPain := M.ReadLongInt(); + AnimRevert := M.ReadByte() <> 0; + OldFire := FFireTime; + FFireTime := M.ReadLongInt(); + if (OldFire <= 0) and (FFireTime > 0) then + g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y); RevertAnim(AnimRevert); if MonsterState <> MState then begin - if (MState = MONSTATE_GO) and (MonsterState = MONSTATE_SLEEP) then - WakeUpSound; - if (MState = MONSTATE_DIE) then - DieSound; - if (MState = MONSTATE_PAIN) then - MakeBloodSimple(Min(200, MonsterPain)); - if (MState = MONSTATE_ATTACK) then - kick(nil); - if (MState = MONSTATE_DEAD) then - SetDeadAnim; + if (MState = MONSTATE_GO) and (MonsterState = MONSTATE_SLEEP) then WakeUpSound(); + if (MState = MONSTATE_DIE) then DieSound(); + if (MState = MONSTATE_PAIN) then MakeBloodSimple(Min(200, MonsterPain)); + if (MState = MONSTATE_ATTACK) then kick(nil); + if (MState = MONSTATE_DEAD) then SetDeadAnim(); SetState(MState, MFAnm); end; end; end; -procedure MC_RECV_MonsterShot(P: Pointer); +procedure MC_RECV_MonsterShot(var M: TMsg); var ID: Integer; - M: TMonster; + Mon: TMonster; X, Y, VX, VY: Integer; begin - ID := e_Raw_Read_Word(P); + ID := M.ReadWord(); - M := g_Monsters_Get(ID); - if M = nil then Exit; + Mon := g_Monsters_ByUID(ID); + if Mon = nil then Exit; - X := e_Raw_Read_LongInt(P); - Y := e_Raw_Read_LongInt(P); - VX := e_Raw_Read_LongInt(P); - VY := e_Raw_Read_LongInt(P); + X := M.ReadLongInt(); + Y := M.ReadLongInt(); + VX := M.ReadLongInt(); + VY := M.ReadLongInt(); - M.ClientAttack(X, Y, VX, VY); + Mon.ClientAttack(X, Y, VX, VY); end; -procedure MC_RECV_MonsterDelete(P: Pointer); +procedure MC_RECV_MonsterDelete(var M: TMsg); var ID: Integer; - M: TMonster; + Mon: TMonster; begin - ID := e_Raw_Read_Word(P); - M := g_Monsters_Get(ID); - if M = nil then Exit; - - gMonsters[ID].SetState(5); - gMonsters[ID].MonsterRemoved := True; + ID := M.ReadWord(); + Mon := g_Monsters_ByUID(ID); + if Mon = nil then Exit; + Mon.SetState(5); + Mon.MonsterRemoved := True; end; -procedure MC_RECV_TimeSync(P: Pointer); +procedure MC_RECV_TimeSync(var M: TMsg); var Time: LongWord; begin - Time := e_Raw_Read_LongWord(P); + Time := M.ReadLongWord(); if gState = STATE_INTERCUSTOM then gServInterTime := Min(Time, 255); end; -procedure MC_RECV_VoteEvent(P: Pointer); +procedure MC_RECV_VoteEvent(var M: TMsg); var EvID: Byte; Str1, Str2: string; Int1, Int2: SmallInt; begin - EvID := e_Raw_Read_Byte(P); - Int1 := e_Raw_Read_SmallInt(P); - Int2 := e_Raw_Read_SmallInt(P); - Str1 := e_Raw_Read_String(P); - Str2 := e_Raw_Read_String(P); - - case EvID of - NET_VE_STARTED: - g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True); - NET_VE_PASSED: - g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True); - NET_VE_FAILED: - g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True); - NET_VE_VOTE: - g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True); - NET_VE_INPROGRESS: - g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True); - end; + EvID := M.ReadByte(); + Int1 := M.ReadSmallInt(); + Int2 := M.ReadSmallInt(); + Str1 := M.ReadString(); + Str2 := M.ReadString(); + + if NetDeafLevel < 2 then + case EvID of + NET_VE_STARTED: + g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True); + NET_VE_PASSED: + g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True); + NET_VE_FAILED: + g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True); + NET_VE_VOTE: + g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True); + NET_VE_INPROGRESS: + g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True); + end; end; // CLIENT SEND procedure MC_SEND_Info(Password: string); -begin - e_Buffer_Clear(@NetOut); - - e_Buffer_Write(@NetOut, Byte(NET_MSG_INFO)); - e_Buffer_Write(@NetOut, GAME_VERSION); - e_Buffer_Write(@NetOut, Password); - e_Buffer_Write(@NetOut, gPlayer1Settings.Name); - e_Buffer_Write(@NetOut, gPlayer1Settings.Model); - e_Buffer_Write(@NetOut, gPlayer1Settings.Color.R); - e_Buffer_Write(@NetOut, gPlayer1Settings.Color.G); - e_Buffer_Write(@NetOut, gPlayer1Settings.Color.B); - e_Buffer_Write(@NetOut, gPlayer1Settings.Team); +var i: Integer; +begin + NetOut.Clear(); + + NetOut.Write(Byte(NET_MSG_INFO)); + NetOut.Write(GAME_VERSION); + NetOut.Write(Password); + NetOut.Write(gPlayer1Settings.Name); + NetOut.Write(gPlayer1Settings.Model); + NetOut.Write(gPlayer1Settings.Color.R); + NetOut.Write(gPlayer1Settings.Color.G); + NetOut.Write(gPlayer1Settings.Color.B); + NetOut.Write(gPlayer1Settings.Team); + NetOut.Write(gPlayer1Settings.WeaponSwitch); + if (gPlayer1Settings.WeaponSwitch = 2) then + for i := WP_FIRST to WP_LAST + 1 do + NetOut.Write(gPlayer1Settings.WeaponPreferences[i]); + NetOut.Write(gPlayer1Settings.SwitchToEmpty); g_Net_Client_Send(True, NET_CHAN_SERVICE); end; procedure MC_SEND_Chat(Txt: string; Mode: Byte); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT)); - e_Buffer_Write(@NetOut, Txt); - e_Buffer_Write(@NetOut, Mode); + NetOut.Write(Byte(NET_MSG_CHAT)); + NetOut.Write(Txt); + NetOut.Write(Mode); g_Net_Client_Send(True, NET_CHAN_CHAT); end; -function isKeyPressed (key1: Word; key2: Word): Boolean; -begin - if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end; - if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end; - result := false; -end; - procedure MC_SEND_PlayerPos(); var kByte: Word; Predict: Boolean; strafeDir: Byte; + WeaponAct: Byte = 0; WeaponSelect: Word = 0; - I: Integer; + i: Integer; begin if not gGameOn then Exit; if gPlayers = nil then Exit; @@ -2814,73 +3192,96 @@ begin begin strafeDir := P1MoveButton shr 4; P1MoveButton := P1MoveButton and $0F; - with gGameControls.P1Control do + + if gPlayerAction[0, ACTION_MOVELEFT] and (not gPlayerAction[0, ACTION_MOVERIGHT]) then + P1MoveButton := 1 + else if (not gPlayerAction[0, ACTION_MOVELEFT]) and gPlayerAction[0, ACTION_MOVERIGHT] then + P1MoveButton := 2 + else if (not gPlayerAction[0, ACTION_MOVELEFT]) and (not gPlayerAction[0, ACTION_MOVERIGHT]) then + P1MoveButton := 0; + + // strafing + if gPlayerAction[0, ACTION_STRAFE] then begin - if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then P1MoveButton := 1 - else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then P1MoveButton := 2 - else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then P1MoveButton := 0; + // new strafe mechanics + if (strafeDir = 0) then + strafeDir := P1MoveButton; // start strafing + // now set direction according to strafe (reversed) + if (strafeDir = 2) then + gPlayer1.SetDirection(TDirection.D_LEFT) + else if (strafeDir = 1) then + gPlayer1.SetDirection(TDirection.D_RIGHT) + end + else + begin + strafeDir := 0; // not strafing anymore + if (P1MoveButton = 2) and gPlayerAction[0, ACTION_MOVELEFT] then + gPlayer1.SetDirection(TDirection.D_LEFT) + else if (P1MoveButton = 1) and gPlayerAction[0, ACTION_MOVERIGHT] then + gPlayer1.SetDirection(TDirection.D_RIGHT) + else if P1MoveButton <> 0 then + gPlayer1.SetDirection(TDirection(P1MoveButton-1)); + end; - // strafing - if isKeyPressed(KeyStrafe, KeyStrafe2) then + gPlayer1.ReleaseKeys; + if P1MoveButton = 1 then + begin + kByte := kByte or NET_KEY_LEFT; + if Predict then gPlayer1.PressKey(KEY_LEFT, 10000); + end; + if P1MoveButton = 2 then + begin + kByte := kByte or NET_KEY_RIGHT; + if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000); + end; + if gPlayerAction[0, ACTION_LOOKUP] then + begin + kByte := kByte or NET_KEY_UP; + gPlayer1.PressKey(KEY_UP, 10000); + end; + if gPlayerAction[0, ACTION_LOOKDOWN] then + begin + kByte := kByte or NET_KEY_DOWN; + gPlayer1.PressKey(KEY_DOWN, 10000); + end; + if gPlayerAction[0, ACTION_JUMP] then + begin + kByte := kByte or NET_KEY_JUMP; + // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option + end; + if gPlayerAction[0, ACTION_ATTACK] then kByte := kByte or NET_KEY_FIRE; + if gPlayerAction[0, ACTION_ACTIVATE] then kByte := kByte or NET_KEY_OPEN; + + for i := WP_FACT to WP_LACT do + begin + if gWeaponAction[0, i] then begin - // new strafe mechanics - if (strafeDir = 0) then strafeDir := P1MoveButton; // start strafing - // now set direction according to strafe (reversed) - if (strafeDir = 2) then gPlayer1.SetDirection(D_LEFT) - else if (strafeDir = 1) then gPlayer1.SetDirection(D_RIGHT); + WeaponAct := WeaponAct or Byte(1 shl i); + gWeaponAction[0, i] := False end - else - begin - if (P1MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then gPlayer1.SetDirection(D_LEFT) - else if (P1MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then gPlayer1.SetDirection(D_RIGHT) - else if P1MoveButton <> 0 then gPlayer1.SetDirection(TDirection(P1MoveButton-1)); - end; + end; - gPlayer1.ReleaseKeys; - if P1MoveButton = 1 then - begin - kByte := kByte or NET_KEY_LEFT; - if Predict then gPlayer1.PressKey(KEY_LEFT, 10000); - end; - if P1MoveButton = 2 then - begin - kByte := kByte or NET_KEY_RIGHT; - if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000); - end; - if isKeyPressed(KeyUp, KeyUp2) then - begin - kByte := kByte or NET_KEY_UP; - gPlayer1.PressKey(KEY_UP, 10000); - end; - if isKeyPressed(KeyDown, KeyDown2) then - begin - kByte := kByte or NET_KEY_DOWN; - gPlayer1.PressKey(KEY_DOWN, 10000); - end; - if isKeyPressed(KeyJump, KeyJump2) then + for i := WP_FIRST to WP_LAST do + begin + if gSelectWeapon[0, i] then begin - kByte := kByte or NET_KEY_JUMP; - // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option - end; - if isKeyPressed(KeyFire, KeyFire2) then kByte := kByte or NET_KEY_FIRE; - if isKeyPressed(KeyOpen, KeyOpen2) then kByte := kByte or NET_KEY_OPEN; - if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then kByte := kByte or NET_KEY_NW; - if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then kByte := kByte or NET_KEY_PW; - for I := 0 to High(KeyWeapon) do - if isKeyPressed(KeyWeapon[I], KeyWeapon2[I]) then - WeaponSelect := WeaponSelect or Word(1 shl I); + WeaponSelect := WeaponSelect or Word(1 shl i); + gSelectWeapon[0, i] := False + end end; + // fix movebutton state P1MoveButton := P1MoveButton or (strafeDir shl 4); end else kByte := NET_KEY_CHAT; - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRPOS)); - e_Buffer_Write(@NetOut, gTime); - e_Buffer_Write(@NetOut, kByte); - e_Buffer_Write(@NetOut, Byte(gPlayer1.Direction)); - e_Buffer_Write(@NetOut, WeaponSelect); + NetOut.Write(Byte(NET_MSG_PLRPOS)); + NetOut.Write(gTime); + NetOut.Write(kByte); + NetOut.Write(Byte(gPlayer1.Direction)); + NetOut.Write(WeaponAct); + NetOut.Write(WeaponSelect); //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING); g_Net_Client_Send(True, NET_CHAN_PLAYERPOS); @@ -2890,249 +3291,58 @@ end; procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a'); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_VOTE_EVENT)); - e_Buffer_Write(@NetOut, Byte(Start)); - e_Buffer_Write(@NetOut, Command); + NetOut.Write(Byte(NET_MSG_VOTE_EVENT)); + NetOut.Write(Byte(Start)); + NetOut.Write(Command); g_Net_Client_Send(True, NET_CHAN_IMPORTANT); end; procedure MC_SEND_PlayerSettings(); -begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSET)); - e_Buffer_Write(@NetOut, gPlayer1Settings.Name); - e_Buffer_Write(@NetOut, gPlayer1Settings.Model); - e_Buffer_Write(@NetOut, gPlayer1Settings.Color.R); - e_Buffer_Write(@NetOut, gPlayer1Settings.Color.G); - e_Buffer_Write(@NetOut, gPlayer1Settings.Color.B); - e_Buffer_Write(@NetOut, gPlayer1Settings.Team); - +var i: Integer; +begin + NetOut.Write(Byte(NET_MSG_PLRSET)); + NetOut.Write(gPlayer1Settings.Name); + NetOut.Write(gPlayer1Settings.Model); + NetOut.Write(gPlayer1Settings.Color.R); + NetOut.Write(gPlayer1Settings.Color.G); + NetOut.Write(gPlayer1Settings.Color.B); + NetOut.Write(gPlayer1Settings.Team); + NetOut.Write(gPlayer1Settings.WeaponSwitch); + if (gPlayer1Settings.WeaponSwitch = 2) then + for i := WP_FIRST to WP_LAST + 1 do + NetOut.Write(gPlayer1Settings.WeaponPreferences[i]); + NetOut.Write(gPlayer1Settings.SwitchToEmpty); g_Net_Client_Send(True, NET_CHAN_IMPORTANT); end; procedure MC_SEND_FullStateRequest(); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_REQFST)); + NetOut.Write(Byte(NET_MSG_REQFST)); g_Net_Client_Send(True, NET_CHAN_SERVICE); end; procedure MC_SEND_CheatRequest(Kind: Byte); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_CHEAT)); - e_Buffer_Write(@NetOut, Kind); + NetOut.Write(Byte(NET_MSG_CHEAT)); + NetOut.Write(Kind); g_Net_Client_Send(True, NET_CHAN_IMPORTANT); end; procedure MC_SEND_RCONPassword(Password: string); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_RCON_AUTH)); - e_Buffer_Write(@NetOut, Password); + NetOut.Write(Byte(NET_MSG_RCON_AUTH)); + NetOut.Write(Password); g_Net_Client_Send(True, NET_CHAN_SERVICE); end; procedure MC_SEND_RCONCommand(Cmd: string); begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_RCON_CMD)); - e_Buffer_Write(@NetOut, Cmd); + NetOut.Write(Byte(NET_MSG_RCON_CMD)); + NetOut.Write(Cmd); g_Net_Client_Send(True, NET_CHAN_SERVICE); end; -// i have no idea why all this stuff is in here - -function ReadFile(const FileName: TFileName): AByte; -var - FileStream : TStream; - fname: string; -begin - e_WriteLog(Format('NETWORK: looking for file "%s"', [FileName]), MSG_NOTIFY); - fname := findDiskWad(FileName); - if length(fname) = 0 then - begin - e_WriteLog(Format('NETWORK: file "%s" not found!', [FileName]), MSG_FATALERROR); - SetLength(Result, 0); - exit; - end; - e_WriteLog(Format('NETWORK: found file "%s"', [fname]), MSG_NOTIFY); - Result := nil; - FileStream := openDiskFileRO(fname); - try - if FileStream.Size > 0 then - begin - SetLength(Result, FileStream.Size); - FileStream.Read(Result[0], FileStream.Size); - end; - finally - FileStream.Free; - end; -end; - -function CreateMapDataMsg(const FileName: TFileName; ResList: TStringList): TMapDataMsg; -var - i: Integer; -begin - Result.MsgId := NET_MSG_MAP_RESPONSE; - Result.FileData := ReadFile(FileName); - Result.FileSize := Length(Result.FileData); - - SetLength(Result.ExternalResources, ResList.Count); - for i:=0 to ResList.Count-1 do - begin - Result.ExternalResources[i].Name := ResList.Strings[i]; - Result.ExternalResources[i].md5 := MD5File(GameDir+'/wads/'+ResList.Strings[i]); - end; -end; - -procedure ResDataMsgToBytes(var bytes: AByte; const ResData: TResDataMsg); -var - ResultStream: TMemoryStream; -begin - ResultStream := TMemoryStream.Create; - - ResultStream.WriteBuffer(ResData.MsgId, SizeOf(ResData.MsgId)); //msgId - ResultStream.WriteBuffer(ResData.FileSize, SizeOf(ResData.FileSize)); //file size - ResultStream.WriteBuffer(ResData.FileData[0], ResData.FileSize); //file data - - SetLength(bytes, ResultStream.Size); - ResultStream.Seek(0, soFromBeginning); - ResultStream.ReadBuffer(bytes[0], ResultStream.Size); - - ResultStream.Free; -end; - -function ResDataFromMsgStream(msgStream: TMemoryStream):TResDataMsg; -begin - msgStream.ReadBuffer(Result.MsgId, SizeOf(Result.MsgId)); - msgStream.ReadBuffer(Result.FileSize, SizeOf(Result.FileSize)); - SetLength(Result.FileData, Result.FileSize); - msgStream.ReadBuffer(Result.FileData[0], Result.FileSize); -end; - -procedure MapDataMsgToBytes(var bytes: AByte; const MapDataMsg: TMapDataMsg); -var - ResultStream: TMemoryStream; - resCount: Integer; -begin - resCount := Length(MapDataMsg.ExternalResources); - - ResultStream := TMemoryStream.Create; - - ResultStream.WriteBuffer(MapDataMsg.MsgId, SizeOf(MapDataMsg.MsgId)); //msgId - ResultStream.WriteBuffer(MapDataMsg.FileSize, SizeOf(MapDataMsg.FileSize)); //file size - ResultStream.WriteBuffer(MapDataMsg.FileData[0], MapDataMsg.FileSize); //file data - - ResultStream.WriteBuffer(resCount, SizeOf(resCount)); //res count - ResultStream.WriteBuffer(MapDataMsg.ExternalResources[0], resCount*SizeOf(TExternalResourceInfo)); //res data - - SetLength(bytes, ResultStream.Size); - ResultStream.Seek(0, soFromBeginning); - ResultStream.ReadBuffer(bytes[0], ResultStream.Size); - - ResultStream.Free; -end; - -function MapDataFromMsgStream(msgStream: TMemoryStream):TMapDataMsg; -var - resCount: Integer; -begin - msgStream.ReadBuffer(Result.MsgId, SizeOf(Result.MsgId)); - msgStream.ReadBuffer(Result.FileSize, SizeOf(Result.FileSize)); //file size - - SetLength(Result.FileData, Result.FileSize); - msgStream.ReadBuffer(Result.FileData[0], Result.FileSize); //file data - - msgStream.ReadBuffer(resCount, SizeOf(resCount)); //res count - SetLength(Result.ExternalResources, resCount); - - msgStream.ReadBuffer(Result.ExternalResources[0], resCount * SizeOf(TExternalResourceInfo)); //res data -end; - -function IsValidFileName(const S: String): Boolean; -const - Forbidden: set of Char = ['<', '>', '|', '"', ':', '*', '?']; -var - I: Integer; -begin - Result := S <> ''; - for I := 1 to Length(S) do - Result := Result and (not(S[I] in Forbidden)); -end; - -function IsValidFilePath(const S: String): Boolean; -var - I: Integer; -begin - Result := False; - if not IsValidFileName(S) then exit; - if FileExists(S) then exit; - I := LastDelimiter('\/', S); - if (I > 0) then - if (not DirectoryExists(Copy(S, 1, I-1))) then - exit; - Result := True; -end; - -procedure MC_SEND_MapRequest(); -begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_MAP_REQUEST)); - g_Net_Client_Send(True, NET_CHAN_IMPORTANT); -end; - -procedure MC_SEND_ResRequest(const resName: AnsiString); -begin - e_Buffer_Write(@NetOut, Byte(NET_MSG_RES_REQUEST)); - e_Buffer_Write(@NetOut, resName); - g_Net_Client_Send(True, NET_CHAN_IMPORTANT); -end; - -procedure MH_RECV_MapRequest(C: pTNetClient; P: Pointer); -var - payload: AByte; - peer: pENetPeer; - mapDataMsg: TMapDataMsg; -begin - e_WriteLog('NET: Received map request from ' + - DecodeIPV4(C.Peer.address.host), MSG_NOTIFY); - - mapDataMsg := CreateMapDataMsg(MapsDir + gGameSettings.WAD, gExternalResources); - peer := NetClients[C.ID].Peer; - - MapDataMsgToBytes(payload, mapDataMsg); - g_Net_SendData(payload, peer, True, NET_CHAN_DOWNLOAD); - - payload := nil; - mapDataMsg.FileData := nil; - mapDataMsg.ExternalResources := nil; -end; - -procedure MH_RECV_ResRequest(C: pTNetClient; P: Pointer); -var - payload: AByte; - peer: pENetPeer; - FileName: String; - resDataMsg: TResDataMsg; -begin - FileName := ExtractFileName(e_Raw_Read_String(P)); - e_WriteLog('NET: Received res request: ' + FileName + - ' from ' + DecodeIPV4(C.Peer.address.host), MSG_NOTIFY); - - if not IsValidFilePath(FileName) then - begin - e_WriteLog('Invalid filename: ' + FileName, MSG_WARNING); - exit; - end; - - peer := NetClients[C.ID].Peer; - - if gExternalResources.IndexOf(FileName) > -1 then - begin - resDataMsg.MsgId := NET_MSG_RES_RESPONSE; - resDataMsg.FileData := ReadFile(GameDir+'/wads/'+FileName); - resDataMsg.FileSize := Length(resDataMsg.FileData); - - ResDataMsgToBytes(payload, resDataMsg); - g_Net_SendData(payload, peer, True, NET_CHAN_DOWNLOAD); - end; -end; end.