diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas
index 88764ed9a2bd913b481a581d55c0d65450d6c3f8..759f4b6bd963964b6c9d76524fd178902881091b 100644 (file)
--- a/src/game/g_netmsg.pas
+++ b/src/game/g_netmsg.pas
-(* 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
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
NET_MSG_FLAG = 107;
NET_MSG_REQFST = 108;
NET_MSG_GSET = 109;
NET_MSG_FLAG = 107;
NET_MSG_REQFST = 108;
NET_MSG_GSET = 109;
+ NET_MSG_FLAGPOS= 110;
NET_MSG_PLR = 111;
NET_MSG_PLRPOS = 112;
NET_MSG_PLR = 111;
NET_MSG_PLRPOS = 112;
NET_MSG_ISPAWN = 121;
NET_MSG_IDEL = 122;
NET_MSG_ISPAWN = 121;
NET_MSG_IDEL = 122;
+ NET_MSG_IPOS = 123;
NET_MSG_MSPAWN = 131;
NET_MSG_MPOS = 132;
NET_MSG_MSPAWN = 131;
NET_MSG_MPOS = 132;
NET_MSG_MSHOT = 134;
NET_MSG_MDEL = 135;
NET_MSG_MSHOT = 134;
NET_MSG_MDEL = 135;
- NET_MSG_PSTATE = 141;
- NET_MSG_PTEX = 142;
+ NET_MSG_PSTATE = 141;
+ NET_MSG_PTEX = 142;
NET_MSG_TSOUND = 151;
NET_MSG_TMUSIC = 152;
NET_MSG_TSOUND = 151;
NET_MSG_TMUSIC = 152;
NET_MSG_TIME_SYNC = 194;
NET_MSG_VOTE_EVENT = 195;
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_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;
NET_CHAT_SYSTEM = 0;
NET_CHAT_PLAYER = 1;
NET_EV_LMS_DRAW = 16;
NET_EV_KILLCOMBO = 17;
NET_EV_PLAYER_TOUCH = 18;
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;
NET_VE_STARTED = 1;
NET_VE_PASSED = 2;
NET_CHEAT_SUICIDE = 1;
NET_CHEAT_SPECTATE = 2;
NET_CHEAT_SUICIDE = 1;
NET_CHEAT_SPECTATE = 2;
+ NET_CHEAT_READY = 3;
+ NET_CHEAT_DROPFLAG = 4;
NET_MAX_DIFFTIME = 5000 div 36;
// HOST MESSAGES
NET_MAX_DIFFTIME = 5000 div 36;
// HOST MESSAGES
+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);
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);
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_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_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);
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);
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_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);
procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
// PLAYER
procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
@@ -167,6 +179,7 @@ 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);
// 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(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
// PANEL
procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
procedure MC_RECV_CoopStats(var M: TMsg);
procedure MC_RECV_GameEvent(var M: TMsg);
procedure MC_RECV_FlagEvent(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(var M: TMsg): Word;
procedure MC_RECV_GameSettings(var M: TMsg);
// PLAYER
function MC_RECV_PlayerCreate(var M: TMsg): Word;
// ITEM
procedure MC_RECV_ItemSpawn(var M: TMsg);
procedure MC_RECV_ItemDestroy(var M: TMsg);
// ITEM
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(var M: TMsg);
procedure MC_RECV_PanelState(var M: TMsg);
// PANEL
procedure MC_RECV_PanelTexture(var M: TMsg);
procedure MC_RECV_PanelState(var M: TMsg);
procedure MC_SEND_RCONCommand(Cmd: string);
procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
// DOWNLOAD
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
type
TExternalResourceInfo = record
ExternalResources: array of TExternalResourceInfo;
end;
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
implementation
uses
- 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,
+ {$IFDEF ENABLE_MENU}
+ g_gui,
+ {$ENDIF}
+ Math, ENet, e_input, e_log, g_base, g_basic,
+ 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_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF;
const
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;
//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
// HOST MESSAGES //
// GAME
+procedure MH_MalformedPacket(C: pTNetClient);
+begin
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
+ _lc[I_NET_DISC_PROTOCOL]);
+ g_Net_Host_Kick(C^.ID, NET_DISC_PROTOCOL);
+end;
+
procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
var
Txt: string;
Mode: Byte;
PID: Word;
Pl: TPlayer;
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);
begin
PID := C^.Player;
Pl := g_Player_Get(PID);
- Txt := M.ReadString();
- Mode := M.ReadByte();
+ 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
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
var
Ver, PName, Model, Pw: string;
R, G, B, T: Byte;
var
Ver, PName, Model, Pw: string;
R, G, B, T: Byte;
+ WeapSwitch: Byte;
+ TmpPrefArray: Array [WP_FIRST .. WP_LAST + 1] of Byte;
+ SwitchEmpty: Byte;
+ SkipF: Byte;
PID: Word;
Color: TRGB;
I: Integer;
PID: Word;
Color: TRGB;
I: Integer;
+ Err: Boolean;
begin
begin
- Ver := M.ReadString();
- Pw := M.ReadString();
- PName := M.ReadString();
- Model := M.ReadString();
- R := M.ReadByte();
- G := M.ReadByte();
- B := M.ReadByte();
- T := M.ReadByte();
+ 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();
+ for I := WP_FIRST to WP_LAST + 1 do
+ TmpPrefArray[I] := M.ReadByte();
+ SwitchEmpty := M.ReadByte();
+ SkipF := M.ReadByte();
+ except
+ Err := True;
+ end;
+
+ if Err then begin MH_MalformedPacket(C); Exit; end;
if Ver <> GAME_VERSION then
begin
g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
_lc[I_NET_DISC_VERSION]);
if Ver <> GAME_VERSION then
begin
g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
_lc[I_NET_DISC_VERSION]);
- enet_peer_disconnect(C^.Peer, NET_DISC_VERSION);
+ g_Net_Host_Kick(C^.ID, NET_DISC_VERSION);
Exit;
end;
Exit;
end;
begin
g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
_lc[I_NET_DISC_BAN]);
begin
g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
_lc[I_NET_DISC_BAN]);
- enet_peer_disconnect(C^.Peer, NET_DISC_BAN);
+ g_Net_Host_Kick(C^.ID, NET_DISC_BAN);
end
else
begin
g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
_lc[I_NET_DISC_BAN]);
end
else
begin
g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
_lc[I_NET_DISC_BAN]);
- enet_peer_disconnect(C^.Peer, NET_DISC_TEMPBAN);
+ g_Net_Host_Kick(C^.ID, NET_DISC_TEMPBAN);
end;
Exit;
end;
end;
Exit;
end;
begin
g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
_lc[I_NET_DISC_PASSWORD]);
begin
g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
_lc[I_NET_DISC_PASSWORD]);
- enet_peer_disconnect(C^.Peer, NET_DISC_PASSWORD);
+ g_Net_Host_Kick(C^.ID, NET_DISC_PASSWORD);
Exit;
end;
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;
Color.R := R;
Color.B := B;
Color.G := G;
with g_Player_Get(PID) do
begin
Name := PName;
with g_Player_Get(PID) do
begin
Name := PName;
+ WeapSwitchMode := WeapSwitch;
+ SetWeaponPrefs(TmpPrefArray);
+ SwitchToEmpty := SwitchEmpty;
+ SkipFist := SkipF;
+ if (g_Force_Model_Get() <> 0) then
+ SetModel(g_Forced_Model_GetName());
Reset(True);
end;
C^.Player := PID;
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) +
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);
MH_SEND_Info(C^.ID);
Name := PName;
FClientID := C^.ID;
// round in progress, don't spawn
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;
end;
- for I := Low(NetClients) to High(NetClients) do
+ //if not C^.WaitForFirstSpawn then
begin
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);
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;
end;
+
procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
begin
procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
begin
+ //e_LogWritefln('*** client #%u (cid #%u) full state request', [C.ID, C.Player]);
+
+ if C^.FullUpdateSent then
+ begin
+ // FullStateRequest spam?
+ g_Net_Penalize(C, 'duplicate full state request');
+ exit;
+ end;
+
if gGameOn then
if gGameOn then
+ begin
MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
+ end
else
else
+ begin
C^.RequestedFullUpdate := True;
C^.RequestedFullUpdate := True;
+ end;
end;
// PLAYER
end;
// PLAYER
function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
var
Dir, i: Byte;
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;
WeaponSelect: Word;
PID: Word;
kByte: Word;
Pl: TPlayer;
GT: LongWord;
+ Err: Boolean;
begin
Result := 0;
begin
Result := 0;
+ Err := False;
if not gGameOn then Exit;
if not gGameOn then Exit;
- GT := M.ReadLongWord();
+ 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
PID := C^.Player;
Pl := g_Player_Get(PID);
if Pl = nil then
with Pl do
begin
NetTime := GT;
with Pl do
begin
NetTime := GT;
- kByte := M.ReadWord();
- Dir := M.ReadByte();
- WeaponSelect := M.ReadWord();
+ 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;
//e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING);
if Direction <> TDirection(Dir) then
JustTeleported := False;
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_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
for i := 0 to 15 do
begin
var
CheatKind: Byte;
Pl: TPlayer;
var
CheatKind: Byte;
Pl: TPlayer;
+ Err: Boolean;
begin
begin
+ Err := False;
Pl := g_Player_Get(C^.Player);
if Pl = nil then Exit;
Pl := g_Player_Get(C^.Player);
if Pl = nil then Exit;
- CheatKind := M.ReadByte();
+ try
+ CheatKind := M.ReadByte();
+ except
+ Err := True;
+ end;
+
+ if Err then begin MH_MalformedPacket(C); Exit; end;
case CheatKind of
NET_CHEAT_SUICIDE:
case CheatKind of
NET_CHEAT_SUICIDE:
NET_CHEAT_SPECTATE:
begin
if Pl.FSpectator then
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;
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;
end;
end;
TmpModel: string;
TmpColor: TRGB;
TmpTeam: Byte;
TmpModel: string;
TmpColor: TRGB;
TmpTeam: Byte;
+ TmpWeapSwitch: Byte;
+ TmpPrefArray: Array [WP_FIRST .. WP_LAST + 1] of Byte;
+ TmpSwEmpty: Byte;
+ TmpSkipF: Byte;
+ I: Integer;
Pl: TPlayer;
Pl: TPlayer;
+ Err: Boolean;
begin
begin
- TmpName := M.ReadString();
- TmpModel := M.ReadString();
- TmpColor.R := M.ReadByte();
- TmpColor.G := M.ReadByte();
- TmpColor.B := M.ReadByte();
- TmpTeam := M.ReadByte();
+ 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();
+ for I := WP_FIRST to WP_LAST + 1 do
+ TmpPrefArray[I] := M.ReadByte();
+ TmpSwEmpty := M.ReadByte();
+ TmpSkipF := 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;
Pl := g_Player_Get(C^.Player);
if Pl = nil then Exit;
Pl.Name := TmpName;
end;
Pl.Name := TmpName;
end;
- if TmpModel <> Pl.Model.Name then
+ if (g_Force_Model_Get() <> 0) then
+ TmpModel := g_Forced_Model_GetName();
+ if TmpModel <> Pl.Model.GetName() then
Pl.SetModel(TmpModel);
Pl.SetModel(TmpModel);
+ if (TmpWeapSwitch <> Pl.WeapSwitchMode) then
+ Pl.WeapSwitchMode := TmpWeapSwitch;
+
+ Pl.SetWeaponPrefs(TmpPrefArray);
+ if (TmpSwEmpty <> Pl.SwitchToEmpty) then
+ Pl.SwitchToEmpty := TmpSwEmpty;
+
+ if (TmpSkipF <> Pl.SkipFist) then
+ Pl.SkipFist := TmpSkipF;
+
MH_SEND_PlayerSettings(Pl.UID, TmpModel);
end;
MH_SEND_PlayerSettings(Pl.UID, TmpModel);
end;
procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
var
Pwd: string;
procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
var
Pwd: string;
+ Err: Boolean;
begin
begin
- Pwd := M.ReadString();
+ 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
if not NetAllowRCON then Exit;
if Pwd = NetRCONPassword then
begin
procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
var
Cmd: string;
procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
var
Cmd: string;
+ Err: Boolean;
begin
begin
- Cmd := M.ReadString();
+ 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
if not NetAllowRCON then Exit;
if not C^.RCONAuth then
begin
Name, Command: string;
Need: Integer;
Pl: TPlayer;
Name, Command: string;
Need: Integer;
Pl: TPlayer;
+ Err: Boolean;
begin
begin
- Start := M.ReadByte() <> 0;
- Command := M.ReadString();
+ 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;
Pl := g_Player_Get(C^.Player);
if Pl = nil then Exit;
// GAME (SEND)
// 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
function sendItemRespawn (it: PItem): Boolean;
begin
@@ -644,12 +903,21 @@ procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_E
begin
result := false; // don't stop
MH_SEND_PanelState(pan.guid, ID); // anyway, to sync mplats
begin
result := false; // don't stop
MH_SEND_PanelState(pan.guid, ID); // anyway, to sync mplats
- if (pan.GetTextureCount > 1) then MH_SEND_PanelTexture(pan.guid, pan.LastAnimLoop, ID);
+ if (pan.CanChangeTexture) then MH_SEND_PanelTexture(pan.guid, pan.LastAnimLoop, ID);
end;
var
I: Integer;
begin
end;
var
I: Integer;
begin
+ if (ID < 0) or (ID >= Length(NetClients)) then
+ exit; // bogus client, this shouldn't happen
+
+ NetClients[ID].FullUpdateSent := True;
+
+ e_LogWritefln('*** client #%u (cid #%u) will get everything', [ID, NetClients[ID].Player]);
+
+ MH_ProcessFirstSpawn(@NetClients[ID]);
+
if gPlayers <> nil then
begin
for I := Low(gPlayers) to High(gPlayers) do
if gPlayers <> nil then
begin
for I := Low(gPlayers) to High(gPlayers) do
if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME;
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);
end;
procedure MH_SEND_Info(ID: Byte);
-var
- Map: string;
begin
begin
- Map := g_ExtractFileName(gMapInfo.Map);
-
NetOut.Clear();
NetOut.Write(Byte(NET_MSG_INFO));
NetOut.Write(ID);
NetOut.Write(NetClients[ID].Player);
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(gWADHash);
NetOut.Write(gGameSettings.GameMode);
- NetOut.Write(gGameSettings.GoalLimit);
+ NetOut.Write(gGameSettings.ScoreLimit);
NetOut.Write(gGameSettings.TimeLimit);
NetOut.Write(gGameSettings.MaxLives);
NetOut.Write(gGameSettings.Options);
NetOut.Write(gGameSettings.TimeLimit);
NetOut.Write(gGameSettings.MaxLives);
NetOut.Write(gGameSettings.Options);
if Mode = NET_CHAT_PLAYER then
begin
g_Console_Add(Txt, True);
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
end
else
if Mode = NET_CHAT_TEAM then
if (gPlayer1.Team = TEAM_RED) and (Team = TEAM_RED) then
begin
g_Console_Add(#18'[Team] '#2 + Txt, True);
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);
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
end;
end;
end
begin
Name := g_Net_ClientName_ByID(ID);
g_Console_Add('-> ' + Name + ': ' + Txt, True);
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;
end;
end;
NetOut.Write(Byte(NET_MSG_SCORE));
if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
begin
NetOut.Write(Byte(NET_MSG_SCORE));
if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
begin
- NetOut.Write(gTeamStat[TEAM_RED].Goals);
- NetOut.Write(gTeamStat[TEAM_BLUE].Goals);
+ NetOut.Write(gTeamStat[TEAM_RED].Score);
+ NetOut.Write(gTeamStat[TEAM_BLUE].Score);
end
else
if gGameSettings.GameMode = GM_COOP then
end
else
if gGameSettings.GameMode = GM_COOP then
NetOut.Write(EvStr);
NetOut.Write(Byte(gLastMap));
NetOut.Write(gTime);
NetOut.Write(EvStr);
NetOut.Write(Byte(gLastMap));
NetOut.Write(gTime);
- if (EvType = NET_EV_MAPSTART) and (Pos(':\', EvStr) > 0) then
+ if (EvType = NET_EV_MAPSTART) and isWadPath(EvStr) then
begin
NetOut.Write(Byte(1));
NetOut.Write(gWADHash);
begin
NetOut.Write(Byte(1));
NetOut.Write(gWADHash);
NetOut.Write(gFlags[Flag].Obj.Y);
NetOut.Write(gFlags[Flag].Obj.Vel.X);
NetOut.Write(gFlags[Flag].Obj.Vel.Y);
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;
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
NetOut.Write(Byte(NET_MSG_GSET));
NetOut.Write(gGameSettings.GameMode);
procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
begin
NetOut.Write(Byte(NET_MSG_GSET));
NetOut.Write(gGameSettings.GameMode);
- NetOut.Write(gGameSettings.GoalLimit);
+ NetOut.Write(gGameSettings.ScoreLimit);
NetOut.Write(gGameSettings.TimeLimit);
NetOut.Write(gGameSettings.MaxLives);
NetOut.Write(gGameSettings.Options);
NetOut.Write(gGameSettings.TimeLimit);
NetOut.Write(gGameSettings.MaxLives);
NetOut.Write(gGameSettings.Options);
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 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;
end;
+ if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
+
NetOut.Write(kByte);
NetOut.Write(kByte);
- if Direction = D_LEFT then NetOut.Write(Byte(0)) else NetOut.Write(Byte(1));
+ 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(GameX);
NetOut.Write(GameY);
NetOut.Write(GameVelX);
NetOut.Write(Byte(FNoRespawn));
NetOut.Write(Byte(FJetpack));
NetOut.Write(FFireTime);
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);
end;
g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
NetOut.Write(PID);
NetOut.Write(Pl.Name);
if Mdl = '' then
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);
else
NetOut.Write(Mdl);
NetOut.Write(Pl.FColor.R);
procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
var
it: PItem;
procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
var
it: PItem;
+ tt: Byte;
begin
it := g_Items_ByIdx(IID);
NetOut.Write(Byte(NET_MSG_ISPAWN));
NetOut.Write(IID);
NetOut.Write(Byte(Quiet));
begin
it := g_Items_ByIdx(IID);
NetOut.Write(Byte(NET_MSG_ISPAWN));
NetOut.Write(IID);
NetOut.Write(Byte(Quiet));
- NetOut.Write(it.ItemType);
+ 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(Byte(it.Fall));
NetOut.Write(Byte(it.Respawnable));
NetOut.Write(it.Obj.X);
g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
end;
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(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
// PANEL
procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
if Mode <> NET_CHAT_SYSTEM then
begin
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
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;
- end else
+ end else if (NetDeafLevel < 2) then
g_Console_Add(Txt, True);
end;
g_Console_Add(Txt, True);
end;
Kind: Byte;
X, Y: Integer;
Ang: SmallInt;
Kind: Byte;
X, Y: Integer;
Ang: SmallInt;
- Anim: TAnimation;
- ID: LongWord;
begin
if not gGameOn then Exit;
Kind := M.ReadByte();
begin
if not gGameOn then Exit;
Kind := M.ReadByte();
NET_GFX_TELE:
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;
+ g_GFX_QueueEffect(R_GFX_TELEPORT_FAST, X, Y);
if Ang = 1 then
g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
end;
NET_GFX_EXPLODE:
begin
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;
+ g_GFX_QueueEffect(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 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;
+ g_GFX_QueueEffect(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 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;
+ g_GFX_QueueEffect(R_GFX_BFG_HIT, X - 32, Y - 32);
end;
NET_GFX_FIRE:
begin
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;
+ g_GFX_QueueEffect(R_GFX_FIRE, X, Y);
if Ang = 1 then
g_Sound_PlayExAt('SOUND_FIRE', X, Y);
end;
NET_GFX_RESPAWN:
begin
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;
+ g_GFX_QueueEffect(R_GFX_ITEM_RESPAWN, X, Y);
if Ang = 1 then
g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
end;
if Ang = 1 then
g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
end;
begin
if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
begin
begin
if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
begin
- gTeamStat[TEAM_RED].Goals := M.ReadSmallInt();
- gTeamStat[TEAM_BLUE].Goals := M.ReadSmallInt();
+ gTeamStat[TEAM_RED].Score := M.ReadSmallInt();
+ gTeamStat[TEAM_BLUE].Score := M.ReadSmallInt();
end
else
if gGameSettings.GameMode = GM_COOP then
end
else
if gGameSettings.GameMode = GM_COOP then
i1, i2: TStrings_Locale;
pln: String;
cnt: Byte;
i1, i2: TStrings_Locale;
pln: String;
cnt: Byte;
+ goodCmd: Boolean = true;
begin
FillChar(EvHash, Sizeof(EvHash), 0);
EvType := M.ReadByte();
begin
FillChar(EvHash, Sizeof(EvHash), 0);
EvType := M.ReadByte();
gTime := EvTime;
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
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
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:
end;
NET_EV_RCON:
NET_EV_CHANGE_TEAM:
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:
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:
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:
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:
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
NET_EV_SCORE:
begin
NET_EV_LMS_START:
begin
g_Player_RemoveAllCorpses;
NET_EV_LMS_START:
begin
g_Player_RemoveAllCorpses;
+ gLMSRespawn := LMS_RESPAWN_NONE;
g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
end;
g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
end;
NET_EV_LMS_DRAW:
g_Game_Message(_lc[I_GAME_WIN_DRAW], 144);
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);
NET_EV_KILLCOMBO:
g_Game_Announce_KillCombo(EvNum);
pl.Touch();
end;
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;
end;
end;
+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;
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
Quiet: Boolean;
s, ts: string;
begin
gFlags[Fl].Obj.Y := M.ReadLongInt();
gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
gFlags[Fl].Obj.Vel.Y := 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
Pl := g_Player_Get(PID);
if (Pl = nil) and
s := _lc[I_PLAYER_FLAG_BLUE];
g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
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:
end;
FLAG_STATE_CAPTURED:
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);
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:
end;
FLAG_STATE_DROPPED:
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);
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:
end;
FLAG_STATE_SCORED:
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);
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:
end;
FLAG_STATE_RETURNED:
s := _lc[I_PLAYER_FLAG_BLUE];
g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
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;
end;
end;
end;
procedure MC_RECV_GameSettings(var M: TMsg);
begin
gGameSettings.GameMode := M.ReadByte();
procedure MC_RECV_GameSettings(var M: TMsg);
begin
gGameSettings.GameMode := M.ReadByte();
- gGameSettings.GoalLimit := M.ReadWord();
+ gGameSettings.ScoreLimit := M.ReadWord();
gGameSettings.TimeLimit := M.ReadWord();
gGameSettings.MaxLives := M.ReadByte();
gGameSettings.Options := M.ReadLongWord();
gGameSettings.TimeLimit := M.ReadWord();
gGameSettings.MaxLives := M.ReadByte();
gGameSettings.Options := M.ReadLongWord();
if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
begin
if (Pl <> nil) then Exit;
if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
begin
if (Pl <> nil) then Exit;
+ if (g_Force_Model_Get() <> 0) then
+ Model := g_Forced_Model_GetName();
DID := g_Player_Create(Model, Color, T, False);
with g_Player_Get(DID) do
begin
DID := g_Player_Create(Model, Color, T, False);
with g_Player_Get(DID) do
begin
end;
end;
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;
Result := PID;
end;
ReleaseKeys;
ReleaseKeys;
- if (kByte = NET_KEY_CHAT) then
+ if LongBool(kByte and NET_KEY_CHAT) then
PressKey(KEY_CHAT, 10000)
else
begin
PressKey(KEY_CHAT, 10000)
else
begin
if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
end;
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();
SetDirection(TDirection(Dir));
GameVelX := M.ReadLongInt();
var
PID: Word;
Pl: TPlayer;
var
PID: Word;
Pl: TPlayer;
- I: Integer;
- OldJet: Boolean;
+ I, OldFire: Integer;
+ OldJet, Flam: Boolean;
NewTeam: Byte;
begin
PID := M.ReadWord();
NewTeam: Byte;
begin
PID := M.ReadWord();
FSpectator := M.ReadByte() <> 0;
if FSpectator then
begin
FSpectator := M.ReadByte() <> 0;
if FSpectator then
begin
- if Pl = gPlayer1 then
+ if UID = NetPlrUID1 then
begin
begin
- gLMSPID1 := UID;
+ gSpectLatchPID1 := UID;
gPlayer1 := nil;
end;
gPlayer1 := nil;
end;
- if Pl = gPlayer2 then
+ if UID = NetPlrUID2 then
begin
begin
- gLMSPID2 := UID;
+ gSpectLatchPID2 := UID;
gPlayer2 := nil;
end;
end
else
begin
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;
FNoRespawn := M.ReadByte() <> 0;
OldJet := FJetpack;
FJetpack := M.ReadByte() <> 0;
end;
FGhost := M.ReadByte() <> 0;
FPhysics := M.ReadByte() <> 0;
FNoRespawn := M.ReadByte() <> 0;
OldJet := FJetpack;
FJetpack := M.ReadByte() <> 0;
+ OldFire := FFireTime;
FFireTime := M.ReadLongInt();
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 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;
if Team <> NewTeam then
Pl.ChangeTeam(NewTeam);
end;
Result := 0;
if Pl = nil then Exit;
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);
g_Player_Remove(PID);
var
TmpName: string;
TmpModel: string;
var
TmpName: string;
TmpModel: string;
+ CheckModel: string;
TmpColor: TRGB;
TmpTeam: Byte;
Pl: TPlayer;
TmpColor: TRGB;
TmpTeam: Byte;
Pl: TPlayer;
if Pl.Name <> TmpName then
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;
Pl.Name := TmpName;
end;
- if TmpModel <> Pl.Model.Name then
+ if (g_Force_Model_Get() <> 0) then
+ TmpModel := g_Forced_Model_GetName();
+ if TmpModel <> Pl.Model.GetName() then
Pl.SetModel(TmpModel);
end;
Pl.SetModel(TmpModel);
end;
procedure MC_RECV_ItemSpawn(var M: TMsg);
var
ID: Word;
procedure MC_RECV_ItemSpawn(var M: TMsg);
var
ID: Word;
- AID: DWord;
X, Y, VX, VY: Integer;
T: Byte;
Quiet, Fall{, Resp}: Boolean;
X, Y, VX, VY: Integer;
T: Byte;
Quiet, Fall{, Resp}: Boolean;
- Anim: TAnimation;
it: PItem;
begin
if not gGameOn then Exit;
it: PItem;
begin
if not gGameOn then Exit;
VX := M.ReadLongInt();
VY := M.ReadLongInt();
VX := M.ReadLongInt();
VY := M.ReadLongInt();
- g_Items_Create(X, Y, T, Fall, False, False, ID);
+ 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 := g_Items_ByIdx(ID);
it.Obj.Vel.X := VX;
if not Quiet then
begin
g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
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;
+ g_GFX_QueueEffect(R_GFX_ITEM_RESPAWN, X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16);
end;
end;
end;
end;
g_Items_Remove(ID);
end;
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(var M: TMsg);
// PANEL
procedure MC_RECV_PanelTexture(var M: TMsg);
TP := g_Map_PanelByGUID(PGUID);
if (TP <> nil) then
begin
TP := g_Map_PanelByGUID(PGUID);
if (TP <> nil) then
begin
- if Loop = 0 then
- begin
- // switch texture
- TP.SetTexture(Tex, Loop);
- TP.SetFrame(Fr, Cnt);
- end
- else
- begin
- // looped or non-looped animation
- TP.NextTexture(Loop);
- end;
+ // switch texture
+ TP.SetTexture(Tex, Loop);
+ TP.SetFrame(Fr, Cnt);
end;
end;
end;
end;
if gTriggers[I].ClientID = SID then
with gTriggers[I] do
begin
if gTriggers[I].ClientID = SID then
with gTriggers[I] do
begin
- if SPlaying then
+ if Sound <> nil then
begin
begin
- if trigData.trigLocal then
- Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), trigData.trigVolume/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
else
- Sound.PlayPanVolume((trigData.trigPan-127.0)/128.0, trigData.trigVolume/255.0);
- Sound.SetPosition(SPos);
- end
- else
- if Sound.IsPlaying then Sound.Stop;
+ if Sound.IsPlaying then Sound.Stop;
+ end;
SoundPlayCount := SCount;
end;
SoundPlayCount := SCount;
end;
MPlaying := M.ReadByte() <> 0;
MPos := M.ReadLongWord();
MPaused := M.ReadByte() <> 0;
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);
if MPlaying then
begin
gMusic.SetByName(MName);
gMusic.Play(True);
- gMusic.SetPosition(MPos);
+ // gMusic.SetPosition(MPos);
gMusic.SpecPause := MPaused;
end
else
gMusic.SpecPause := MPaused;
end
else
procedure MC_RECV_MonsterState(var M: TMsg);
var
procedure MC_RECV_MonsterState(var M: TMsg);
var
- ID: Integer;
+ ID, OldFire: Integer;
MState, MFAnm: Byte;
Mon: TMonster;
AnimRevert: Boolean;
MState, MFAnm: Byte;
Mon: TMonster;
AnimRevert: Boolean;
MonsterAmmo := M.ReadLongInt();
MonsterPain := M.ReadLongInt();
AnimRevert := M.ReadByte() <> 0;
MonsterAmmo := M.ReadLongInt();
MonsterPain := M.ReadLongInt();
AnimRevert := M.ReadByte() <> 0;
+ OldFire := FFireTime;
FFireTime := M.ReadLongInt();
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
RevertAnim(AnimRevert);
if MonsterState <> MState then
Str1 := M.ReadString();
Str2 := M.ReadString();
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
procedure MC_SEND_Info(Password: string);
end;
// CLIENT SEND
procedure MC_SEND_Info(Password: string);
+var i: Integer;
begin
NetOut.Clear();
begin
NetOut.Clear();
NetOut.Write(gPlayer1Settings.Color.G);
NetOut.Write(gPlayer1Settings.Color.B);
NetOut.Write(gPlayer1Settings.Team);
NetOut.Write(gPlayer1Settings.Color.G);
NetOut.Write(gPlayer1Settings.Color.B);
NetOut.Write(gPlayer1Settings.Team);
+ NetOut.Write(gPlayer1Settings.WeaponSwitch);
+ for i := WP_FIRST to WP_LAST + 1 do
+ NetOut.Write(gPlayer1Settings.WeaponPreferences[i]);
+ NetOut.Write(gPlayer1Settings.SwitchToEmpty);
+ NetOut.Write(gPlayer1Settings.SkipFist);
g_Net_Client_Send(True, NET_CHAN_SERVICE);
end;
g_Net_Client_Send(True, NET_CHAN_SERVICE);
end;
g_Net_Client_Send(True, NET_CHAN_CHAT);
end;
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;
procedure MC_SEND_PlayerPos();
var
kByte: Word;
Predict: Boolean;
strafeDir: Byte;
+ WeaponAct: Byte = 0;
WeaponSelect: Word = 0;
WeaponSelect: Word = 0;
- I: Integer;
+ i: Integer;
begin
if not gGameOn then Exit;
if gPlayers = nil then Exit;
begin
if not gGameOn then Exit;
if gPlayers = nil then Exit;
kByte := 0;
Predict := NetPredictSelf; // and (not NetGotKeys);
kByte := 0;
Predict := NetPredictSelf; // and (not NetGotKeys);
+{$IFDEF DISABLE_MENU}
+ if (not gConsoleShow) and (not gChatShow) then
+{$ELSE}
if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
+{$ENDIF}
begin
strafeDir := P1MoveButton shr 4;
P1MoveButton := P1MoveButton and $0F;
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
+ 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;
+
+ 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
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;
+ 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;
- // strafing
- if isKeyPressed(KeyStrafe, KeyStrafe2) then
+ for i := WP_FACT to WP_LACT do
+ begin
+ if gWeaponAction[0, i] then
begin
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
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
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;
end;
+
// fix movebutton state
P1MoveButton := P1MoveButton or (strafeDir shl 4);
end
// fix movebutton state
P1MoveButton := P1MoveButton or (strafeDir shl 4);
end
NetOut.Write(gTime);
NetOut.Write(kByte);
NetOut.Write(Byte(gPlayer1.Direction));
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);
NetOut.Write(WeaponSelect);
//e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING);
g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
end;
procedure MC_SEND_PlayerSettings();
end;
procedure MC_SEND_PlayerSettings();
+var i: Integer;
begin
NetOut.Write(Byte(NET_MSG_PLRSET));
NetOut.Write(gPlayer1Settings.Name);
begin
NetOut.Write(Byte(NET_MSG_PLRSET));
NetOut.Write(gPlayer1Settings.Name);
NetOut.Write(gPlayer1Settings.Color.G);
NetOut.Write(gPlayer1Settings.Color.B);
NetOut.Write(gPlayer1Settings.Team);
NetOut.Write(gPlayer1Settings.Color.G);
NetOut.Write(gPlayer1Settings.Color.B);
NetOut.Write(gPlayer1Settings.Team);
+ NetOut.Write(gPlayer1Settings.WeaponSwitch);
+ for i := WP_FIRST to WP_LAST + 1 do
+ NetOut.Write(gPlayer1Settings.WeaponPreferences[i]);
+ NetOut.Write(gPlayer1Settings.SwitchToEmpty);
+ NetOut.Write(gPlayer1Settings.SkipFist);
g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
end;
g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
end;
g_Net_Client_Send(True, NET_CHAN_SERVICE);
end;
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
- 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), 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; 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), 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.
end.