X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_netmsg.pas;h=d88e6af38e973a1e3258c75985d38829b65c0615;hb=abda6900c041e39944de6a49aa088a60c170715e;hp=ec78e3020b83f0328be0831527027955a9ca6b4f;hpb=69ed21bb4a9cd4c59ba1acfd5971c7be5f854ee2;p=d2df-sdl.git diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas index ec78e30..d88e6af 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 @@ -67,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 +112,7 @@ const 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; @@ -132,6 +134,8 @@ const // 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); @@ -140,12 +144,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); @@ -242,8 +246,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 @@ -264,14 +269,15 @@ 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_graphics, e_log, - g_textures, g_gfx, g_sound, g_console, g_basic, g_options, g_main, + Math, ENet, e_input, e_log, g_base, g_basic, r_gfx, + g_textures, g_gfx, g_sound, g_console, g_options, 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; @@ -293,6 +299,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 // @@ -384,6 +417,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) + @@ -396,37 +430,88 @@ 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_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 @@ -509,7 +594,12 @@ 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; @@ -560,7 +650,7 @@ begin Pl.Name := TmpName; end; - if TmpModel <> Pl.Model.Name then + if TmpModel <> Pl.Model.GetName() then Pl.SetModel(TmpModel); MH_SEND_PlayerSettings(Pl.UID, TmpModel); @@ -644,7 +734,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 @@ -668,6 +758,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 @@ -725,27 +821,18 @@ begin if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME; - if gLMSRespawn > LMS_RESPAWN_NONE then - begin - MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000, 'N', ID); - end; - g_Net_Flush(); end; procedure MH_SEND_Info(ID: Byte); -var - Map: string; begin - Map := g_ExtractFileName(gMapInfo.Map); - NetOut.Clear(); NetOut.Write(Byte(NET_MSG_INFO)); NetOut.Write(ID); NetOut.Write(NetClients[ID].Player); - NetOut.Write(gGameSettings.WAD); - NetOut.Write(Map); + NetOut.Write(ExtractFileName(gGameSettings.WAD)); + NetOut.Write(g_ExtractFileName(gMapInfo.Map)); NetOut.Write(gWADHash); NetOut.Write(gGameSettings.GameMode); NetOut.Write(gGameSettings.GoalLimit); @@ -1023,9 +1110,10 @@ 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; + 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); @@ -1091,6 +1179,7 @@ begin NetOut.Write(Byte(FJetpack)); NetOut.Write(FFireTime); NetOut.Write(Byte(FFlaming)); + NetOut.Write(FSpawnInvul); end; g_Net_Host_Send(ID, True, NET_CHAN_PLAYER); @@ -1153,7 +1242,7 @@ begin NetOut.Write(PID); NetOut.Write(Pl.Name); if Mdl = '' then - NetOut.Write(Pl.Model.Name) + NetOut.Write(Pl.Model.GetName()) else NetOut.Write(Mdl); NetOut.Write(Pl.FColor.R); @@ -1427,22 +1516,25 @@ begin if Mode <> NET_CHAT_SYSTEM then begin - if Mode = NET_CHAT_PLAYER then + if NetDeafLevel = 0 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)); + 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; @@ -1451,8 +1543,6 @@ var Kind: Byte; X, Y: Integer; Ang: SmallInt; - Anim: TAnimation; - ID: LongWord; begin if not gGameOn then Exit; Kind := M.ReadByte(); @@ -1466,72 +1556,40 @@ begin NET_GFX_TELE: begin - if g_Frames_Get(ID, 'FRAMES_TELEPORT') then - begin - Anim := TAnimation.Create(ID, False, 3); - g_GFX_OnceAnim(X, Y, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_TELEPORT_FAST, X, Y); if Ang = 1 then g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y); end; NET_GFX_EXPLODE: begin - if g_Frames_Get(ID, 'FRAMES_EXPLODE_ROCKET') then - begin - Anim := TAnimation.Create(ID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(X-64, Y-64, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_EXPLODE_ROCKET, X - 64, Y - 64); if Ang = 1 then g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X, Y); end; NET_GFX_BFGEXPL: begin - if g_Frames_Get(ID, 'FRAMES_EXPLODE_BFG') then - begin - Anim := TAnimation.Create(ID, False, 6); - Anim.Blending := False; - g_GFX_OnceAnim(X-64, Y-64, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_EXPLODE_BFG, X - 64, Y - 64); if Ang = 1 then g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X, Y); end; NET_GFX_BFGHIT: begin - if g_Frames_Get(ID, 'FRAMES_BFGHIT') then - begin - Anim := TAnimation.Create(ID, False, 4); - g_GFX_OnceAnim(X-32, Y-32, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_BFG_HIT, X - 32, Y - 32); end; NET_GFX_FIRE: begin - if g_Frames_Get(ID, 'FRAMES_FIRE') then - begin - Anim := TAnimation.Create(ID, False, 4); - g_GFX_OnceAnim(X, Y, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_FIRE, X, Y); if Ang = 1 then g_Sound_PlayExAt('SOUND_FIRE', X, Y); end; NET_GFX_RESPAWN: begin - if g_Frames_Get(ID, 'FRAMES_ITEM_RESPAWN') then - begin - Anim := TAnimation.Create(ID, False, 4); - g_GFX_OnceAnim(X, Y, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_ITEM_RESPAWN, X, Y); if Ang = 1 then g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y); end; @@ -1664,6 +1722,7 @@ var i1, i2: TStrings_Locale; pln: String; cnt: Byte; + goodCmd: Boolean = true; begin FillChar(EvHash, Sizeof(EvHash), 0); EvType := M.ReadByte(); @@ -1679,33 +1738,63 @@ 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; + 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 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(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: @@ -1722,26 +1811,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 @@ -1821,6 +1930,7 @@ begin NET_EV_LMS_START: begin g_Player_RemoveAllCorpses; + gLMSRespawn := LMS_RESPAWN_NONE; g_Game_Message(_lc[I_MESSAGE_LMS_START], 144); end; @@ -1841,6 +1951,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); @@ -2081,7 +2194,8 @@ begin end; end; - g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True); + 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; @@ -2124,7 +2238,7 @@ begin ReleaseKeys; - if (kByte = NET_KEY_CHAT) then + if LongBool(kByte and NET_KEY_CHAT) then PressKey(KEY_CHAT, 10000) else begin @@ -2135,7 +2249,9 @@ 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 := M.ReadLongInt(); @@ -2204,23 +2320,29 @@ begin 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 := M.ReadByte() <> 0; FPhysics := M.ReadByte() <> 0; @@ -2232,6 +2354,7 @@ begin 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 @@ -2305,7 +2428,8 @@ begin Result := 0; if Pl = nil then Exit; - g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True); + 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); @@ -2370,11 +2494,12 @@ 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; - if TmpModel <> Pl.Model.Name then + if TmpModel <> Pl.Model.GetName() then Pl.SetModel(TmpModel); end; @@ -2383,11 +2508,9 @@ end; procedure MC_RECV_ItemSpawn(var M: TMsg); var ID: Word; - AID: DWord; X, Y, VX, VY: Integer; T: Byte; Quiet, Fall{, Resp}: Boolean; - Anim: TAnimation; it: PItem; begin if not gGameOn then Exit; @@ -2411,12 +2534,7 @@ begin if not Quiet then begin g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y); - if g_Frames_Get(AID, 'FRAMES_ITEM_RESPAWN') then - begin - Anim := TAnimation.Create(AID, False, 4); - g_GFX_OnceAnim(X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16, Anim); - Anim.Free(); - end; + r_GFX_OnceAnim(R_GFX_ITEM_RESPAWN, X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16); end; end; @@ -2579,6 +2697,7 @@ begin MPlaying := M.ReadByte() <> 0; MPos := M.ReadLongWord(); MPaused := M.ReadByte() <> 0; + MPos := MPos+1; //k8: stfu, fpc! if MPlaying then begin @@ -2763,18 +2882,19 @@ begin Str1 := M.ReadString(); Str2 := M.ReadString(); - 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; + 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 @@ -2966,201 +3086,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.