v1.6.17.0 (Unto the Breach update)
This commit is contained in:
@@ -49,7 +49,7 @@ namespace Barotrauma.Networking
|
||||
string[] lines;
|
||||
try
|
||||
{
|
||||
lines = File.ReadAllLines(LegacySavePath);
|
||||
lines = File.ReadAllLines(LegacySavePath, catchUnauthorizedAccessExceptions: false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.Server.SendChatMessage(txt, senderClient: c, chatMode: chatMode);
|
||||
GameMain.Server.SendChatMessage(txt, senderClient: c, chatMode: chatMode, type: type == ChatMessageType.Team ? type : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,13 +102,19 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GameMain.Server == null || !GameMain.Server.ServerSettings.KarmaEnabled) { return 100.0f; }
|
||||
if (GameMain.Server == null || !GameMain.Server.ServerSettings.KarmaEnabled || GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
return 100.0f;
|
||||
}
|
||||
if (HasPermission(ClientPermissions.KarmaImmunity)) { return 100.0f; }
|
||||
return karma;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (GameMain.Server == null || !GameMain.Server.ServerSettings.KarmaEnabled) { return; }
|
||||
if (GameMain.Server == null || !GameMain.Server.ServerSettings.KarmaEnabled || GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
karma = Math.Min(Math.Max(value, 0.0f), 100.0f);
|
||||
if (!MathUtils.NearlyEqual(karma, syncedKarma, 10.0f))
|
||||
{
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
try
|
||||
{
|
||||
Data = File.ReadAllBytes(filePath);
|
||||
Data = File.ReadAllBytes(filePath, catchUnauthorizedAccessExceptions: false);
|
||||
}
|
||||
catch (System.IO.IOException e)
|
||||
{
|
||||
@@ -400,7 +400,7 @@ namespace Barotrauma.Networking
|
||||
if (GameMain.GameSession != null &&
|
||||
!ActiveTransfers.Any(t => t.Connection == inc.Sender && t.FileType == FileTransferType.CampaignSave))
|
||||
{
|
||||
StartTransfer(inc.Sender, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
|
||||
StartTransfer(inc.Sender, FileTransferType.CampaignSave, GameMain.GameSession.DataPath.LoadPath);
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign)
|
||||
{
|
||||
client.LastCampaignSaveSendTime = (campaign.LastSaveID, (float)Lidgren.Network.NetTime.Now);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+15
-6
@@ -269,10 +269,15 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
switch (inc.SenderConnection.Status)
|
||||
NetConnectionStatus status = inc.ReadHeader<NetConnectionStatus>();
|
||||
switch (status)
|
||||
{
|
||||
case NetConnectionStatus.Disconnected:
|
||||
LidgrenConnection? conn = connectedClients.Select(c => c.Connection).FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
|
||||
|
||||
string disconnectMsg = inc.ReadString();
|
||||
var peerDisconnectPacket =
|
||||
PeerDisconnectPacket.FromLidgrenStringRepresentation(disconnectMsg).Fallback(PeerDisconnectPacket.WithReason(DisconnectReason.Unknown));
|
||||
if (conn != null)
|
||||
{
|
||||
if (conn == OwnerConnection)
|
||||
@@ -283,7 +288,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
Disconnect(conn, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
||||
Disconnect(conn, peerDisconnectPacket);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -291,7 +296,7 @@ namespace Barotrauma.Networking
|
||||
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
|
||||
if (pendingClient != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
||||
RemovePendingClient(pendingClient, peerDisconnectPacket);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +337,9 @@ namespace Barotrauma.Networking
|
||||
if (status == Steamworks.AuthResponse.OK)
|
||||
{
|
||||
pendingClient.Connection.SetAccountInfo(new AccountInfo(new SteamId(steamId), new SteamId(ownerId)));
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
pendingClient.InitializationStep = ShouldAskForPassword(serverSettings, pendingClient.Connection)
|
||||
? ConnectionInitialization.Password
|
||||
: ConnectionInitialization.ContentPackageOrder;
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
}
|
||||
else
|
||||
@@ -450,7 +457,9 @@ namespace Barotrauma.Networking
|
||||
pendingClient.Connection.SetAccountInfo(accountInfo);
|
||||
pendingClient.Name = packet.Name;
|
||||
pendingClient.OwnerKey = packet.OwnerKey;
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
pendingClient.InitializationStep = ShouldAskForPassword(serverSettings, pendingClient.Connection)
|
||||
? ConnectionInitialization.Password
|
||||
: ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
|
||||
void rejectClient()
|
||||
@@ -470,7 +479,7 @@ namespace Barotrauma.Networking
|
||||
if (authenticators is null
|
||||
|| !packet.AuthTicket.TryUnwrap(out var authTicket)
|
||||
|| !authenticators.TryGetValue(authTicket.Kind, out var authenticator))
|
||||
{
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Debug server accepts unauthenticated connections", Microsoft.Xna.Framework.Color.Yellow);
|
||||
acceptClient(new AccountInfo(new UnauthenticatedAccountId(packet.Name)));
|
||||
|
||||
+3
-1
@@ -344,7 +344,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
// Do nothing with the auth ticket because that should be handled by the owner peer,
|
||||
// just assume that authentication succeeded
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
pendingClient.InitializationStep = ShouldAskForPassword(serverSettings, pendingClient.Connection)
|
||||
? ConnectionInitialization.Password
|
||||
: ConnectionInitialization.ContentPackageOrder;
|
||||
pendingClient.Name = packet.Name;
|
||||
pendingClient.AuthSessionStarted = true;
|
||||
}
|
||||
|
||||
+13
@@ -359,5 +359,18 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
protected static void LogMalformedMessage()
|
||||
=> DebugConsole.ThrowError("Received malformed message from remote peer.");
|
||||
|
||||
protected bool ShouldAskForPassword(ServerSettings serverSettings, NetworkConnection connection)
|
||||
{
|
||||
if (!serverSettings.HasPassword) { return false; }
|
||||
|
||||
if (GameMain.Server is { } server && server.FindAndRemoveRecentlyDisconnectedConnection(connection))
|
||||
{
|
||||
// do not ask passwords from clients that have recently disconnected
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -8,16 +9,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
partial class RespawnManager : Entity, IServerSerializable
|
||||
{
|
||||
private DateTime despawnTime;
|
||||
|
||||
private float shuttleEmptyTimer;
|
||||
|
||||
private int pendingRespawnCount, requiredRespawnCount;
|
||||
private int prevPendingRespawnCount, prevRequiredRespawnCount;
|
||||
public bool IsShuttleInsideLevel => RespawnShuttles.Any(s => s.WorldPosition.Y < Level.Loaded.Size.Y);
|
||||
|
||||
public bool IsShuttleInsideLevel => RespawnShuttle != null && RespawnShuttle.WorldPosition.Y < Level.Loaded.Size.Y;
|
||||
|
||||
private IEnumerable<Client> GetClientsToRespawn()
|
||||
private IEnumerable<Client> GetClientsToRespawn(CharacterTeamType teamId)
|
||||
{
|
||||
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
|
||||
foreach (Client c in networkMember.ConnectedClients)
|
||||
@@ -25,6 +21,10 @@ namespace Barotrauma.Networking
|
||||
if (!c.InGame) { continue; }
|
||||
if (c.SpectateOnly && (GameMain.Server.ServerSettings.AllowSpectating || GameMain.Server.OwnerConnection == c.Connection)) { continue; }
|
||||
if (c.Character != null && !c.Character.IsDead) { continue; }
|
||||
if (c.TeamID != CharacterTeamType.None && c.TeamID != teamId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var matchingData = campaign?.GetClientCharacterData(c);
|
||||
|
||||
@@ -80,36 +80,42 @@ namespace Barotrauma.Networking
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<CharacterInfo> GetBotsToRespawn()
|
||||
private static List<CharacterInfo> GetBotsToRespawn(CharacterTeamType teamId)
|
||||
{
|
||||
//this works under the assumption that GetCharacterInfos only returns bots in MP
|
||||
var botInfos = GameMain.GameSession.CrewManager.GetCharacterInfos()
|
||||
.Where(botInfo => botInfo.TeamID == teamId)
|
||||
//filter out players in case a player has been given control of a bot using console commands
|
||||
.Where(botInfo => GameMain.Server.ConnectedClients.None(c => c.CharacterInfo == botInfo))
|
||||
.ToList();
|
||||
|
||||
if (GameMain.Server.ServerSettings.BotSpawnMode == BotSpawnMode.Normal)
|
||||
{
|
||||
return Character.CharacterList
|
||||
.FindAll(c => c.TeamID == CharacterTeamType.Team1 && c.AIController != null && c.Info != null && c.IsDead)
|
||||
.Select(c => c.Info)
|
||||
.ToList();
|
||||
return botInfos.Where(ci => ci.Character == null || ci.Character.IsDead).ToList();
|
||||
}
|
||||
|
||||
int currPlayerCount = GameMain.Server.ConnectedClients.Count(c =>
|
||||
c.InGame &&
|
||||
(!c.SpectateOnly || (!GameMain.Server.ServerSettings.AllowSpectating && GameMain.Server.OwnerConnection != c.Connection)));
|
||||
|
||||
var existingBots = Character.CharacterList
|
||||
.FindAll(c => c.TeamID == CharacterTeamType.Team1 && c.AIController != null && c.Info != null);
|
||||
|
||||
var existingBots = Character.CharacterList.FindAll(c => c.IsBot && !c.IsDead && c.TeamID == teamId);
|
||||
int requiredBots = GameMain.Server.ServerSettings.BotCount - currPlayerCount;
|
||||
requiredBots -= existingBots.Count(b => !b.IsDead);
|
||||
|
||||
List<CharacterInfo> botsToRespawn = new List<CharacterInfo>();
|
||||
for (int i = 0; i < requiredBots; i++)
|
||||
{
|
||||
CharacterInfo botToRespawn = existingBots.Find(b => b.IsDead)?.Info;
|
||||
CharacterInfo botToRespawn = botInfos.FirstOrDefault(b => b.Character == null || b.Character.IsDead);
|
||||
if (botToRespawn == null)
|
||||
{
|
||||
botToRespawn = new CharacterInfo(CharacterPrefab.HumanSpeciesName);
|
||||
botToRespawn = new CharacterInfo(CharacterPrefab.HumanSpeciesName)
|
||||
{
|
||||
TeamID = teamId
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
botInfos.Remove(botToRespawn);
|
||||
existingBots.Remove(botToRespawn.Character);
|
||||
}
|
||||
botsToRespawn.Add(botToRespawn);
|
||||
@@ -117,9 +123,27 @@ namespace Barotrauma.Networking
|
||||
return botsToRespawn;
|
||||
}
|
||||
|
||||
private bool ShouldStartRespawnCountdown()
|
||||
private string GetRespawnShuttleText(CharacterTeamType team)
|
||||
{
|
||||
int characterToRespawnCount = GetClientsToRespawn().Count();
|
||||
if (teamSpecificStates.Count == 1)
|
||||
{
|
||||
return "respawn shuttle";
|
||||
}
|
||||
return team == CharacterTeamType.Team1 ? "respawn shuttle (team 1)" : "respawn shuttle (team 2)";
|
||||
}
|
||||
private string GetTeamNameText(CharacterTeamType team)
|
||||
{
|
||||
if (teamSpecificStates.Count == 1)
|
||||
{
|
||||
return "everyone";
|
||||
}
|
||||
return team == CharacterTeamType.Team1 ? "team 1" : "team 2";
|
||||
}
|
||||
|
||||
|
||||
private bool ShouldStartRespawnCountdown(TeamSpecificState teamSpecificState)
|
||||
{
|
||||
int characterToRespawnCount = GetClientsToRespawn(teamSpecificState.TeamID).Count();
|
||||
return ShouldStartRespawnCountdown(characterToRespawnCount);
|
||||
}
|
||||
|
||||
@@ -133,108 +157,91 @@ namespace Barotrauma.Networking
|
||||
return characterToRespawnCount >= GetMinCharactersToRespawn();
|
||||
}
|
||||
|
||||
partial void UpdateWaiting(float _)
|
||||
partial void UpdateWaiting(TeamSpecificState teamSpecificState)
|
||||
{
|
||||
if (RespawnShuttle != null)
|
||||
//no respawns in the first minute of the round - otherwise it can be that bots
|
||||
//are respawned to "fill" the spots of players who are taking a long time to load in
|
||||
if (GameMain.GameSession is { RoundDuration: < 60 })
|
||||
{
|
||||
RespawnShuttle.Velocity = Vector2.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
pendingRespawnCount = GetClientsToRespawn().Count();
|
||||
requiredRespawnCount = GetMinCharactersToRespawn();
|
||||
if (pendingRespawnCount != prevPendingRespawnCount ||
|
||||
requiredRespawnCount != prevRequiredRespawnCount)
|
||||
var teamId = teamSpecificState.TeamID;
|
||||
var respawnShuttle = GetShuttle(teamId);
|
||||
if (respawnShuttle != null)
|
||||
{
|
||||
prevPendingRespawnCount = pendingRespawnCount;
|
||||
prevRequiredRespawnCount = requiredRespawnCount;
|
||||
respawnShuttle.Velocity = Vector2.Zero;
|
||||
}
|
||||
|
||||
teamSpecificState.PendingRespawnCount = GetClientsToRespawn(teamId).Count();
|
||||
if (GameMain.GameSession?.Campaign == null)
|
||||
{
|
||||
teamSpecificState.PendingRespawnCount += GetBotsToRespawn(teamId).Count;
|
||||
}
|
||||
teamSpecificState.RequiredRespawnCount = GetMinCharactersToRespawn();
|
||||
if (teamSpecificState.PendingRespawnCount != teamSpecificState.PrevPendingRespawnCount ||
|
||||
teamSpecificState.RequiredRespawnCount != teamSpecificState.PrevRequiredRespawnCount)
|
||||
{
|
||||
teamSpecificState.PrevPendingRespawnCount = teamSpecificState.PendingRespawnCount;
|
||||
teamSpecificState.PrevRequiredRespawnCount = teamSpecificState.RequiredRespawnCount;
|
||||
GameMain.Server.CreateEntityEvent(this);
|
||||
}
|
||||
|
||||
if (RespawnCountdownStarted)
|
||||
if (teamSpecificState.RespawnCountdownStarted)
|
||||
{
|
||||
if (pendingRespawnCount == 0)
|
||||
if (teamSpecificState.PendingRespawnCount == 0)
|
||||
{
|
||||
RespawnCountdownStarted = false;
|
||||
teamSpecificState.RespawnCountdownStarted = false;
|
||||
GameMain.Server.CreateEntityEvent(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool shouldStartCountdown = ShouldStartRespawnCountdown(pendingRespawnCount);
|
||||
bool shouldStartCountdown = ShouldStartRespawnCountdown(teamSpecificState.PendingRespawnCount);
|
||||
if (shouldStartCountdown)
|
||||
{
|
||||
RespawnCountdownStarted = true;
|
||||
if (RespawnTime < DateTime.Now)
|
||||
teamSpecificState.RespawnCountdownStarted = true;
|
||||
if (teamSpecificState.RespawnTime < DateTime.Now)
|
||||
{
|
||||
RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, (int)(GameMain.Server.ServerSettings.RespawnInterval * 1000.0f));
|
||||
teamSpecificState.RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, (int)(GameMain.Server.ServerSettings.RespawnInterval * 1000.0f));
|
||||
}
|
||||
GameMain.Server.CreateEntityEvent(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (RespawnCountdownStarted && DateTime.Now > RespawnTime)
|
||||
if (teamSpecificState.RespawnCountdownStarted && DateTime.Now > teamSpecificState.RespawnTime)
|
||||
{
|
||||
DispatchShuttle();
|
||||
RespawnCountdownStarted = false;
|
||||
DispatchShuttle(teamSpecificState);
|
||||
teamSpecificState.RespawnCountdownStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DispatchShuttle()
|
||||
private void DispatchShuttle(TeamSpecificState teamSpecificState)
|
||||
{
|
||||
if (RespawnShuttle != null)
|
||||
if (RespawnShuttles.Any())
|
||||
{
|
||||
CurrentState = State.Transporting;
|
||||
ResetShuttle(teamSpecificState);
|
||||
teamSpecificState.CurrentState = State.Transporting;
|
||||
GameMain.Server.CreateEntityEvent(this);
|
||||
|
||||
ResetShuttle();
|
||||
|
||||
if (shuttleSteering != null)
|
||||
{
|
||||
shuttleSteering.TargetVelocity = Vector2.Zero;
|
||||
}
|
||||
|
||||
Vector2 spawnPos = FindSpawnPos();
|
||||
RespawnCharacters(spawnPos, out bool anyCharacterSpawnedInShuttle);
|
||||
if (anyCharacterSpawnedInShuttle)
|
||||
{
|
||||
GameServer.Log("Dispatching the respawn shuttle.", ServerLog.MessageType.Spawning);
|
||||
CoroutineManager.StopCoroutines("forcepos");
|
||||
if (spawnPos.Y > Level.Loaded.Size.Y)
|
||||
{
|
||||
CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f), "forcepos");
|
||||
}
|
||||
else
|
||||
{
|
||||
RespawnShuttle.SetPosition(spawnPos);
|
||||
RespawnShuttle.Velocity = Vector2.Zero;
|
||||
RespawnShuttle.NeutralizeBallast();
|
||||
RespawnShuttle.EnableMaintainPosition();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GameServer.Log("Respawning everyone in main sub.", ServerLog.MessageType.Spawning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentState = State.Waiting;
|
||||
GameServer.Log("Respawning everyone in main sub.", ServerLog.MessageType.Spawning);
|
||||
teamSpecificState.CurrentState = State.Waiting;
|
||||
GameServer.Log($"Respawning {GetTeamNameText(teamSpecificState.TeamID)} in the main sub.", ServerLog.MessageType.Spawning);
|
||||
GameMain.Server.CreateEntityEvent(this);
|
||||
|
||||
RespawnCharacters(shuttlePos: null, out _);
|
||||
}
|
||||
RespawnCharacters(teamSpecificState);
|
||||
}
|
||||
|
||||
partial void UpdateReturningProjSpecific(float deltaTime)
|
||||
partial void UpdateReturningProjSpecific(TeamSpecificState teamSpecificState, float deltaTime)
|
||||
{
|
||||
//speed up despawning if there's no-one inside the shuttle
|
||||
if (despawnTime > DateTime.Now + new TimeSpan(0, 0, seconds: 30) && CheckShuttleEmpty(deltaTime))
|
||||
if (teamSpecificState.DespawnTime > DateTime.Now + new TimeSpan(0, 0, seconds: 30) && CheckShuttleEmpty(deltaTime))
|
||||
{
|
||||
despawnTime = DateTime.Now + new TimeSpan(0, 0, seconds: 30);
|
||||
teamSpecificState.DespawnTime = DateTime.Now + new TimeSpan(0, 0, seconds: 30);
|
||||
}
|
||||
|
||||
foreach (Door door in shuttleDoors)
|
||||
foreach (Door door in shuttleDoors[teamSpecificState.TeamID])
|
||||
{
|
||||
if (door.IsOpen)
|
||||
{
|
||||
@@ -242,87 +249,71 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
var shuttleGaps = Gap.GapList.FindAll(g => g.Submarine == RespawnShuttle && g.ConnectedWall != null);
|
||||
var shuttleGaps = Gap.GapList.FindAll(g => RespawnShuttles.Contains(g.Submarine) && g.ConnectedWall != null);
|
||||
shuttleGaps.ForEach(g => Spawner.AddEntityToRemoveQueue(g));
|
||||
|
||||
var dockingPorts = Item.ItemList.FindAll(i => i.Submarine == RespawnShuttle && i.GetComponent<DockingPort>() != null);
|
||||
var dockingPorts = Item.ItemList.FindAll(i => RespawnShuttles.Contains(i.Submarine) && i.GetComponent<DockingPort>() != null);
|
||||
dockingPorts.ForEach(d => d.GetComponent<DockingPort>().Undock());
|
||||
|
||||
//shuttle has returned if the path has been traversed or the shuttle is close enough to the exit
|
||||
if (!CoroutineManager.IsCoroutineRunning("forcepos"))
|
||||
if (!IsShuttleInsideLevel || DateTime.Now > teamSpecificState.DespawnTime)
|
||||
{
|
||||
if ((shuttleSteering?.SteeringPath != null && shuttleSteering.SteeringPath.Finished)
|
||||
|| (RespawnShuttle.WorldPosition.Y + RespawnShuttle.Borders.Y > Level.Loaded.StartPosition.Y - Level.ShaftHeight &&
|
||||
Math.Abs(Level.Loaded.StartPosition.X - RespawnShuttle.WorldPosition.X) < 1000.0f))
|
||||
{
|
||||
CoroutineManager.StopCoroutines("forcepos");
|
||||
CoroutineManager.StartCoroutine(
|
||||
ForceShuttleToPos(new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + 1000.0f), 100.0f), "forcepos");
|
||||
ResetShuttle(teamSpecificState);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsShuttleInsideLevel || DateTime.Now > despawnTime)
|
||||
{
|
||||
CoroutineManager.StopCoroutines("forcepos");
|
||||
|
||||
ResetShuttle();
|
||||
|
||||
CurrentState = State.Waiting;
|
||||
GameServer.Log("The respawn shuttle has left.", ServerLog.MessageType.Spawning);
|
||||
teamSpecificState.CurrentState = State.Waiting;
|
||||
GameServer.Log($"The {GetRespawnShuttleText(teamSpecificState.TeamID)} has left.", ServerLog.MessageType.Spawning);
|
||||
GameMain.Server.CreateEntityEvent(this);
|
||||
|
||||
RespawnCountdownStarted = false;
|
||||
ReturnCountdownStarted = false;
|
||||
teamSpecificState.RespawnCountdownStarted = false;
|
||||
teamSpecificState.ReturnCountdownStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateTransportingProjSpecific(float deltaTime)
|
||||
partial void UpdateTransportingProjSpecific(TeamSpecificState teamSpecificState, float deltaTime)
|
||||
{
|
||||
if (!ReturnCountdownStarted)
|
||||
if (!teamSpecificState.ReturnCountdownStarted)
|
||||
{
|
||||
//if there are no living chracters inside, transporting can be stopped immediately
|
||||
if (CheckShuttleEmpty(deltaTime))
|
||||
{
|
||||
ReturnTime = DateTime.Now;
|
||||
ReturnCountdownStarted = true;
|
||||
teamSpecificState.ReturnTime = DateTime.Now;
|
||||
teamSpecificState.ReturnCountdownStarted = true;
|
||||
}
|
||||
else if (!ShouldStartRespawnCountdown())
|
||||
else if (!ShouldStartRespawnCountdown(teamSpecificState))
|
||||
{
|
||||
//don't start counting down until someone else needs to respawn
|
||||
ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(maxTransportTime * 1000));
|
||||
despawnTime = ReturnTime + new TimeSpan(0, 0, seconds: 30);
|
||||
teamSpecificState.ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(maxTransportTime * 1000));
|
||||
teamSpecificState.DespawnTime = teamSpecificState.ReturnTime + new TimeSpan(0, 0, seconds: 30);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReturnCountdownStarted = true;
|
||||
teamSpecificState.ReturnCountdownStarted = true;
|
||||
GameMain.Server.CreateEntityEvent(this);
|
||||
}
|
||||
}
|
||||
else if (CheckShuttleEmpty(deltaTime))
|
||||
{
|
||||
ReturnTime = DateTime.Now;
|
||||
teamSpecificState.ReturnTime = DateTime.Now;
|
||||
}
|
||||
|
||||
if (DateTime.Now > ReturnTime)
|
||||
if (DateTime.Now > teamSpecificState.ReturnTime)
|
||||
{
|
||||
if (IsShuttleInsideLevel)
|
||||
{
|
||||
GameServer.Log("The respawn shuttle is leaving.", ServerLog.MessageType.ServerMessage);
|
||||
GameServer.Log($"The {GetRespawnShuttleText(teamSpecificState.TeamID)} is leaving.", ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
CurrentState = State.Returning;
|
||||
teamSpecificState.CurrentState = State.Returning;
|
||||
|
||||
GameMain.Server.CreateEntityEvent(this);
|
||||
|
||||
RespawnCountdownStarted = false;
|
||||
teamSpecificState.RespawnCountdownStarted = false;
|
||||
maxTransportTime = GameMain.Server.ServerSettings.MaxTransportTime;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckShuttleEmpty(float deltaTime)
|
||||
{
|
||||
if (!Character.CharacterList.Any(c => c.Submarine == RespawnShuttle && !c.IsDead))
|
||||
if (RespawnShuttles.All(respawnShuttle => Character.CharacterList.None(c => c.Submarine == respawnShuttle && !c.IsDead)))
|
||||
{
|
||||
shuttleEmptyTimer += deltaTime;
|
||||
}
|
||||
@@ -333,15 +324,18 @@ namespace Barotrauma.Networking
|
||||
return shuttleEmptyTimer > 1.0f;
|
||||
}
|
||||
|
||||
private void RespawnCharacters(Vector2? shuttlePos, out bool anyCharacterSpawnedInShuttle)
|
||||
private void RespawnCharacters(TeamSpecificState teamSpecificState)
|
||||
{
|
||||
respawnedCharacters.Clear();
|
||||
|
||||
var respawnSub = RespawnShuttle ?? Submarine.MainSub;
|
||||
var teamID = teamSpecificState.TeamID;
|
||||
int teamIndex = teamID == CharacterTeamType.Team1 ? 0 : 1;
|
||||
bool anyCharacterSpawnedInShuttle = false;
|
||||
teamSpecificState.RespawnedCharacters.Clear();
|
||||
|
||||
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
|
||||
bool isPvPMode = GameMain.GameSession.GameMode is PvPMode;
|
||||
int teamCount = isPvPMode ? 2 : 1;
|
||||
|
||||
var clients = GetClientsToRespawn().ToList();
|
||||
var clients = GetClientsToRespawn(teamID).ToList();
|
||||
foreach (Client c in clients)
|
||||
{
|
||||
// Get rid of the existing character
|
||||
@@ -355,18 +349,31 @@ namespace Barotrauma.Networking
|
||||
c.CharacterInfo = matchingData.CharacterInfo;
|
||||
}
|
||||
|
||||
//all characters are in Team 1 in game modes/missions with only one team.
|
||||
//if at some point we add a game mode with multiple teams where respawning is possible, this needs to be reworked
|
||||
c.TeamID = CharacterTeamType.Team1;
|
||||
c.CharacterInfo ??= new CharacterInfo(CharacterPrefab.HumanSpeciesName, c.Name);
|
||||
|
||||
//force everyone to team 1 if there's just one team
|
||||
if (teamCount == 1)
|
||||
{
|
||||
c.TeamID = teamID;
|
||||
}
|
||||
else if (isPvPMode && c.TeamID == CharacterTeamType.None)
|
||||
{
|
||||
GameMain.Server.AssignClientToPvpTeamMidgame(c);
|
||||
}
|
||||
c.CharacterInfo.TeamID = c.TeamID;
|
||||
}
|
||||
List<CharacterInfo> characterInfos = clients.Select(c => c.CharacterInfo).ToList();
|
||||
|
||||
//bots don't respawn in the campaign
|
||||
var botsToSpawn = GetBotsToRespawn(teamID);
|
||||
if (campaign == null)
|
||||
{
|
||||
var botsToSpawn = GetBotsToRespawn();
|
||||
characterInfos.AddRange(botsToSpawn);
|
||||
foreach (var bot in botsToSpawn)
|
||||
{
|
||||
// Get rid of the existing bots' corpses
|
||||
if (bot.Character is Character character) { character.DespawnNow(); }
|
||||
}
|
||||
}
|
||||
|
||||
GameMain.Server.AssignJobs(clients);
|
||||
@@ -374,38 +381,61 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (campaign?.GetClientCharacterData(c) == null || c.CharacterInfo.Job == null)
|
||||
{
|
||||
c.CharacterInfo.Job = new Job(c.AssignedJob.Prefab, Rand.RandSync.Unsynced, c.AssignedJob.Variant);
|
||||
c.CharacterInfo.Job = new Job(c.AssignedJob.Prefab, isPvPMode, Rand.RandSync.Unsynced, c.AssignedJob.Variant);
|
||||
}
|
||||
}
|
||||
|
||||
//the spawnpoints where the characters will spawn
|
||||
var shuttleSpawnPoints = WayPoint.SelectCrewSpawnPoints(characterInfos, respawnSub);
|
||||
//the spawnpoints where they would spawn if they were spawned inside the main sub
|
||||
//(in order to give them appropriate ID card tags)
|
||||
var mainSubSpawnPoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub);
|
||||
System.Diagnostics.Debug.Assert(characterInfos.All(c => c.TeamID == teamID),
|
||||
"List of characters to respawn contained characters from the wrong team.");
|
||||
|
||||
Submarine mainSub = Submarine.MainSubs[teamIndex];
|
||||
Submarine respawnSub = null;
|
||||
|
||||
Submarine respawnShuttle = GetShuttle(teamID);
|
||||
Vector2? shuttlePos = null;
|
||||
if (respawnShuttle != null)
|
||||
{
|
||||
respawnSub = respawnShuttle;
|
||||
shuttlePos = FindSpawnPos(respawnShuttle, mainSub);
|
||||
}
|
||||
|
||||
respawnSub ??= mainSub ?? Level.Loaded.StartOutpost;
|
||||
|
||||
ItemPrefab divingSuitPrefab = null;
|
||||
if ((shuttlePos != null && Level.Loaded.GetRealWorldDepth(shuttlePos.Value.Y) > Level.DefaultRealWorldCrushDepth) ||
|
||||
Level.Loaded.GetRealWorldDepth(Submarine.MainSub.WorldPosition.Y) > Level.DefaultRealWorldCrushDepth)
|
||||
(mainSub != null && Level.Loaded.GetRealWorldDepth(mainSub.WorldPosition.Y) > Level.DefaultRealWorldCrushDepth))
|
||||
{
|
||||
divingSuitPrefab = ItemPrefab.Prefabs.FirstOrDefault(it => it.Tags.Any(t => t == "respawnsuitdeep"));
|
||||
}
|
||||
divingSuitPrefab ??=
|
||||
divingSuitPrefab ??=
|
||||
ItemPrefab.Prefabs.FirstOrDefault(it => it.Tags.Any(t => t == "respawnsuit")) ??
|
||||
ItemPrefab.Find(null, "divingsuit".ToIdentifier());
|
||||
ItemPrefab oxyPrefab = ItemPrefab.Find(null, "oxygentank".ToIdentifier());
|
||||
ItemPrefab scooterPrefab = ItemPrefab.Find(null, "underwaterscooter".ToIdentifier());
|
||||
ItemPrefab batteryPrefab = ItemPrefab.Find(null, "batterycell".ToIdentifier());
|
||||
|
||||
var cargoSp = WayPoint.WayPointList.Find(wp => wp.Submarine == respawnSub && wp.SpawnType == SpawnType.Cargo);
|
||||
//the spawnpoints where the characters will spawn
|
||||
var selectedSpawnPoints = WayPoint.SelectCrewSpawnPoints(characterInfos, respawnSub);
|
||||
if (isPvPMode && Level.Loaded != null && Level.Loaded.ShouldSpawnCrewInsideOutpost())
|
||||
{
|
||||
var spawnWaypoints = WayPoint.GetOutpostSpawnPoints(teamID);
|
||||
for (int i = 0; i < characterInfos.Count; i++)
|
||||
{
|
||||
selectedSpawnPoints[i] = spawnWaypoints.GetRandomUnsynced();
|
||||
}
|
||||
}
|
||||
|
||||
anyCharacterSpawnedInShuttle = false;
|
||||
//the spawnpoints where they would spawn if they were spawned inside the main sub
|
||||
//(in order to give them appropriate ID card tags)
|
||||
var mainSubSpawnPoints = mainSub != null ? WayPoint.SelectCrewSpawnPoints(characterInfos, mainSub) : null;
|
||||
var cargoSp = WayPoint.WayPointList.Find(wp => wp.Submarine == respawnSub && wp.SpawnType == SpawnType.Cargo);
|
||||
|
||||
for (int i = 0; i < characterInfos.Count; i++)
|
||||
{
|
||||
bool bot = i >= clients.Count;
|
||||
var characterInfo = characterInfos[i];
|
||||
|
||||
characterInfos[i].ClearCurrentOrders();
|
||||
bool bot = botsToSpawn.Contains(characterInfo);
|
||||
characterInfo.ClearCurrentOrders();
|
||||
|
||||
CharacterCampaignData characterCampaignData = null;
|
||||
bool forceSpawnInMainSub = false;
|
||||
@@ -413,7 +443,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
//the client has opted to change the name of their new character
|
||||
//when the character spawns, set the client's name to match
|
||||
if (clients[i].PendingName == characterInfos[i].Name)
|
||||
if (clients[i].PendingName == characterInfo.Name)
|
||||
{
|
||||
GameMain.Server?.TryChangeClientName(clients[i], clients[i].PendingName);
|
||||
clients[i].PendingName = null;
|
||||
@@ -428,32 +458,31 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
ReduceCharacterSkillsOnDeath(characterInfos[i]);
|
||||
characterInfos[i].RemoveSavedStatValuesOnDeath();
|
||||
characterInfos[i].CauseOfDeath = null;
|
||||
ReduceCharacterSkillsOnDeath(characterInfo);
|
||||
characterInfo.RemoveSavedStatValuesOnDeath();
|
||||
characterInfo.CauseOfDeath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!forceSpawnInMainSub)
|
||||
if (!forceSpawnInMainSub && respawnShuttle != null)
|
||||
{
|
||||
anyCharacterSpawnedInShuttle = true;
|
||||
anyCharacterSpawnedInShuttle = true;
|
||||
}
|
||||
|
||||
var character = Character.Create(characterInfos[i], (forceSpawnInMainSub ? mainSubSpawnPoints[i] : shuttleSpawnPoints[i]).WorldPosition, characterInfos[i].Name, isRemotePlayer: !bot, hasAi: bot);
|
||||
var character = Character.Create(characterInfo, (forceSpawnInMainSub ? mainSubSpawnPoints[i] : selectedSpawnPoints[i]).WorldPosition, characterInfo.Name, isRemotePlayer: !bot, hasAi: bot);
|
||||
characterCampaignData?.ApplyWalletData(character);
|
||||
character.TeamID = CharacterTeamType.Team1;
|
||||
character.LoadTalents();
|
||||
if (characterInfos[i].LastRewardDistribution.TryUnwrap(out int salary))
|
||||
if (characterInfo.LastRewardDistribution.TryUnwrap(out int salary))
|
||||
{
|
||||
character.Wallet.SetRewardDistribution(salary);
|
||||
}
|
||||
|
||||
respawnedCharacters.Add(character);
|
||||
teamSpecificState.RespawnedCharacters.Add(character);
|
||||
|
||||
if (bot)
|
||||
{
|
||||
GameServer.Log(string.Format("Respawning bot {0} as {1}", character.Info.Name, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
|
||||
GameServer.Log(string.Format("Respawning bot {0} as {1}.", character.Info.Name, characterInfo.Job.Name), ServerLog.MessageType.Spawning);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -475,11 +504,18 @@ namespace Barotrauma.Networking
|
||||
clients[i].Character = character;
|
||||
character.SetOwnerClient(clients[i]);
|
||||
GameServer.Log(
|
||||
$"Respawning {GameServer.ClientLogName(clients[i])} ({clients[i].Connection.Endpoint}) as {characterInfos[i].Job.Name}", ServerLog.MessageType.Spawning);
|
||||
$"Respawning {GameServer.ClientLogName(clients[i])} ({clients[i].Connection.Endpoint}) as {characterInfo.Job.Name}.", ServerLog.MessageType.Spawning);
|
||||
}
|
||||
|
||||
if (RespawnShuttle != null && anyCharacterSpawnedInShuttle)
|
||||
if (respawnShuttle != null && anyCharacterSpawnedInShuttle)
|
||||
{
|
||||
GameServer.Log($"Dispatching the {GetRespawnShuttleText(teamID)}.", ServerLog.MessageType.Spawning);
|
||||
respawnShuttle.SetPosition(shuttlePos.Value);
|
||||
respawnShuttle.Velocity = Vector2.Zero;
|
||||
respawnShuttle.NeutralizeBallast();
|
||||
respawnShuttle.EnableMaintainPosition();
|
||||
shuttleSteering[teamID].ForEach(s => s.TargetVelocity = Vector2.Zero);
|
||||
|
||||
List<Item> newRespawnItems = new List<Item>();
|
||||
Vector2 pos = cargoSp?.Position ?? character.Position;
|
||||
if (divingSuitPrefab != null)
|
||||
@@ -513,25 +549,29 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
}
|
||||
if (respawnContainer != null)
|
||||
{
|
||||
AutoItemPlacer.RegenerateLoot(RespawnShuttle, respawnContainer);
|
||||
}
|
||||
|
||||
//try to put the items in containers in the shuttle
|
||||
foreach (var respawnItem in newRespawnItems)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(!respawnItem.Removed);
|
||||
foreach (Item shuttleItem in RespawnShuttle.GetItems(alsoFromConnectedSubs: false))
|
||||
//already in a container (a battery we just placed in a scooter?) -> don't move to a cabinet
|
||||
if (respawnItem.Container == null)
|
||||
{
|
||||
if (shuttleItem.NonInteractable || shuttleItem.NonPlayerTeamInteractable) { continue; }
|
||||
var container = shuttleItem.GetComponent<ItemContainer>();
|
||||
if (container != null && container.Inventory.TryPutItem(respawnItem, user: null))
|
||||
foreach (Item shuttleItem in respawnShuttle.GetItems(alsoFromConnectedSubs: false))
|
||||
{
|
||||
break;
|
||||
if (shuttleItem.NonInteractable || shuttleItem.NonPlayerTeamInteractable) { continue; }
|
||||
var container = shuttleItem.GetComponent<ItemContainer>();
|
||||
if (container != null && container.Inventory.TryPutItem(respawnItem, user: null))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
respawnItems.Add(respawnItem);
|
||||
teamSpecificState.RespawnItems.Add(respawnItem);
|
||||
}
|
||||
|
||||
foreach (var respawnContainer in respawnContainers[teamID])
|
||||
{
|
||||
teamSpecificState.RespawnItems.AddRange(AutoItemPlacer.RegenerateLoot(respawnShuttle, respawnContainer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,10 +581,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ReduceCharacterSkillsOnDeath(characterInfos[i], applyExtraSkillLoss: true);
|
||||
}
|
||||
WayPoint jobItemSpawnPoint = mainSubSpawnPoints != null ? mainSubSpawnPoints[i] : selectedSpawnPoints[i];
|
||||
if (characterData == null || characterData.HasSpawned)
|
||||
{
|
||||
//give the character the items they would've gotten if they had spawned in the main sub
|
||||
character.GiveJobItems(mainSubSpawnPoints[i]);
|
||||
character.GiveJobItems(isPvPMode, jobItemSpawnPoint);
|
||||
if (campaign != null)
|
||||
{
|
||||
characterData = campaign.SetClientCharacterData(clients[i]);
|
||||
@@ -559,16 +600,17 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
character.GiveJobItems(mainSubSpawnPoints[i]);
|
||||
character.GiveJobItems(isPvPMode, jobItemSpawnPoint);
|
||||
}
|
||||
characterData.ApplyHealthData(character);
|
||||
character.GiveIdCardTags(mainSubSpawnPoints[i]);
|
||||
character.GiveIdCardTags(jobItemSpawnPoint);
|
||||
characterData.HasSpawned = true;
|
||||
}
|
||||
|
||||
//add the ID card tags they should've gotten when spawning in the shuttle
|
||||
character.GiveIdCardTags(shuttleSpawnPoints[i], createNetworkEvent: true);
|
||||
character.GiveIdCardTags(selectedSpawnPoints[i], createNetworkEvent: true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -605,24 +647,29 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.WriteRangedInteger((int)CurrentState, 0, Enum.GetNames(typeof(State)).Length);
|
||||
|
||||
switch (CurrentState)
|
||||
msg.WriteByte((byte)c.TeamID);
|
||||
foreach (var teamSpecificState in teamSpecificStates.Values)
|
||||
{
|
||||
case State.Transporting:
|
||||
msg.WriteBoolean(ReturnCountdownStarted);
|
||||
msg.WriteSingle(GameMain.Server.ServerSettings.MaxTransportTime);
|
||||
msg.WriteSingle((float)(ReturnTime - DateTime.Now).TotalSeconds);
|
||||
break;
|
||||
case State.Waiting:
|
||||
msg.WriteUInt16((ushort)pendingRespawnCount);
|
||||
msg.WriteUInt16((ushort)requiredRespawnCount);
|
||||
msg.WriteBoolean(IsRespawnDecisionPendingForClient(c));
|
||||
msg.WriteBoolean(RespawnCountdownStarted);
|
||||
msg.WriteSingle((float)(RespawnTime - DateTime.Now).TotalSeconds);
|
||||
break;
|
||||
case State.Returning:
|
||||
break;
|
||||
msg.WriteByte((byte)teamSpecificState.TeamID);
|
||||
msg.WriteRangedInteger((int)teamSpecificState.CurrentState, 0, Enum.GetNames(typeof(State)).Length);
|
||||
|
||||
switch (teamSpecificState.CurrentState)
|
||||
{
|
||||
case State.Transporting:
|
||||
msg.WriteBoolean(teamSpecificState.ReturnCountdownStarted);
|
||||
msg.WriteSingle(GameMain.Server.ServerSettings.MaxTransportTime);
|
||||
msg.WriteSingle((float)(teamSpecificState.ReturnTime - DateTime.Now).TotalSeconds);
|
||||
break;
|
||||
case State.Waiting:
|
||||
msg.WriteUInt16((ushort)teamSpecificState.PendingRespawnCount);
|
||||
msg.WriteUInt16((ushort)teamSpecificState.RequiredRespawnCount);
|
||||
msg.WriteBoolean(IsRespawnDecisionPendingForClient(c));
|
||||
msg.WriteBoolean(teamSpecificState.RespawnCountdownStarted);
|
||||
msg.WriteSingle((float)(teamSpecificState.RespawnTime - DateTime.Now).TotalSeconds);
|
||||
break;
|
||||
case State.Returning:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
msg.WritePadBits();
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
using Barotrauma.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -106,6 +106,7 @@ namespace Barotrauma.Networking
|
||||
if (requiredFlags.HasFlag(NetFlags.Properties))
|
||||
{
|
||||
WriteExtraCargo(outMsg);
|
||||
WritePerks(outMsg);
|
||||
}
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.HiddenSubs))
|
||||
@@ -129,6 +130,40 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadPerks(IReadMessage incMsg, Client c)
|
||||
{
|
||||
if (!HasPermissionToChangePerks(c)) return;
|
||||
|
||||
bool changed = ReadPerks(incMsg);
|
||||
if (!changed) { return; }
|
||||
|
||||
UpdateFlag(NetFlags.Properties);
|
||||
SaveSettings();
|
||||
GameMain.NetLobbyScreen.LastUpdateID++;
|
||||
|
||||
static bool HasPermissionToChangePerks(Client client)
|
||||
{
|
||||
if (client.HasPermission(Networking.ClientPermissions.ManageSettings)) { return true; }
|
||||
|
||||
bool isPvP = GameMain.NetLobbyScreen?.SelectedMode == GameModePreset.PvP;
|
||||
bool hasSelectedTeam = client.PreferredTeam is CharacterTeamType.Team1 or CharacterTeamType.Team2;
|
||||
var otherClients = GameMain.NetworkMember?.ConnectedClients?.Where(c => c != client).ToImmutableArray() ?? ImmutableArray<Client>.Empty;
|
||||
|
||||
if (isPvP)
|
||||
{
|
||||
if (!hasSelectedTeam) { return false; }
|
||||
|
||||
return !otherClients
|
||||
.Where(c => c.PreferredTeam == client.PreferredTeam)
|
||||
.Any(static c => c.HasPermission(Networking.ClientPermissions.ManageSettings));
|
||||
}
|
||||
else
|
||||
{
|
||||
return !otherClients.Any(static c => c.HasPermission(Networking.ClientPermissions.ManageSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerRead(IReadMessage incMsg, Client c)
|
||||
{
|
||||
if (!c.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
|
||||
@@ -136,7 +171,7 @@ namespace Barotrauma.Networking
|
||||
NetFlags flags = (NetFlags)incMsg.ReadByte();
|
||||
|
||||
bool changed = false;
|
||||
|
||||
|
||||
if (flags.HasFlag(NetFlags.Properties))
|
||||
{
|
||||
bool propertiesChanged = ReadExtraCargo(incMsg);
|
||||
@@ -176,6 +211,7 @@ namespace Barotrauma.Networking
|
||||
if (propertiesChanged)
|
||||
{
|
||||
UpdateFlag(NetFlags.Properties);
|
||||
GameMain.Server.RefreshPvpTeamAssignments(); // the changed settings might be relevant to team logic, so refresh
|
||||
}
|
||||
changed |= propertiesChanged;
|
||||
}
|
||||
@@ -189,9 +225,18 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (flags.HasFlag(NetFlags.Misc))
|
||||
{
|
||||
int orBits = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
|
||||
int andBits = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
|
||||
GameMain.NetLobbyScreen.MissionType = (MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);
|
||||
List<Identifier> missionTypes = new List<Identifier>(GameMain.NetLobbyScreen.MissionTypes);
|
||||
Identifier addedMissionType = incMsg.ReadIdentifier();
|
||||
Identifier removedMissionType = incMsg.ReadIdentifier();
|
||||
if (!addedMissionType.IsEmpty)
|
||||
{
|
||||
missionTypes.Add(addedMissionType);
|
||||
}
|
||||
if (!removedMissionType.IsEmpty)
|
||||
{
|
||||
missionTypes.Remove(removedMissionType);
|
||||
}
|
||||
GameMain.NetLobbyScreen.MissionTypes = missionTypes;
|
||||
|
||||
//the byte indicates the direction we're changing the value, subtract one to get negative values from a byte
|
||||
TraitorDangerLevel = TraitorDangerLevel + incMsg.ReadByte() - 1;
|
||||
@@ -361,28 +406,20 @@ namespace Barotrauma.Networking
|
||||
if (min > -1 && max > -1) { AllowedClientNameChars.Add(new Range<int>(min, max)); }
|
||||
}
|
||||
|
||||
AllowedRandomMissionTypes = new List<MissionType>();
|
||||
string[] allowedMissionTypeNames = doc.Root.GetAttributeStringArray(
|
||||
"AllowedRandomMissionTypes", Enum.GetValues(typeof(MissionType)).Cast<MissionType>().Select(m => m.ToString()).ToArray());
|
||||
foreach (string missionTypeName in allowedMissionTypeNames)
|
||||
{
|
||||
if (Enum.TryParse(missionTypeName, out MissionType missionType))
|
||||
{
|
||||
if (missionType == Barotrauma.MissionType.None) { continue; }
|
||||
if (MissionPrefab.HiddenMissionClasses.Contains(missionType)) { continue; }
|
||||
AllowedRandomMissionTypes.Add(missionType);
|
||||
}
|
||||
}
|
||||
AllowedRandomMissionTypes = doc.Root.GetAttributeIdentifierArray(
|
||||
"AllowedRandomMissionTypes", MissionPrefab.GetAllMultiplayerSelectableMissionTypes().ToArray()).ToList();
|
||||
|
||||
ServerName = doc.Root.GetAttributeString("name", "");
|
||||
if (ServerName.Length > NetConfig.ServerNameMaxLength) { ServerName = ServerName.Substring(0, NetConfig.ServerNameMaxLength); }
|
||||
ServerMessageText = doc.Root.GetAttributeString("ServerMessage", "");
|
||||
|
||||
GameMain.NetLobbyScreen.SelectedModeIdentifier = GameModeIdentifier;
|
||||
//handle Random as the mission type, which is no longer a valid setting
|
||||
//MissionType.All offers equivalent functionality
|
||||
if (MissionType == "Random") { MissionType = "All"; }
|
||||
GameMain.NetLobbyScreen.MissionTypeName = MissionType;
|
||||
if (AllowedRandomMissionTypes.Contains(Tags.MissionTypeAll))
|
||||
{
|
||||
AllowedRandomMissionTypes = MissionPrefab.GetAllMultiplayerSelectableMissionTypes().ToList();
|
||||
}
|
||||
AllowedRandomMissionTypes = AllowedRandomMissionTypes.Distinct().ToList();
|
||||
ValidateMissionTypes();
|
||||
|
||||
GameMain.NetLobbyScreen.SetBotSpawnMode(BotSpawnMode);
|
||||
GameMain.NetLobbyScreen.SetBotCount(BotCount);
|
||||
@@ -404,6 +441,20 @@ namespace Barotrauma.Networking
|
||||
CampaignSettings = new CampaignSettings(element);
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<Identifier> selectedCoalitionPerks = SelectedCoalitionPerks.ToHashSet();
|
||||
HashSet<Identifier> selectedSeparatistsPerks = SelectedSeparatistsPerks.ToHashSet();
|
||||
foreach (DisembarkPerkPrefab prefab in DisembarkPerkPrefab.Prefabs)
|
||||
{
|
||||
if (prefab.Cost == 0)
|
||||
{
|
||||
selectedSeparatistsPerks.Add(prefab.Identifier);
|
||||
selectedCoalitionPerks.Add(prefab.Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
SelectedCoalitionPerks = selectedCoalitionPerks.ToArray();
|
||||
SelectedSeparatistsPerks = selectedSeparatistsPerks.ToArray();
|
||||
}
|
||||
|
||||
public string SelectNonHiddenSubmarine(string current = null)
|
||||
|
||||
@@ -374,13 +374,32 @@ namespace Barotrauma
|
||||
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowSubVoting);
|
||||
if (GameMain.Server.ServerSettings.AllowSubVoting)
|
||||
{
|
||||
IReadOnlyDictionary<SubmarineInfo, int> voteList = GetVoteCounts<SubmarineInfo>(VoteType.Sub, GameMain.Server.ConnectedClients);
|
||||
bool isMultiSub = GameMain.NetLobbyScreen.SelectedMode == GameModePreset.PvP;
|
||||
msg.WriteBoolean(isMultiSub);
|
||||
|
||||
var subVoters = isMultiSub ?
|
||||
GameMain.Server.ConnectedClients.Where(static c => c.PreferredTeam is CharacterTeamType.Team1) :
|
||||
GameMain.Server.ConnectedClients;
|
||||
|
||||
IReadOnlyDictionary<SubmarineInfo, int> voteList = GetVoteCounts<SubmarineInfo>(VoteType.Sub, subVoters);
|
||||
msg.WriteByte((byte)voteList.Count);
|
||||
foreach (KeyValuePair<SubmarineInfo, int> vote in voteList)
|
||||
{
|
||||
msg.WriteByte((byte)vote.Value);
|
||||
msg.WriteString(vote.Key.Name);
|
||||
}
|
||||
|
||||
if (isMultiSub)
|
||||
{
|
||||
var separatistsVotes = GetVoteCounts<SubmarineInfo>(VoteType.Sub, GameMain.Server.ConnectedClients.Where(static c => c.PreferredTeam is CharacterTeamType.Team2));
|
||||
msg.WriteByte((byte)separatistsVotes.Count);
|
||||
|
||||
foreach (var (info, amount) in separatistsVotes)
|
||||
{
|
||||
msg.WriteByte((byte)amount);
|
||||
msg.WriteString(info.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowModeVoting);
|
||||
if (GameMain.Server.ServerSettings.AllowModeVoting)
|
||||
|
||||
Reference in New Issue
Block a user