X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_netmsg.pas;h=6133b001e2d5fd2df4777195d2bfe433b232db3c;hb=2304c541d7bdbf7de389437482ecdff37fc7fbd5;hp=e9cc68c2ee55ac7fcb0067073ed725ecdedd6e9e;hpb=249437fb26187bc1db9b35f59ad597e169ec9c01;p=d2df-sdl.git diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas index e9cc68c..6133b00 100644 --- a/src/game/g_netmsg.pas +++ b/src/game/g_netmsg.pas @@ -2,8 +2,7 @@ * * 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 @@ -16,8 +15,6 @@ {$INCLUDE ../shared/a_modes.inc} unit g_netmsg; -{.$DEFINE K8_XXX_WEAPON_DEBUG} - interface uses e_msg, g_net, g_triggers, Classes, SysUtils, md5; @@ -69,10 +66,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; @@ -111,6 +110,8 @@ 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_VE_STARTED = 1; NET_VE_PASSED = 2; @@ -126,11 +127,14 @@ const NET_CHEAT_SUICIDE = 1; NET_CHEAT_SPECTATE = 2; + NET_CHEAT_READY = 3; NET_MAX_DIFFTIME = 5000 div 36; // HOST MESSAGES +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); @@ -139,12 +143,12 @@ 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_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); @@ -241,8 +245,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 @@ -263,8 +268,9 @@ 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 @@ -292,6 +298,33 @@ const //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 // @@ -383,6 +416,7 @@ begin end; C^.Player := PID; + C^.WaitForFirstSpawn := false; g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True); e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) + @@ -401,44 +435,93 @@ begin FNoRespawn := True; Spectate; FWantsInGame := True; // TODO: look into this later + C^.WaitForFirstSpawn := true; end else - Respawn(gGameSettings.GameType = GT_SINGLE); + begin + 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; 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_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; + 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 + plr.Respawn(False); +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; var M: TMsg): Word; var - Dir{, i}: Byte; - //WeaponSelect: Word; + Dir, i: Byte; + WeaponSelect: Word; PID: Word; kByte: Word; Pl: TPlayer; GT: LongWord; - newweapon: Byte; begin Result := 0; if not gGameOn then Exit; @@ -456,22 +539,13 @@ begin NetTime := GT; kByte := M.ReadWord(); Dir := M.ReadByte(); - //WeaponSelect := M.ReadWord(); - newweapon := M.ReadByte(); - if (newweapon <> CurrWeap) then - begin -{$IFDEF K8_XXX_WEAPON_DEBUG} - writeln('HOST PLRPOS: got: currweap=', CurrWeap, '; curfrm=', gTime, '; netweap=', newweapon, '; oldweap=', CurrWeap); -{$ENDIF} - //NetForceWeap := newweapon; - SetWeaponHost(newweapon); - end; + WeaponSelect := M.ReadWord(); //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING); if Direction <> TDirection(Dir) then JustTeleported := False; SetDirection(TDirection(Dir)); - ReleaseKeysNoWeapon(); + ReleaseKeys; if kByte = NET_KEY_CHAT then begin @@ -486,10 +560,9 @@ 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); + 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 15 do begin if (WeaponSelect and Word(1 shl i)) <> 0 then @@ -498,7 +571,6 @@ begin QueueWeaponSwitch(i); end; end; - *) end; // MH_SEND_PlayerPos(False, PID, C^.ID); @@ -524,6 +596,21 @@ begin 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; end; end; @@ -640,7 +727,7 @@ 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 @@ -664,6 +751,12 @@ procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_E 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 @@ -723,8 +816,11 @@ begin if gLMSRespawn > LMS_RESPAWN_NONE then begin + e_LogWritefln('*** client #%u (cid #%u) WARMUP', [ID, NetClients[ID].Player]); MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000, 'N', ID); end; + + g_Net_Flush(); end; procedure MH_SEND_Info(ID: Byte); @@ -1037,7 +1133,6 @@ procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE); var P: TPlayer; I: Integer; - ww: Word; begin P := g_Player_Get(PID); if P = nil then Exit; @@ -1056,10 +1151,8 @@ begin NetOut.Write(Lives); NetOut.Write(Team); - // collect all weapons in one word - ww := 0; - for I := WP_FIRST to WP_LAST do if (FWeapon[I]) then ww := ww or (1 shl (i-WP_FIRST)); - NetOut.Write(Word(ww)); + for I := WP_FIRST to WP_LAST do + NetOut.Write(Byte(FWeapon[I])); for I := A_BULLETS to A_HIGH do NetOut.Write(FAmmo[I]); @@ -1070,31 +1163,24 @@ begin for I := MR_SUIT to MR_MAX do NetOut.Write(LongWord(FMegaRulez[I])); - // collect all special flags in one byte - ww := 0; - if (R_ITEM_BACKPACK in FRulez) then ww := ww or $01; - if (R_KEY_RED in FRulez) then ww := ww or $02; - if (R_KEY_GREEN in FRulez) then ww := ww or $04; - if (R_KEY_BLUE in FRulez) then ww := ww or $08; - if (R_BERSERK in FRulez) then ww := ww or $10; - NetOut.Write(Byte(ww)); + 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(CurrWeap)); - - // other flags - ww := 0; - if (FSpectator) then ww := ww or $01; - if (FGhost) then ww := ww or $02; - if (FPhysics) then ww := ww or $04; - if (FNoRespawn) then ww := ww or $08; - if (FJetpack) then ww := ww or $10; - NetOut.Write(Byte(ww)); + 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)); end; g_Net_Host_Send(ID, True, NET_CHAN_PLAYER); @@ -1668,6 +1754,7 @@ var i1, i2: TStrings_Locale; pln: String; cnt: Byte; + goodCmd: Boolean = true; begin FillChar(EvHash, Sizeof(EvHash), 0); EvType := M.ReadByte(); @@ -1683,33 +1770,60 @@ begin 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; + 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 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; + gWADHash := EvHash; + if not g_Game_StartMap(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; + 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: @@ -1733,10 +1847,16 @@ begin 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); @@ -1855,6 +1975,21 @@ 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; @@ -1863,7 +1998,7 @@ var PID: Word; Pl: TPlayer; EvType: Byte; - Fl: Byte; + Fl, a: Byte; Quiet: Boolean; s, ts: string; begin @@ -1900,6 +2035,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: @@ -1915,6 +2060,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: @@ -1930,6 +2085,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: @@ -1948,6 +2113,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: @@ -1961,6 +2136,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; end; end; @@ -2043,8 +2228,6 @@ begin Exit; end; gTime := GT; - if g_Game_IsClient and (gPlayer1 <> nil) and (gPlayer1.NetForceWeapFIdx >= gTime+15) then gPlayer1.NetForceWeapFIdx := 0; - if g_Game_IsClient and (gPlayer2 <> nil) and (gPlayer2.NetForceWeapFIdx >= gTime+15) then gPlayer2.NetForceWeapFIdx := 0; PID := M.ReadWord(); Pl := g_Player_Get(PID); @@ -2063,7 +2246,7 @@ begin TmpX := M.ReadLongInt(); TmpY := M.ReadLongInt(); - ReleaseKeysNoWeapon; + ReleaseKeys; if (kByte = NET_KEY_CHAT) then PressKey(KEY_CHAT, 10000) @@ -2092,11 +2275,9 @@ 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; - ww: Word; - newweapon: Byte; begin PID := M.ReadWord(); Pl := g_Player_Get(PID); @@ -2115,12 +2296,8 @@ begin Lives := M.ReadByte(); NewTeam := M.ReadByte(); - ww := M.ReadWord(); for I := WP_FIRST to WP_LAST do - begin - FWeapon[I] := ((ww and $01) <> 0); - ww := ww shr 1; - end; + FWeapon[I] := (M.ReadByte() <> 0); for I := A_BULLETS to A_HIGH do FAmmo[I] := M.ReadWord(); @@ -2132,32 +2309,23 @@ begin FMegaRulez[I] := M.ReadLongWord(); FRulez := []; - // unpack special flags - ww := M.ReadByte(); - if ((ww and $01) <> 0) then FRulez := FRulez+[R_ITEM_BACKPACK]; - if ((ww and $02) <> 0) then FRulez := FRulez+[R_KEY_RED]; - if ((ww and $04) <> 0) then FRulez := FRulez+[R_KEY_GREEN]; - if ((ww and $08) <> 0) then FRulez := FRulez+[R_KEY_BLUE]; - if ((ww and $10) <> 0) then FRulez := FRulez+[R_BERSERK]; + if (M.ReadByte() <> 0) then + FRulez := FRulez + [R_ITEM_BACKPACK]; + if (M.ReadByte() <> 0) then + FRulez := FRulez + [R_KEY_RED]; + if (M.ReadByte() <> 0) then + FRulez := FRulez + [R_KEY_GREEN]; + if (M.ReadByte() <> 0) then + FRulez := FRulez + [R_KEY_BLUE]; + if (M.ReadByte() <> 0) then + FRulez := FRulez + [R_BERSERK]; Frags := M.ReadLongInt(); Death := M.ReadLongInt(); - newweapon := M.ReadByte(); -{$IFDEF K8_XXX_WEAPON_DEBUG} - writeln('CLIENT PLRSTATS: got: currweap=', CurrWeap, '; curfrm=', gTime, '; netweap=', newweapon, '; lastnwfrm=', NetForceWeapFIdx); -{$ENDIF} - if (gTime >= NetForceWeapFIdx) then - begin - //NetForceWeap := newweapon; - SetWeaponHost(newweapon); - end; - //SetWeapon(M.ReadByte()); - - // other flags - ww := M.ReadByte(); + SetWeapon(M.ReadByte()); - FSpectator := ((ww and $01) <> 0); + FSpectator := M.ReadByte() <> 0; if FSpectator then begin if Pl = gPlayer1 then @@ -2178,17 +2346,22 @@ begin if (gPlayer2 = nil) and (gLMSPID2 > 0) then gPlayer2 := g_Player_Get(gLMSPID2); end; - - FGhost := ((ww and $02) <> 0); - FPhysics := ((ww and $04) <> 0); - FNoRespawn := ((ww and $08) <> 0); + FGhost := M.ReadByte() <> 0; + FPhysics := M.ReadByte() <> 0; + FNoRespawn := M.ReadByte() <> 0; OldJet := FJetpack; - FJetpack := ((ww and $10) <> 0); + 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; 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; @@ -2530,12 +2703,13 @@ begin 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 @@ -2621,7 +2795,7 @@ end; procedure MC_RECV_MonsterState(var M: TMsg); var - ID: Integer; + ID, OldFire: Integer; MState, MFAnm: Byte; Mon: TMonster; AnimRevert: Boolean; @@ -2642,7 +2816,10 @@ begin 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 @@ -2753,20 +2930,13 @@ begin 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; - //WeaponSelect: Word = 0; - I: Integer; + WeaponSelect: Word = 0; + i: Integer; begin if not gGameOn then Exit; if gPlayers = nil then Exit; @@ -2779,105 +2949,94 @@ 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 + // 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 - 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; + 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 - 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(TDirection.D_LEFT) - else if (strafeDir = 1) then gPlayer1.SetDirection(TDirection.D_RIGHT); - end - else - begin - if (P1MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then gPlayer1.SetDirection(TDirection.D_LEFT) - else if (P1MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then gPlayer1.SetDirection(TDirection.D_RIGHT) - else if P1MoveButton <> 0 then gPlayer1.SetDirection(TDirection(P1MoveButton-1)); - 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 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; + if gPlayerAction[0, ACTION_WEAPNEXT] then kByte := kByte or NET_KEY_NW; + if gPlayerAction[0, ACTION_WEAPPREV] then kByte := kByte or NET_KEY_PW; - gPlayer1.ReleaseKeys; - gPlayer1.weaponSwitchKeysStateChange(-1, isKeyPressed(KeyNextWeapon, KeyNextWeapon2)); - gPlayer1.weaponSwitchKeysStateChange(-2, isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2)); + gPlayerAction[0, ACTION_WEAPNEXT] := False; // HACK, remove after readyweaon&pendinweapon implementation + gPlayerAction[0, ACTION_WEAPPREV] := False; // HACK, remove after readyweaon&pendinweapon implementation - 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 - 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; - // do not send weapon switch keys, `MH_SEND_PlayerStats()` will send changed weapon anyway - if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) and gPlayer1.isWeaponSwitchKeyReleased(-1) then gPlayer1.PressKey(KEY_NEXTWEAPON); //kByte := kByte or NET_KEY_NW; - if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) and gPlayer1.isWeaponSwitchKeyReleased(-2) then gPlayer1.PressKey(KEY_PREVWEAPON); //kByte := kByte or NET_KEY_PW; - for I := 0 to High(KeyWeapon) do + for i := WP_FIRST to WP_LAST do + begin + if gSelectWeapon[0, i] then begin - if isKeyPressed(KeyWeapon[I], KeyWeapon2[I]) then - begin - //writeln('keyweapon #', i, ' is pressed; released=', Integer(gPlayer1.isWeaponSwitchKeyReleased(i)), '; frm=', gPlayer1.NetForceWeapFIdx, '; gTime=', gTime); - gPlayer1.weaponSwitchKeysStateChange(i, true); - if gPlayer1.isWeaponSwitchKeyReleased(i) then - begin - //writeln('keyweapon #', i, ' is pressed; released=', Integer(gPlayer1.isWeaponSwitchKeyReleased(i)), '; frm=', gPlayer1.NetForceWeapFIdx, '; gTime=', gTime); - gPlayer1.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best - //WeaponSelect := WeaponSelect or Word(1 shl I); - end; - end - else - begin - gPlayer1.weaponSwitchKeysStateChange(i, false); - end; - end; + 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; - gPlayer1.weaponSwitchKeysShiftNewStates(); - NetOut.Write(Byte(NET_MSG_PLRPOS)); NetOut.Write(gTime); NetOut.Write(kByte); NetOut.Write(Byte(gPlayer1.Direction)); - NetOut.Write(Byte(gPlayer1.CurrWeap)); -{$IFDEF K8_XXX_WEAPON_DEBUG} - if (kByte and NET_KEY_FIRE) <> 0 then writeln('FIRE: CurrWeap=', gPlayer1.CurrWeap); -{$ENDIF} - //e_WriteLog(Format('S:nwp=%d; nwfidx=%d', [Integer(gPlayer1.NetForceWeap), Integer(gPlayer1.NetForceWeapFIdx)]), TMsgType.Warning); - //NetOut.Write(WeaponSelect); + NetOut.Write(WeaponSelect); //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING); g_Net_Client_Send(True, NET_CHAN_PLAYERPOS); - // force player weapon - //{if (gPlayer1.NetForceWeapFIdx < gTime) then} gPlayer1.SetWeapon(gPlayer1.NetForceWeap); - //kBytePrev := kByte; //kDirPrev := gPlayer1.Direction; end; @@ -2932,201 +3091,5 @@ begin 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]), TMsgType.Notify); - fname := findDiskWad(FileName); - if length(fname) = 0 then - begin - e_WriteLog(Format('NETWORK: file "%s" not found!', [FileName]), TMsgType.Fatal); - SetLength(Result, 0); - exit; - end; - e_WriteLog(Format('NETWORK: found file "%s"', [fname]), TMsgType.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 - NetOut.Write(Byte(NET_MSG_MAP_REQUEST)); - g_Net_Client_Send(True, NET_CHAN_IMPORTANT); -end; - -procedure MC_SEND_ResRequest(const resName: AnsiString); -begin - NetOut.Write(Byte(NET_MSG_RES_REQUEST)); - NetOut.Write(resName); - g_Net_Client_Send(True, NET_CHAN_IMPORTANT); -end; - -procedure MH_RECV_MapRequest(C: pTNetClient; var M: TMsg); -var - payload: AByte; - peer: pENetPeer; - mapDataMsg: TMapDataMsg; -begin - e_WriteLog('NET: Received map request from ' + - DecodeIPV4(C^.Peer.address.host), TMsgType.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; var M: TMsg); -var - payload: AByte; - peer: pENetPeer; - FileName: String; - resDataMsg: TResDataMsg; -begin - FileName := ExtractFileName(M.ReadString()); - e_WriteLog('NET: Received res request: ' + FileName + - ' from ' + DecodeIPV4(C^.Peer.address.host), TMsgType.Notify); - - if not IsValidFilePath(FileName) then - begin - e_WriteLog('Invalid filename: ' + FileName, TMsgType.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.