diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas
index ddef35df216b9f645023eef3ab3d511aee45fe38..5e95a6a57dd97b42a4d31178c153e94589092816 100644 (file)
--- a/src/game/g_netmsg.pas
+++ b/src/game/g_netmsg.pas
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;
// 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);
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);
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) +
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
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;
// 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
@@ -698,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
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);
NetOut.Write(Byte(FJetpack));
NetOut.Write(FFireTime);
NetOut.Write(Byte(FFlaming));
+ NetOut.Write(FSpawnInvul);
end;
g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
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;
gGameSettings.GameMode := gSwitchGameMode;
gWADHash := EvHash;
- if not g_Game_StartMap(EvStr, True) then
+ 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]))
NET_EV_MAPEND:
begin
+ gLMSRespawn := LMS_RESPAWN_NONE;
+ gLMSRespawnTime := 0;
if (g_Res_received_map_start <> 0) then
begin
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_LMS_START:
begin
g_Player_RemoveAllCorpses;
+ gLMSRespawn := LMS_RESPAWN_NONE;
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_NOSPAWN:
+ g_Console_Add(_lc[I_PLAYER_SPECT4], True);
+
NET_EV_KILLCOMBO:
g_Game_Announce_KillCombo(EvNum);
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;
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