v0.10.5.1

This commit is contained in:
Juan Pablo Arce
2020-09-22 11:31:56 -03:00
parent 44032d0ae0
commit 0002ad2c50
343 changed files with 12276 additions and 5023 deletions
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Net;
using Barotrauma.Steam;
namespace Barotrauma.Networking
{
@@ -10,15 +11,16 @@ namespace Barotrauma.Networking
{
private static UInt16 LastIdentifier = 0;
public BannedPlayer(string name, string ip, string reason, DateTime? expirationTime)
public BannedPlayer(string name, string endPoint, string reason, DateTime? expirationTime)
{
this.Name = name;
this.IP = ip;
this.EndPoint = endPoint;
ParseEndPointAsSteamId();
this.Reason = reason;
this.ExpirationTime = expirationTime;
this.UniqueIdentifier = LastIdentifier; LastIdentifier++;
this.IsRangeBan = IP.IndexOf(".x") > -1;
this.IsRangeBan = EndPoint.IndexOf(".x") > -1;
}
public BannedPlayer(string name, ulong steamID, string reason, DateTime? expirationTime)
@@ -31,27 +33,27 @@ namespace Barotrauma.Networking
this.IsRangeBan = false;
this.IP = "";
this.EndPoint = "";
}
public bool CompareTo(string ipCompare)
public bool CompareTo(string endpointCompare)
{
if (string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(IP)) { return false; }
if (string.IsNullOrEmpty(EndPoint) || string.IsNullOrEmpty(EndPoint)) { return false; }
if (!IsRangeBan)
{
return ipCompare == IP;
return endpointCompare == EndPoint;
}
else
{
int rangeBanIndex = IP.IndexOf(".x");
if (ipCompare.Length < rangeBanIndex) return false;
return ipCompare.Substring(0, rangeBanIndex) == IP.Substring(0, rangeBanIndex);
int rangeBanIndex = EndPoint.IndexOf(".x");
if (endpointCompare.Length < rangeBanIndex) return false;
return endpointCompare.Substring(0, rangeBanIndex) == EndPoint.Substring(0, rangeBanIndex);
}
}
public bool CompareTo(IPAddress ipCompare)
{
if (string.IsNullOrEmpty(IP) || ipCompare == null) { return false; }
if (string.IsNullOrEmpty(EndPoint) || ipCompare == null) { return false; }
if (ipCompare.IsIPv4MappedToIPv6 && CompareTo(ipCompare.MapToIPv4NoThrow().ToString()))
{
return true;
@@ -123,7 +125,10 @@ namespace Barotrauma.Networking
{
reason = string.Empty;
if (IPAddress.IsLoopback(IP)) { return false; }
var bannedPlayer = bannedPlayers.Find(bp => bp.CompareTo(IP) || (steamID > 0 && bp.SteamID == steamID));
var bannedPlayer = bannedPlayers.Find(bp =>
bp.CompareTo(IP) ||
(steamID > 0 && bp.SteamID == steamID) ||
(SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID));
reason = bannedPlayer?.Reason;
return bannedPlayer != null;
}
@@ -142,7 +147,9 @@ namespace Barotrauma.Networking
{
reason = string.Empty;
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
var bannedPlayer = bannedPlayers.Find(bp => steamID > 0 && bp.SteamID == steamID);
var bannedPlayer = bannedPlayers.Find(bp =>
steamID > 0 &&
(bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID));
reason = bannedPlayer?.Reason;
return bannedPlayer != null;
}
@@ -153,9 +160,9 @@ namespace Barotrauma.Networking
BanPlayer(name, ipStr, 0, reason, duration);
}
public void BanPlayer(string name, string ip, string reason, TimeSpan? duration)
public void BanPlayer(string name, string endPoint, string reason, TimeSpan? duration)
{
BanPlayer(name, ip, 0, reason, duration);
BanPlayer(name, endPoint, 0, reason, duration);
}
public void BanPlayer(string name, ulong steamID, string reason, TimeSpan? duration)
@@ -163,9 +170,9 @@ namespace Barotrauma.Networking
BanPlayer(name, "", steamID, reason, duration);
}
private void BanPlayer(string name, string ip, ulong steamID, string reason, TimeSpan? duration)
private void BanPlayer(string name, string endPoint, ulong steamID, string reason, TimeSpan? duration)
{
var existingBan = bannedPlayers.Find(bp => bp.IP == ip && bp.SteamID == steamID);
var existingBan = bannedPlayers.Find(bp => bp.EndPoint == endPoint && bp.SteamID == steamID);
if (existingBan != null)
{
if (!duration.HasValue) return;
@@ -190,9 +197,9 @@ namespace Barotrauma.Networking
expirationTime = DateTime.Now + duration.Value;
}
if (!string.IsNullOrEmpty(ip))
if (!string.IsNullOrEmpty(endPoint))
{
bannedPlayers.Add(new BannedPlayer(name, ip, reason, expirationTime));
bannedPlayers.Add(new BannedPlayer(name, endPoint, reason, expirationTime));
}
else if (steamID > 0)
{
@@ -221,12 +228,15 @@ namespace Barotrauma.Networking
}
}
public void UnbanIP(string ip)
public void UnbanEndPoint(string endPoint)
{
var player = bannedPlayers.Find(bp => bp.IP == ip);
ulong steamId = SteamManager.SteamIDStringToUInt64(endPoint);
var player = bannedPlayers.Find(bp =>
bp.EndPoint == endPoint ||
(steamId != 0 && steamId == SteamManager.SteamIDStringToUInt64(bp.EndPoint)));
if (player == null)
{
DebugConsole.Log("Could not unban IP \"" + ip + "\". Matching player not found.");
DebugConsole.Log("Could not unban endpoint \"" + endPoint + "\". Matching player not found.");
}
else
{
@@ -246,10 +256,10 @@ namespace Barotrauma.Networking
private void RangeBan(BannedPlayer banned)
{
banned.IP = ToRange(banned.IP);
banned.EndPoint = ToRange(banned.EndPoint);
BannedPlayer bp;
while ((bp = bannedPlayers.Find(x => banned.CompareTo(x.IP))) != null)
while ((bp = bannedPlayers.Find(x => banned.CompareTo(x.EndPoint))) != null)
{
//remove all specific bans that are now covered by the rangeban
bannedPlayers.Remove(bp);
@@ -270,7 +280,7 @@ namespace Barotrauma.Networking
foreach (BannedPlayer banned in bannedPlayers)
{
string line = banned.Name;
line += "," + ((banned.SteamID > 0) ? banned.SteamID.ToString() : banned.IP);
line += "," + ((banned.SteamID > 0) ? SteamManager.SteamIDUInt64ToString(banned.SteamID) : banned.EndPoint);
line += "," + (banned.ExpirationTime.HasValue ? banned.ExpirationTime.Value.ToString() : "");
if (!string.IsNullOrWhiteSpace(banned.Reason)) line += "," + banned.Reason;
@@ -314,7 +324,7 @@ namespace Barotrauma.Networking
outMsg.Write(bannedPlayer.IsRangeBan); outMsg.WritePadBits();
if (c.Connection == GameMain.Server.OwnerConnection)
{
outMsg.Write(bannedPlayer.IP);
outMsg.Write(bannedPlayer.EndPoint);
outMsg.Write(bannedPlayer.SteamID);
}
}
@@ -347,7 +357,7 @@ namespace Barotrauma.Networking
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
if (bannedPlayer != null)
{
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.IP + ")", ServerLog.MessageType.ConsoleUsage);
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.EndPoint + ")", ServerLog.MessageType.ConsoleUsage);
RemoveBan(bannedPlayer);
}
}
@@ -358,7 +368,7 @@ namespace Barotrauma.Networking
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
if (bannedPlayer != null)
{
GameServer.Log(GameServer.ClientLogName(c) + " rangebanned " + bannedPlayer.Name + " (" + bannedPlayer.IP + ")", ServerLog.MessageType.ConsoleUsage);
GameServer.Log(GameServer.ClientLogName(c) + " rangebanned " + bannedPlayer.Name + " (" + bannedPlayer.EndPoint + ")", ServerLog.MessageType.ConsoleUsage);
RangeBan(bannedPlayer);
}
}
@@ -48,8 +48,8 @@ namespace Barotrauma.Networking
public readonly Dictionary<UInt16, double> EntityEventLastSent = new Dictionary<UInt16, double>();
//when was a position update for a given entity last sent to the client
// key = entity id, value = NetTime.Now when sending
public readonly Dictionary<UInt16, float> PositionUpdateLastSent = new Dictionary<UInt16, float>();
// key = entity, value = NetTime.Now when sending
public readonly Dictionary<Entity, float> PositionUpdateLastSent = new Dictionary<Entity, float>();
public readonly Queue<Entity> PendingPositionUpdates = new Queue<Entity>();
public bool ReadyToStart;
@@ -145,19 +145,9 @@ namespace Barotrauma.Networking
return true;
}
public bool EndpointMatches(string endpoint)
public bool EndpointMatches(string endPoint)
{
if (Connection is LidgrenConnection lidgrenConn)
{
if (lidgrenConn.IPEndPoint?.Address == null) { return false; }
if ((lidgrenConn.IPEndPoint?.Address.IsIPv4MappedToIPv6 ?? false) &&
lidgrenConn.IPEndPoint?.Address.MapToIPv4NoThrow().ToString() == endpoint)
{
return true;
}
}
return Connection.EndPointString == endpoint;
return Connection.EndpointMatches(endPoint);
}
public void SetPermissions(ClientPermissions permissions, List<DebugConsole.Command> permittedConsoleCommands)
@@ -135,7 +135,7 @@ namespace Barotrauma.Networking
if (!File.Exists(filePath))
{
DebugConsole.ThrowError("Failed to initiate file transfer (file \"" + filePath + "\" not found.");
DebugConsole.ThrowError("Failed to initiate file transfer (file \"" + filePath + "\" not found).\n" + Environment.StackTrace);
return null;
}
@@ -282,7 +282,9 @@ namespace Barotrauma.Networking
Client newClient = new Client(clName, GetNewClientID());
newClient.InitClientSync();
newClient.Connection = connection;
newClient.Connection.Status = NetworkConnectionStatus.Connected;
newClient.SteamID = connection.SteamID;
newClient.Language = connection.Language;
ConnectedClients.Add(newClient);
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(newClient));
@@ -382,14 +384,12 @@ namespace Barotrauma.Networking
for (int i = Character.CharacterList.Count - 1; i >= 0; i--)
{
Character character = Character.CharacterList[i];
if (character.IsDead || !character.ClientDisconnected) continue;
if (character.IsDead || !character.ClientDisconnected) { continue; }
character.KillDisconnectedTimer += deltaTime;
character.SetStun(1.0f);
Client owner = connectedClients.Find(c =>
c.Name == character.OwnerClientName &&
c.EndpointMatches(character.OwnerClientEndPoint));
Client owner = connectedClients.Find(c => c.EndpointMatches(character.OwnerClientEndPoint));
if ((OwnerConnection == null || owner?.Connection != OwnerConnection) && character.KillDisconnectedTimer > serverSettings.KillDisconnectedTime)
{
@@ -397,8 +397,7 @@ namespace Barotrauma.Networking
continue;
}
if (owner != null &&
owner.InGame && !owner.NeedsMidRoundSync &&
if (owner != null && owner.InGame && !owner.NeedsMidRoundSync &&
(!serverSettings.AllowSpectating || !owner.SpectateOnly))
{
SetClientCharacter(owner, character);
@@ -436,16 +435,21 @@ namespace Barotrauma.Networking
{
if (Level.Loaded?.EndOutpost != null)
{
bool charactersInsideOutpost = connectedClients.Any(c =>
int charactersInsideOutpost = connectedClients.Count(c =>
c.Character != null &&
!c.Character.IsDead &&
!c.Character.IsDead && !c.Character.IsUnconscious &&
c.Character.Submarine == Level.Loaded.EndOutpost);
int charactersOutsideOutpost = connectedClients.Count(c =>
c.Character != null &&
!c.Character.IsDead && !c.Character.IsUnconscious &&
c.Character.Submarine != Level.Loaded.EndOutpost);
//level finished if the sub is docked to the outpost
//or very close and someone from the crew made it inside the outpost
subAtLevelEnd =
Submarine.MainSub.DockedTo.Contains(Level.Loaded.EndOutpost) ||
(Submarine.MainSub.AtEndPosition && charactersInsideOutpost);
(Submarine.MainSub.AtEndPosition && charactersInsideOutpost > 0) ||
(charactersInsideOutpost > charactersOutsideOutpost);
}
else
{
@@ -464,7 +468,7 @@ namespace Barotrauma.Networking
endRoundDelay = 5.0f;
endRoundTimer += deltaTime;
}
else if (serverSettings.EndRoundAtLevelEnd && subAtLevelEnd && !(GameMain.GameSession?.GameMode is CampaignMode))
else if (subAtLevelEnd && !(GameMain.GameSession?.GameMode is CampaignMode))
{
endRoundDelay = 5.0f;
endRoundTimer += deltaTime;
@@ -493,7 +497,7 @@ namespace Barotrauma.Networking
{
Log("Ending round (entire crew dead)", ServerLog.MessageType.ServerMessage);
}
else if (serverSettings.EndRoundAtLevelEnd && subAtLevelEnd)
else if (subAtLevelEnd)
{
Log("Ending round (submarine reached the end of the level)", ServerLog.MessageType.ServerMessage);
}
@@ -628,7 +632,11 @@ namespace Barotrauma.Networking
{
if (character.healthUpdateTimer <= 0.0f)
{
character.healthUpdateTimer = character.HealthUpdateInterval;
if (!character.HealthUpdatePending)
{
character.healthUpdateTimer = character.HealthUpdateInterval;
}
character.HealthUpdatePending = true;
}
else
{
@@ -1532,8 +1540,7 @@ namespace Barotrauma.Networking
{
foreach (Character character in Character.CharacterList)
{
if (!character.Enabled) continue;
if (!character.Enabled) { continue; }
if (c.SpectatePos == null)
{
if (c.Character != null && Vector2.DistanceSquared(character.WorldPosition, c.Character.WorldPosition) >= NetConfig.DisableCharacterDistSqr)
@@ -1543,17 +1550,24 @@ namespace Barotrauma.Networking
}
else
{
if (Vector2.DistanceSquared(character.WorldPosition, c.SpectatePos.Value) >= NetConfig.DisableCharacterDistSqr)
if (character != c.Character && Vector2.DistanceSquared(character.WorldPosition, c.SpectatePos.Value) >= NetConfig.DisableCharacterDistSqr)
{
continue;
}
}
float updateInterval = character.GetPositionUpdateInterval(c);
c.PositionUpdateLastSent.TryGetValue(character.ID, out float lastSent);
if (lastSent > Lidgren.Network.NetTime.Now - updateInterval) { continue; }
if (!c.PendingPositionUpdates.Contains(character)) c.PendingPositionUpdates.Enqueue(character);
c.PositionUpdateLastSent.TryGetValue(character, out float lastSent);
if (lastSent > NetTime.Now)
{
//sent in the future -> can't be right, remove
c.PositionUpdateLastSent.Remove(character);
}
else
{
if (lastSent > NetTime.Now - updateInterval) { continue; }
}
if (!c.PendingPositionUpdates.Contains(character)) { c.PendingPositionUpdates.Enqueue(character); }
}
foreach (Submarine sub in Submarine.Loaded)
@@ -1568,16 +1582,24 @@ namespace Barotrauma.Networking
{
if (item.PositionUpdateInterval == float.PositiveInfinity) { continue; }
float updateInterval = item.GetPositionUpdateInterval(c);
c.PositionUpdateLastSent.TryGetValue(item.ID, out float lastSent);
if (lastSent > Lidgren.Network.NetTime.Now - updateInterval) { continue; }
if (!c.PendingPositionUpdates.Contains(item)) c.PendingPositionUpdates.Enqueue(item);
c.PositionUpdateLastSent.TryGetValue(item, out float lastSent);
if (lastSent > NetTime.Now)
{
//sent in the future -> can't be right, remove
c.PositionUpdateLastSent.Remove(item);
}
else
{
if (lastSent > NetTime.Now - updateInterval) { continue; }
}
if (!c.PendingPositionUpdates.Contains(item)) { c.PendingPositionUpdates.Enqueue(item); }
}
}
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME);
outmsg.Write((float)Lidgren.Network.NetTime.Now);
outmsg.Write((float)NetTime.Now);
outmsg.Write((byte)ServerNetObject.SYNC_IDS);
outmsg.Write(c.LastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server
@@ -1636,7 +1658,7 @@ namespace Barotrauma.Networking
outmsg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
outmsg.WritePadBits();
c.PositionUpdateLastSent[entity.ID] = (float)Lidgren.Network.NetTime.Now;
c.PositionUpdateLastSent[entity] = (float)NetTime.Now;
c.PendingPositionUpdates.Dequeue();
}
positionUpdateBytes = outmsg.LengthBytes - positionUpdateBytes;
@@ -1713,7 +1735,7 @@ namespace Barotrauma.Networking
outmsg.Write(client.SteamID);
outmsg.Write(client.NameID);
outmsg.Write(client.Name);
outmsg.Write(client.Character == null || !gameStarted ? (client.PreferredJob ?? "") : "");
outmsg.Write(client.Character?.Info?.Job != null && gameStarted ? client.Character.Info.Job.Prefab.Identifier : (client.PreferredJob ?? ""));
outmsg.Write(client.Character == null || !gameStarted ? (ushort)0 : client.Character.ID);
if (c.HasPermission(ClientPermissions.ServerLog))
{
@@ -2099,11 +2121,12 @@ namespace Barotrauma.Networking
}
MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode;
bool missionAllowRespawn = campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn);
bool missionAllowRespawn = GameMain.GameSession.Campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn);
bool outpostAllowRespawn = GameMain.GameSession.Campaign != null && Level.Loaded?.Type == LevelData.LevelType.Outpost;
if (serverSettings.AllowRespawn && missionAllowRespawn)
if (serverSettings.AllowRespawn && (missionAllowRespawn || outpostAllowRespawn))
{
respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle ? selectedShuttle : null);
respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle && !outpostAllowRespawn ? selectedShuttle : null);
}
Level.Loaded?.SpawnNPCs();
@@ -2298,6 +2321,11 @@ namespace Barotrauma.Networking
yield return CoroutineStatus.Running;
if (GameMain.Server?.ServerSettings?.Voting != null)
{
GameMain.Server.ServerSettings.Voting.ResetVotes(GameMain.Server.ConnectedClients);
}
GameMain.GameScreen.Select();
Log("Round started.", ServerLog.MessageType.ServerMessage);
@@ -2332,7 +2360,8 @@ namespace Barotrauma.Networking
msg.Write(gameSession.GameMode.Preset.Identifier);
bool missionAllowRespawn = campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn);
msg.Write(serverSettings.AllowRespawn && missionAllowRespawn);
bool outpostAllowRespawn = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost;
msg.Write(missionAllowRespawn || outpostAllowRespawn);
msg.Write(serverSettings.AllowDisguises);
msg.Write(serverSettings.AllowRewiring);
msg.Write(serverSettings.AllowRagdollButton);
@@ -2470,23 +2499,6 @@ namespace Barotrauma.Networking
traitorResult.ServerWrite(msg);
}
// used to check if client and server mismatch upgrades
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
msg.Write(true);
Dictionary<string, int> dict = UpgradeManager.GetMetadataLevels(campaign?.CampaignMetadata);
msg.Write((ushort)dict.Count);
foreach (var (key, value) in dict)
{
msg.Write(key);
msg.Write((byte)value);
}
}
else
{
msg.Write(false);
}
foreach (Client client in connectedClients)
{
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
@@ -2544,12 +2556,18 @@ namespace Barotrauma.Networking
{
if (!Client.IsValidName(newName, serverSettings))
{
SendDirectChatMessage("Could not change your name to \"" + newName + "\" (the name contains disallowed symbols).", c, ChatMessageType.MessageBox);
SendDirectChatMessage($"ServerMessage.NameChangeFailedSymbols~[newname]={newName}", c, ChatMessageType.ServerMessageBox);
return false;
}
if (Homoglyphs.Compare(newName.ToLower(), ServerName.ToLower()))
{
SendDirectChatMessage("Could not change your name to \"" + newName + "\" (too similar to the server's name).", c, ChatMessageType.MessageBox);
SendDirectChatMessage($"ServerMessage.NameChangeFailedServerTooSimilar~[newname]={newName}", c, ChatMessageType.ServerMessageBox);
return false;
}
if (c.KickVoteCount > 0)
{
SendDirectChatMessage($"ServerMessage.NameChangeFailedVoteKick~[newname]={newName}", c, ChatMessageType.ServerMessageBox);
return false;
}
}
@@ -2557,11 +2575,11 @@ namespace Barotrauma.Networking
Client nameTaken = ConnectedClients.Find(c2 => c != c2 && Homoglyphs.Compare(c2.Name.ToLower(), newName.ToLower()));
if (nameTaken != null)
{
SendDirectChatMessage("Could not change your name to \"" + newName + "\" (too similar to the name of the client \"" + nameTaken.Name + "\").", c, ChatMessageType.MessageBox);
SendDirectChatMessage($"ServerMessage.NameChangeFailedClientTooSimilar~[newname]={newName}~[takenname]={nameTaken.Name}", c, ChatMessageType.ServerMessageBox);
return false;
}
SendChatMessage("Player \"" + c.Name + "\" has changed their name to \"" + newName + "\".", ChatMessageType.Server);
SendChatMessage($"ServerMessage.NameChangeSuccessful~[oldname]={c.Name}~[newname]={newName}", ChatMessageType.Server);
c.Name = newName;
c.Connection.Name = newName;
return true;
@@ -2633,16 +2651,13 @@ namespace Barotrauma.Networking
string targetMsg = DisconnectReason.Banned.ToString();
DisconnectClient(client, $"ServerMessage.BannedFromServer~[client]={client.Name}", targetMsg, reason, PlayerConnectionChangeType.Banned);
if (client.SteamID == 0 || range)
if (client.Connection is LidgrenConnection lidgrenConn && (client.SteamID == 0 || range))
{
string ip = "";
if (client.Connection is LidgrenConnection lidgrenConn)
{
ip = lidgrenConn.IPEndPoint.Address.IsIPv4MappedToIPv6 ?
lidgrenConn.IPEndPoint.Address.MapToIPv4NoThrow().ToString() :
lidgrenConn.IPEndPoint.Address.ToString();
if (range) { ip = BanList.ToRange(ip); }
}
ip = lidgrenConn.IPEndPoint.Address.IsIPv4MappedToIPv6 ?
lidgrenConn.IPEndPoint.Address.MapToIPv4NoThrow().ToString() :
lidgrenConn.IPEndPoint.Address.ToString();
if (range) { ip = BanList.ToRange(ip); }
serverSettings.BanList.BanPlayer(client.Name, ip, reason, duration);
}
@@ -2652,11 +2667,11 @@ namespace Barotrauma.Networking
}
}
public override void UnbanPlayer(string playerName, string playerIP)
public override void UnbanPlayer(string playerName, string playerEndPoint)
{
if (!string.IsNullOrEmpty(playerIP))
if (!string.IsNullOrEmpty(playerEndPoint))
{
serverSettings.BanList.UnbanIP(playerIP);
serverSettings.BanList.UnbanEndPoint(playerEndPoint);
}
else if (!string.IsNullOrEmpty(playerName))
{
@@ -8,41 +8,9 @@ namespace Barotrauma.Networking
{
class LidgrenServerPeer : ServerPeer
{
private readonly ServerSettings serverSettings;
private NetPeerConfiguration netPeerConfiguration;
private NetServer netServer;
private class PendingClient
{
public string Name;
public int OwnerKey;
public NetConnection Connection;
public ConnectionInitialization InitializationStep;
public double UpdateTime;
public double TimeOut;
public int Retries;
public UInt64? SteamID;
public Int32? PasswordSalt;
public bool AuthSessionStarted;
public PendingClient(NetConnection conn)
{
OwnerKey = 0;
Connection = conn;
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
Retries = 0;
SteamID = null;
PasswordSalt = null;
UpdateTime = Timing.TotalTime + Timing.Step * 3.0;
TimeOut = NetworkConnection.TimeoutThreshold;
AuthSessionStarted = false;
}
}
private readonly List<LidgrenConnection> connectedClients;
private readonly List<PendingClient> pendingClients;
private readonly List<NetIncomingMessage> incomingLidgrenMessages;
public LidgrenServerPeer(int? ownKey, ServerSettings settings)
@@ -51,7 +19,7 @@ namespace Barotrauma.Networking
netServer = null;
connectedClients = new List<LidgrenConnection>();
connectedClients = new List<NetworkConnection>();
pendingClients = new List<PendingClient>();
incomingLidgrenMessages = new List<NetIncomingMessage>();
@@ -168,7 +136,16 @@ namespace Barotrauma.Networking
for (int i = 0; i < pendingClients.Count; i++)
{
PendingClient pendingClient = pendingClients[i];
UpdatePendingClient(pendingClient, deltaTime);
var connection = pendingClient.Connection as LidgrenConnection;
if (connection.NetConnection.Status == NetConnectionStatus.InitiatedConnect ||
connection.NetConnection.Status == NetConnectionStatus.ReceivedInitiation ||
connection.NetConnection.Status == NetConnectionStatus.RespondedAwaitingApproval ||
connection.NetConnection.Status == NetConnectionStatus.RespondedConnect)
{
continue;
}
UpdatePendingClient(pendingClient);
if (i >= pendingClients.Count || pendingClients[i] != pendingClient) { i--; }
}
@@ -214,11 +191,11 @@ namespace Barotrauma.Networking
return;
}
PendingClient pendingClient = pendingClients.Find(c => c.Connection == inc.SenderConnection);
PendingClient pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
if (pendingClient == null)
{
pendingClient = new PendingClient(inc.SenderConnection);
pendingClient = new PendingClient(new LidgrenConnection("PENDING", inc.SenderConnection, 0));
pendingClients.Add(pendingClient);
}
@@ -229,7 +206,7 @@ namespace Barotrauma.Networking
{
if (netServer == null) { return; }
PendingClient pendingClient = pendingClients.Find(c => c.Connection == inc.SenderConnection);
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
byte incByte = inc.ReadByte();
bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
@@ -237,11 +214,11 @@ namespace Barotrauma.Networking
if (isConnectionInitializationStep && pendingClient != null)
{
ReadConnectionInitializationStep(pendingClient, inc);
ReadConnectionInitializationStep(pendingClient, new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
}
else if (!isConnectionInitializationStep)
{
LidgrenConnection conn = connectedClients.Find(c => c.NetConnection == inc.SenderConnection);
LidgrenConnection conn = connectedClients.Find(c => (c is LidgrenConnection l) && l.NetConnection == inc.SenderConnection) as LidgrenConnection;
if (conn == null)
{
if (pendingClient != null)
@@ -278,7 +255,7 @@ namespace Barotrauma.Networking
{
case NetConnectionStatus.Disconnected:
string disconnectMsg;
LidgrenConnection conn = connectedClients.Find(c => c.NetConnection == inc.SenderConnection);
LidgrenConnection conn = connectedClients.Select(c => c as LidgrenConnection).FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
if (conn != null)
{
if (conn == OwnerConnection)
@@ -295,7 +272,7 @@ namespace Barotrauma.Networking
}
else
{
PendingClient pendingClient = pendingClients.Find(c => c.Connection == inc.SenderConnection);
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
if (pendingClient != null)
{
RemovePendingClient(pendingClient, DisconnectReason.Unknown, $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}");
@@ -305,303 +282,6 @@ namespace Barotrauma.Networking
}
}
private void ReadConnectionInitializationStep(PendingClient pendingClient, NetIncomingMessage inc)
{
if (netServer == null) { return; }
pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
//DebugConsole.NewMessage(initializationStep+" "+pendingClient.InitializationStep);
if (pendingClient.InitializationStep != initializationStep) return;
pendingClient.UpdateTime = Timing.TotalTime + Timing.Step;
switch (initializationStep)
{
case ConnectionInitialization.SteamTicketAndVersion:
string name = Client.SanitizeName(inc.ReadString());
int ownKey = inc.ReadInt32();
UInt64 steamId = inc.ReadUInt64();
UInt16 ticketLength = inc.ReadUInt16();
byte[] ticket = inc.ReadBytes(ticketLength);
if (!Client.IsValidName(name, serverSettings))
{
if (OwnerConnection != null ||
!IPAddress.IsLoopback(pendingClient.Connection.RemoteEndPoint.Address.MapToIPv4NoThrow()) &&
ownerKey == null || ownKey == 0 && ownKey != ownerKey)
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
return;
}
}
string version = inc.ReadString();
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
if (!isCompatibleVersion)
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(name + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
return;
}
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
if (nameTaken != null)
{
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
return;
}
int contentPackageCount = inc.ReadVariableInt32();
List<ClientContentPackage> clientContentPackages = new List<ClientContentPackage>();
for (int i = 0; i < contentPackageCount; i++)
{
string packageName = inc.ReadString();
string packageHash = inc.ReadString();
clientContentPackages.Add(new ClientContentPackage(packageName, packageHash));
}
//check if the client is missing any of our packages
List<ContentPackage> missingPackages = new List<ContentPackage>();
foreach (ContentPackage serverContentPackage in GameMain.SelectedPackages)
{
if (!serverContentPackage.HasMultiplayerIncompatibleContent) continue;
bool packageFound = clientContentPackages.Any(cp => cp.Name == serverContentPackage.Name && cp.Hash == serverContentPackage.MD5hash.Hash);
if (!packageFound) { missingPackages.Add(serverContentPackage); }
}
//check if the client is using packages we don't have
List<ClientContentPackage> redundantPackages = new List<ClientContentPackage>();
foreach (ClientContentPackage clientContentPackage in clientContentPackages)
{
bool packageFound = GameMain.SelectedPackages.Any(cp => cp.Name == clientContentPackage.Name && cp.MD5hash.Hash == clientContentPackage.Hash);
if (!packageFound) { redundantPackages.Add(clientContentPackage); }
}
if (missingPackages.Count == 1)
{
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
$"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
return;
}
else if (missingPackages.Count > 1)
{
List<string> packageStrs = new List<string>();
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
$"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return;
}
if (redundantPackages.Count == 1)
{
RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr(redundantPackages[0])}");
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (using an incompatible content package " + GetPackageStr(redundantPackages[0]) + ")", ServerLog.MessageType.Error);
return;
}
if (redundantPackages.Count > 1)
{
List<string> packageStrs = new List<string>();
redundantPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
GameServer.Log(name + " (" + inc.SenderConnection.RemoteEndPoint.Address + ") couldn't join the server (using incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return;
}
if (pendingClient.SteamID == null)
{
bool requireSteamAuth = GameMain.Config.RequireSteamAuthentication;
#if DEBUG
requireSteamAuth = false;
#endif
//steam auth cannot be done (SteamManager not initialized or no ticket given),
//but it's not required either -> let the client join without auth
if ((!Steam.SteamManager.IsInitialized || (ticket?.Length ?? 0) == 0) &&
!requireSteamAuth)
{
pendingClient.Name = name;
pendingClient.OwnerKey = ownKey;
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
else
{
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: " + authSessionStartState.ToString());
return;
}
pendingClient.SteamID = steamId;
pendingClient.Name = name;
pendingClient.OwnerKey = ownKey;
pendingClient.AuthSessionStarted = true;
}
}
else //TODO: could remove since this seems impossible
{
if (pendingClient.SteamID != steamId)
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
return;
}
}
break;
case ConnectionInitialization.Password:
int pwLength = inc.ReadByte();
byte[] incPassword = new byte[pwLength];
inc.ReadBytes(incPassword, 0, pwLength);
if (pendingClient.PasswordSalt == null)
{
DebugConsole.ThrowError("Received password message from client without salt");
return;
}
if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
{
pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
}
else
{
pendingClient.Retries++;
if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
{
string banMsg = "Failed to enter correct password too many times";
if (pendingClient.SteamID != null)
{
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID.Value, banMsg, null);
}
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.Connection.RemoteEndPoint.Address, banMsg, null);
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
return;
}
}
pendingClient.UpdateTime = Timing.TotalTime;
break;
case ConnectionInitialization.ContentPackageOrder:
pendingClient.InitializationStep = ConnectionInitialization.Success;
pendingClient.UpdateTime = Timing.TotalTime;
break;
}
}
private void UpdatePendingClient(PendingClient pendingClient, float deltaTime)
{
if (netServer == null) { return; }
if (serverSettings.BanList.IsBanned(pendingClient.Connection.RemoteEndPoint.Address, pendingClient.SteamID ?? 0, out string banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
return;
}
//DebugConsole.NewMessage("pending client status: " + pendingClient.InitializationStep);
if (connectedClients.Count >= serverSettings.MaxPlayers)
{
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
}
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
{
LidgrenConnection newConnection = new LidgrenConnection(pendingClient.Name, pendingClient.Connection, pendingClient.SteamID ?? 0)
{
Status = NetworkConnectionStatus.Connected
};
connectedClients.Add(newConnection);
pendingClients.Remove(pendingClient);
if (OwnerConnection == null &&
IPAddress.IsLoopback(pendingClient.Connection.RemoteEndPoint.Address.MapToIPv4NoThrow()) &&
ownerKey != null && pendingClient.OwnerKey != 0 && pendingClient.OwnerKey == ownerKey)
{
ownerKey = null;
OwnerConnection = newConnection;
}
OnInitializationComplete?.Invoke(newConnection);
return;
}
pendingClient.TimeOut -= deltaTime;
if (pendingClient.TimeOut < 0.0)
{
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
}
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
pendingClient.UpdateTime = Timing.TotalTime + 1.0;
NetOutgoingMessage outMsg = netServer.CreateMessage();
outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
outMsg.Write((byte)pendingClient.InitializationStep);
switch (pendingClient.InitializationStep)
{
case ConnectionInitialization.ContentPackageOrder:
var mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
outMsg.WriteVariableInt32(mpContentPackages.Count);
for (int i = 0; i < mpContentPackages.Count; i++)
{
outMsg.Write(mpContentPackages[i].MD5hash.Hash);
}
break;
case ConnectionInitialization.Password:
outMsg.Write(pendingClient.PasswordSalt == null); outMsg.WritePadBits();
if (pendingClient.PasswordSalt == null)
{
pendingClient.PasswordSalt = CryptoRandom.Instance.Next();
outMsg.Write(pendingClient.PasswordSalt.Value);
}
else
{
outMsg.Write(pendingClient.Retries);
}
break;
}
#if DEBUG
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Server.SimulatedDuplicatesChance;
netPeerConfiguration.SimulatedMinimumLatency = GameMain.Server.SimulatedMinimumLatency;
netPeerConfiguration.SimulatedRandomLatency = GameMain.Server.SimulatedRandomLatency;
netPeerConfiguration.SimulatedLoss = GameMain.Server.SimulatedLoss;
#endif
NetSendResult result = netServer.SendMessage(outMsg, pendingClient.Connection, NetDeliveryMethod.ReliableUnordered);
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
{
DebugConsole.NewMessage("Failed to send initialization step " + pendingClient.InitializationStep.ToString() + " to pending client: " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
}
//DebugConsole.NewMessage("sent update to pending client: " + pendingClient.InitializationStep);
}
private void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
{
if (netServer == null) { return; }
if (pendingClients.Contains(pendingClient))
{
pendingClients.Remove(pendingClient);
if (pendingClient.AuthSessionStarted)
{
Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value);
pendingClient.SteamID = null;
pendingClient.AuthSessionStarted = false;
}
pendingClient.Connection.Disconnect(reason + "/" + msg);
}
}
public override void InitializeSteamServerCallbacks()
{
Steamworks.SteamServer.OnValidateAuthTicketResponse += OnAuthChange;
@@ -618,7 +298,7 @@ namespace Barotrauma.Networking
{
if (status != Steamworks.AuthResponse.OK)
{
LidgrenConnection connection = connectedClients.Find(c => c.SteamID == steamID);
LidgrenConnection connection = connectedClients.Find(c => c.SteamID == steamID) as LidgrenConnection;
if (connection != null)
{
Disconnect(connection, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication status changed: " + status.ToString());
@@ -627,7 +307,8 @@ namespace Barotrauma.Networking
return;
}
if (serverSettings.BanList.IsBanned(pendingClient.Connection.RemoteEndPoint.Address, steamID, out string banReason))
LidgrenConnection pendingConnection = pendingClient.Connection as LidgrenConnection;
if (serverSettings.BanList.IsBanned(pendingConnection.NetConnection.RemoteEndPoint.Address, steamID, out string banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
return;
@@ -698,5 +379,87 @@ namespace Barotrauma.Networking
}
lidgrenConn.NetConnection.Disconnect(msg ?? "Disconnected");
}
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
{
LidgrenConnection lidgrenConn = conn as LidgrenConnection;
NetDeliveryMethod lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
switch (deliveryMethod)
{
case DeliveryMethod.Unreliable:
lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
break;
case DeliveryMethod.Reliable:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableUnordered;
break;
case DeliveryMethod.ReliableOrdered:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableOrdered;
break;
}
NetOutgoingMessage lidgrenMsg = netServer.CreateMessage();
lidgrenMsg.Write(msg.Buffer, 0, msg.LengthBytes);
NetSendResult result = netServer.SendMessage(lidgrenMsg, lidgrenConn.NetConnection, lidgrenDeliveryMethod);
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
{
DebugConsole.NewMessage("Failed to send message to " + conn.Name + ": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
}
}
protected override void CheckOwnership(PendingClient pendingClient)
{
LidgrenConnection l = pendingClient.Connection as LidgrenConnection;
if (OwnerConnection == null &&
IPAddress.IsLoopback(l.NetConnection.RemoteEndPoint.Address.MapToIPv4NoThrow()) &&
ownerKey != null && pendingClient.OwnerKey != 0 && pendingClient.OwnerKey == ownerKey)
{
ownerKey = null;
OwnerConnection = pendingClient.Connection;
}
}
protected override void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket)
{
if (pendingClient.SteamID == null)
{
bool requireSteamAuth = GameMain.Config.RequireSteamAuthentication;
#if DEBUG
requireSteamAuth = false;
#endif
//steam auth cannot be done (SteamManager not initialized or no ticket given),
//but it's not required either -> let the client join without auth
if ((!Steam.SteamManager.IsInitialized || (ticket?.Length ?? 0) == 0) &&
!requireSteamAuth)
{
pendingClient.Connection.Name = name;
pendingClient.Name = name;
pendingClient.OwnerKey = ownKey;
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
else
{
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: " + authSessionStartState.ToString());
return;
}
pendingClient.SteamID = steamId;
pendingClient.Connection.Name = name;
pendingClient.Name = name;
pendingClient.OwnerKey = ownKey;
pendingClient.AuthSessionStarted = true;
}
}
else //TODO: could remove since this seems impossible
{
if (pendingClient.SteamID != steamId)
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
return;
}
}
}
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
@@ -7,26 +8,6 @@ namespace Barotrauma.Networking
{
abstract class ServerPeer
{
protected struct ClientContentPackage
{
public string Name;
public string Hash;
public ClientContentPackage(string name, string hash)
{
Name = name; Hash = hash;
}
}
protected string GetPackageStr(ContentPackage contentPackage)
{
return "\"" + contentPackage.Name + "\" (hash " + contentPackage.MD5hash.ShortHash + ")";
}
protected string GetPackageStr(ClientContentPackage contentPackage)
{
return "\"" + contentPackage.Name + "\" (hash " + Md5Hash.GetShortHash(contentPackage.Hash) + ")";
}
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
public delegate void DisconnectCallback(NetworkConnection connection, string reason);
public delegate void InitializationCompleteCallback(NetworkConnection connection);
@@ -48,7 +29,245 @@ namespace Barotrauma.Networking
public abstract void Start();
public abstract void Close(string msg = null);
public abstract void Update(float deltaTime);
protected class PendingClient
{
public string Name;
public int OwnerKey;
public NetworkConnection Connection;
public ConnectionInitialization InitializationStep;
public double UpdateTime;
public double TimeOut;
public int Retries;
public UInt64? SteamID;
public Int32? PasswordSalt;
public bool AuthSessionStarted;
public PendingClient(NetworkConnection conn)
{
OwnerKey = 0;
Connection = conn;
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
Retries = 0;
SteamID = null;
PasswordSalt = null;
UpdateTime = Timing.TotalTime + Timing.Step * 3.0;
TimeOut = NetworkConnection.TimeoutThreshold;
AuthSessionStarted = false;
}
public void Heartbeat()
{
TimeOut = NetworkConnection.TimeoutThreshold;
}
}
protected List<NetworkConnection> connectedClients;
protected List<PendingClient> pendingClients;
protected ServerSettings serverSettings;
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
{
pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
if (pendingClient.InitializationStep != initializationStep) return;
pendingClient.UpdateTime = Timing.TotalTime + Timing.Step;
switch (initializationStep)
{
case ConnectionInitialization.SteamTicketAndVersion:
string name = Client.SanitizeName(inc.ReadString());
int ownerKey = inc.ReadInt32();
UInt64 steamId = inc.ReadUInt64();
UInt16 ticketLength = inc.ReadUInt16();
byte[] ticketBytes = inc.ReadBytes(ticketLength);
if (!Client.IsValidName(name, serverSettings))
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
return;
}
string version = inc.ReadString();
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
if (!isCompatibleVersion)
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
return;
}
string language = inc.ReadString();
pendingClient.Connection.Language = language;
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
if (nameTaken != null)
{
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
return;
}
if (!pendingClient.AuthSessionStarted)
{
ProcessAuthTicket(name, ownerKey, steamId, pendingClient, ticketBytes);
}
break;
case ConnectionInitialization.Password:
int pwLength = inc.ReadByte();
byte[] incPassword = inc.ReadBytes(pwLength);
if (pendingClient.PasswordSalt == null)
{
DebugConsole.ThrowError("Received password message from client without salt");
return;
}
if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
{
pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
}
else
{
pendingClient.Retries++;
if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
{
string banMsg = "Failed to enter correct password too many times";
BanPendingClient(pendingClient, banMsg, null);
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
return;
}
}
pendingClient.UpdateTime = Timing.TotalTime;
break;
case ConnectionInitialization.ContentPackageOrder:
pendingClient.InitializationStep = ConnectionInitialization.Success;
pendingClient.UpdateTime = Timing.TotalTime;
break;
}
}
protected abstract void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket);
protected void BanPendingClient(PendingClient pendingClient, string banReason, TimeSpan? duration)
{
if (pendingClient.Connection is LidgrenConnection l)
{
serverSettings.BanList.BanPlayer(pendingClient.Name, l.NetConnection.RemoteEndPoint.Address, banReason, duration);
}
else if (pendingClient.Connection is SteamP2PConnection s)
{
serverSettings.BanList.BanPlayer(pendingClient.Name, s.SteamID, banReason, duration);
}
}
protected bool IsPendingClientBanned(PendingClient pendingClient, out string banReason)
{
if (pendingClient.Connection is LidgrenConnection l)
{
return serverSettings.BanList.IsBanned(l.NetConnection.RemoteEndPoint.Address, out banReason);
}
else if (pendingClient.Connection is SteamP2PConnection s)
{
return serverSettings.BanList.IsBanned(s.SteamID, out banReason);
}
banReason = null;
return false;
}
protected abstract void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg);
protected void UpdatePendingClient(PendingClient pendingClient)
{
if (IsPendingClientBanned(pendingClient, out string banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
return;
}
if (connectedClients.Count >= serverSettings.MaxPlayers - 1)
{
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
}
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
{
NetworkConnection newConnection = pendingClient.Connection;
connectedClients.Add(newConnection);
pendingClients.Remove(pendingClient);
CheckOwnership(pendingClient);
OnInitializationComplete?.Invoke(newConnection);
}
pendingClient.TimeOut -= Timing.Step;
if (pendingClient.TimeOut < 0.0)
{
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
}
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
pendingClient.UpdateTime = Timing.TotalTime + 1.0;
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep |
PacketHeader.IsServerMessage));
outMsg.Write((byte)pendingClient.InitializationStep);
switch (pendingClient.InitializationStep)
{
case ConnectionInitialization.ContentPackageOrder:
outMsg.Write(GameMain.Server.ServerName);
var mpContentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count);
for (int i = 0; i < mpContentPackages.Count; i++)
{
outMsg.Write(mpContentPackages[i].Name);
outMsg.Write(mpContentPackages[i].MD5hash.Hash);
outMsg.Write(mpContentPackages[i].SteamWorkshopId);
}
break;
case ConnectionInitialization.Password:
outMsg.Write(pendingClient.PasswordSalt == null); outMsg.WritePadBits();
if (pendingClient.PasswordSalt == null)
{
pendingClient.PasswordSalt = Lidgren.Network.CryptoRandom.Instance.Next();
outMsg.Write(pendingClient.PasswordSalt.Value);
}
else
{
outMsg.Write(pendingClient.Retries);
}
break;
}
SendMsgInternal(pendingClient.Connection, DeliveryMethod.Reliable, outMsg);
}
protected virtual void CheckOwnership(PendingClient pendingClient) { }
protected void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
{
if (pendingClients.Contains(pendingClient))
{
Disconnect(pendingClient.Connection, reason + "/" + msg);
pendingClients.Remove(pendingClient);
if (pendingClient.AuthSessionStarted)
{
Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value);
pendingClient.SteamID = null;
pendingClient.AuthSessionStarted = false;
}
}
}
public abstract void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod);
public abstract void Disconnect(NetworkConnection conn, string msg = null);
@@ -10,50 +10,17 @@ namespace Barotrauma.Networking
{
private bool started;
private ServerSettings serverSettings;
public UInt64 OwnerSteamID
{
get;
private set;
}
private class PendingClient
{
public string Name;
public ConnectionInitialization InitializationStep;
public double UpdateTime;
public double TimeOut;
public int Retries;
public UInt64 SteamID;
public Int32? PasswordSalt;
public bool AuthSessionStarted;
public PendingClient(UInt64 steamId)
{
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
Retries = 0;
SteamID = steamId;
PasswordSalt = null;
UpdateTime = Timing.TotalTime+Timing.Step*3.0;
TimeOut = NetworkConnection.TimeoutThreshold;
AuthSessionStarted = false;
}
public void Heartbeat()
{
TimeOut = NetworkConnection.TimeoutThreshold;
}
}
private List<SteamP2PConnection> connectedClients;
private List<PendingClient> pendingClients;
public SteamP2PServerPeer(UInt64 steamId, ServerSettings settings)
{
serverSettings = settings;
connectedClients = new List<SteamP2PConnection>();
connectedClients = new List<NetworkConnection>();
pendingClients = new List<PendingClient>();
ownerKey = null;
@@ -114,10 +81,11 @@ namespace Barotrauma.Networking
//backwards for loop so we can remove elements while iterating
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
connectedClients[i].Decay(deltaTime);
if (connectedClients[i].Timeout < 0.0)
SteamP2PConnection conn = connectedClients[i] as SteamP2PConnection;
conn.Decay(deltaTime);
if (conn.Timeout < 0.0)
{
Disconnect(connectedClients[i], "Timed out");
Disconnect(conn, "Timed out");
}
}
@@ -172,7 +140,7 @@ namespace Barotrauma.Networking
if (senderSteamId != OwnerSteamID) //sender is remote, handle disconnects and heartbeats
{
PendingClient pendingClient = pendingClients.Find(c => c.SteamID == senderSteamId);
SteamP2PConnection connectedClient = connectedClients.Find(c => c.SteamID == senderSteamId);
SteamP2PConnection connectedClient = connectedClients.Find(c => c.SteamID == senderSteamId) as SteamP2PConnection;
pendingClient?.Heartbeat();
connectedClient?.Heartbeat();
@@ -210,6 +178,7 @@ namespace Barotrauma.Networking
}
else if (isConnectionInitializationStep)
{
if (pendingClient != null)
{
ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null));
@@ -219,7 +188,7 @@ namespace Barotrauma.Networking
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClients.Add(new PendingClient(senderSteamId));
pendingClients.Add(new PendingClient(new SteamP2PConnection("PENDING", senderSteamId)) { SteamID = senderSteamId });
}
}
}
@@ -252,7 +221,7 @@ namespace Barotrauma.Networking
string ownerName = inc.ReadString();
OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID)
{
Status = NetworkConnectionStatus.Connected
Language = GameMain.Config.Language
};
OnInitializationComplete?.Invoke(OwnerConnection);
@@ -273,246 +242,6 @@ namespace Barotrauma.Networking
}
}
private void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
{
if (!started) { return; }
pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
//DebugConsole.NewMessage(initializationStep+" "+pendingClient.InitializationStep);
if (pendingClient.InitializationStep != initializationStep) return;
pendingClient.UpdateTime = Timing.TotalTime+Timing.Step;
switch (initializationStep)
{
case ConnectionInitialization.SteamTicketAndVersion:
string name = Client.SanitizeName(inc.ReadString());
UInt64 steamId = inc.ReadUInt64();
UInt16 ticketLength = inc.ReadUInt16();
inc.BitPosition += ticketLength * 8; //skip ticket, owner handles steam authentication
if (!Client.IsValidName(name, serverSettings))
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
return;
}
string version = inc.ReadString();
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
if (!isCompatibleVersion)
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
return;
}
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
if (nameTaken != null)
{
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
return;
}
int contentPackageCount = (int)inc.ReadVariableUInt32();
List<ClientContentPackage> clientContentPackages = new List<ClientContentPackage>();
for (int i = 0; i < contentPackageCount; i++)
{
string packageName = inc.ReadString();
string packageHash = inc.ReadString();
clientContentPackages.Add(new ClientContentPackage(packageName, packageHash));
}
//check if the client is missing any of our packages
List<ContentPackage> missingPackages = new List<ContentPackage>();
foreach (ContentPackage serverContentPackage in GameMain.SelectedPackages)
{
if (!serverContentPackage.HasMultiplayerIncompatibleContent) continue;
bool packageFound = clientContentPackages.Any(cp => cp.Name == serverContentPackage.Name && cp.Hash == serverContentPackage.MD5hash.Hash);
if (!packageFound) { missingPackages.Add(serverContentPackage); }
}
//check if the client is using packages we don't have
List<ClientContentPackage> redundantPackages = new List<ClientContentPackage>();
foreach (ClientContentPackage clientContentPackage in clientContentPackages)
{
bool packageFound = GameMain.SelectedPackages.Any(cp => cp.Name == clientContentPackage.Name && cp.MD5hash.Hash == clientContentPackage.Hash);
if (!packageFound) { redundantPackages.Add(clientContentPackage); }
}
if (missingPackages.Count == 1)
{
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
$"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
return;
}
else if (missingPackages.Count > 1)
{
List<string> packageStrs = new List<string>();
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
$"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return;
}
if (redundantPackages.Count == 1)
{
RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr(redundantPackages[0])}");
GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using an incompatible content package " + GetPackageStr(redundantPackages[0]) + ")", ServerLog.MessageType.Error);
return;
}
if (redundantPackages.Count > 1)
{
List<string> packageStrs = new List<string>();
redundantPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return;
}
if (!pendingClient.AuthSessionStarted)
{
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
pendingClient.Name = name;
pendingClient.AuthSessionStarted = true;
}
break;
case ConnectionInitialization.Password:
int pwLength = inc.ReadByte();
byte[] incPassword = inc.ReadBytes(pwLength);
if (pendingClient.PasswordSalt == null)
{
DebugConsole.ThrowError("Received password message from client without salt");
return;
}
if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
{
pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
}
else
{
pendingClient.Retries++;
if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
{
string banMsg = "Failed to enter correct password too many times";
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID, banMsg, null);
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
return;
}
}
pendingClient.UpdateTime = Timing.TotalTime;
break;
case ConnectionInitialization.ContentPackageOrder:
pendingClient.InitializationStep = ConnectionInitialization.Success;
pendingClient.UpdateTime = Timing.TotalTime;
break;
}
}
private void UpdatePendingClient(PendingClient pendingClient)
{
if (!started) { return; }
if (serverSettings.BanList.IsBanned(pendingClient.SteamID, out string banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
return;
}
//DebugConsole.NewMessage("pending client status: " + pendingClient.InitializationStep);
if (connectedClients.Count >= serverSettings.MaxPlayers - 1)
{
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
}
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
{
SteamP2PConnection newConnection = new SteamP2PConnection(pendingClient.Name, pendingClient.SteamID)
{
Status = NetworkConnectionStatus.Connected
};
connectedClients.Add(newConnection);
pendingClients.Remove(pendingClient);
OnInitializationComplete?.Invoke(newConnection);
}
pendingClient.TimeOut -= Timing.Step;
if (pendingClient.TimeOut < 0.0)
{
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
}
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
pendingClient.UpdateTime = Timing.TotalTime + 1.0;
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(pendingClient.SteamID);
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep |
PacketHeader.IsServerMessage));
outMsg.Write((byte)pendingClient.InitializationStep);
switch (pendingClient.InitializationStep)
{
case ConnectionInitialization.ContentPackageOrder:
var mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count);
for (int i = 0; i < mpContentPackages.Count; i++)
{
outMsg.Write(mpContentPackages[i].MD5hash.Hash);
}
break;
case ConnectionInitialization.Password:
outMsg.Write(pendingClient.PasswordSalt == null); outMsg.WritePadBits();
if (pendingClient.PasswordSalt == null)
{
pendingClient.PasswordSalt = Lidgren.Network.CryptoRandom.Instance.Next();
outMsg.Write(pendingClient.PasswordSalt.Value);
}
else
{
outMsg.Write(pendingClient.Retries);
}
break;
}
byte[] msgToSend = (byte[])outMsg.Buffer.Clone();
Array.Resize(ref msgToSend, outMsg.LengthBytes);
ChildServerRelay.Write(msgToSend);
}
private void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
{
if (!started) { return; }
if (pendingClients.Contains(pendingClient))
{
SendDisconnectMessage(pendingClient.SteamID, reason + "/" + msg);
pendingClients.Remove(pendingClient);
if (pendingClient.AuthSessionStarted)
{
Steam.SteamManager.StopAuthSession(pendingClient.SteamID);
pendingClient.SteamID = 0;
pendingClient.AuthSessionStarted = false;
}
}
}
public override void InitializeSteamServerCallbacks()
{
throw new InvalidOperationException("Called InitializeSteamServerCallbacks on SteamP2PServerPeer!");
@@ -582,5 +311,25 @@ namespace Barotrauma.Networking
{
Disconnect(conn, msg, true);
}
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
{
IWriteMessage msgToSend = new WriteOnlyMessage();
msgToSend.Write(conn.SteamID);
msgToSend.Write((byte)deliveryMethod);
msgToSend.Write(msg.Buffer, 0, msg.LengthBytes);
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
ChildServerRelay.Write(bufToSend);
}
protected override void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket)
{
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
pendingClient.Connection.Name = name;
pendingClient.Name = name;
pendingClient.AuthSessionStarted = true;
}
}
}
@@ -1,5 +1,4 @@
using Barotrauma.Items.Components;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -9,12 +8,27 @@ namespace Barotrauma.Networking
{
partial class RespawnManager : Entity, IServerSerializable
{
private List<Client> GetClientsToRespawn()
private DateTime despawnTime;
private IEnumerable<Client> GetClientsToRespawn()
{
return networkMember.ConnectedClients.FindAll(c =>
c.InGame &&
(!c.SpectateOnly || (!GameMain.Server.ServerSettings.AllowSpectating && GameMain.Server.OwnerConnection != c.Connection)) &&
(c.Character == null || c.Character.IsDead));
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
foreach (Client c in networkMember.ConnectedClients)
{
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; }
//don't allow respawning if the client has previously disconnected and their corpse is still present on the server
var matchingData = campaign?.GetClientCharacterData(c);
if (matchingData != null && matchingData.HasSpawned &&
Character.CharacterList.Any(c => c.Info == matchingData.CharacterInfo && c.CauseOfDeath?.Type == CauseOfDeathType.Disconnected))
{
continue;
}
yield return c;
}
}
private List<CharacterInfo> GetBotsToRespawn()
@@ -56,7 +70,7 @@ namespace Barotrauma.Networking
private bool RespawnPending()
{
int characterToRespawnCount = GetClientsToRespawn().Count;
int characterToRespawnCount = GetClientsToRespawn().Count();
int totalCharacterCount = GameMain.Server.ConnectedClients.Count;
return (float)characterToRespawnCount >= Math.Max((float)totalCharacterCount * GameMain.Server.ServerSettings.MinRespawnRatio, 1.0f);
}
@@ -82,15 +96,9 @@ namespace Barotrauma.Networking
if (RespawnShuttle == null) { return; }
RespawnShuttle.Velocity = Vector2.Zero;
if (shuttleSteering != null)
{
shuttleSteering.AutoPilot = false;
shuttleSteering.MaintainPos = false;
}
}
partial void DispatchShuttle()
private void DispatchShuttle()
{
if (RespawnShuttle != null)
{
@@ -118,13 +126,8 @@ namespace Barotrauma.Networking
{
RespawnShuttle.SetPosition(spawnPos);
RespawnShuttle.Velocity = Vector2.Zero;
if (shuttleSteering != null)
{
shuttleSteering.AutoPilot = true;
shuttleSteering.MaintainPos = true;
shuttleSteering.PosToMaintain = RespawnShuttle.WorldPosition;
shuttleSteering.UnsentChanges = true;
}
RespawnShuttle.NeutralizeBallast();
RespawnShuttle.EnableMaintainPosition();
}
}
else
@@ -219,12 +222,20 @@ namespace Barotrauma.Networking
{
var respawnSub = RespawnShuttle ?? Submarine.MainSub;
var clients = GetClientsToRespawn();
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
var clients = GetClientsToRespawn().ToList();
foreach (Client c in clients)
{
//get rid of the existing character
c.Character?.DespawnNow();
var matchingData = campaign?.GetClientCharacterData(c);
if (matchingData != null && !matchingData.HasSpawned)
{
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 = Character.TeamType.Team1;
@@ -238,7 +249,10 @@ namespace Barotrauma.Networking
GameMain.Server.AssignJobs(clients);
foreach (Client c in clients)
{
c.CharacterInfo.Job = new Job(c.AssignedJob.First, c.AssignedJob.Second);
if (campaign?.GetClientCharacterData(c) == null || c.CharacterInfo.Job == null)
{
c.CharacterInfo.Job = new Job(c.AssignedJob.First, c.AssignedJob.Second);
}
}
//the spawnpoints where the characters will spawn
@@ -314,19 +328,37 @@ namespace Barotrauma.Networking
}
}
//give the character the items they would've gotten if they had spawned in the main sub
character.GiveJobItems(mainSubSpawnPoints[i]);
var characterData = campaign?.GetClientCharacterData(clients[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]);
if (campaign != null)
{
characterData = campaign.SetClientCharacterData(clients[i]);
characterData.HasSpawned = true;
}
}
else
{
characterData.SpawnInventoryItems(character.Info, character.Inventory);
characterData.ApplyHealthData(character.Info, character);
character.GiveIdCardTags(mainSubSpawnPoints[i]);
characterData.HasSpawned = true;
}
//add the ID card tags they should've gotten when spawning in the shuttle
foreach (Item item in character.Inventory.Items)
{
if (item == null || item.Prefab.Identifier != "idcard") continue;
if (item == null || item.Prefab.Identifier != "idcard") { continue; }
foreach (string s in shuttleSpawnPoints[i].IdCardTags)
{
item.AddTag(s);
}
if (!string.IsNullOrWhiteSpace(shuttleSpawnPoints[i].IdCardDesc))
{
item.Description = shuttleSpawnPoints[i].IdCardDesc;
}
}
}
}
@@ -8,8 +8,6 @@ namespace Barotrauma.Steam
private static void InitializeProjectSpecific() { isInitialized = true; }
private static void UpdateProjectSpecific(float deltaTime) { }
public static bool CreateServer(Networking.GameServer server, bool isPublic)
{
isInitialized = true;
@@ -17,8 +15,8 @@ namespace Barotrauma.Steam
Steamworks.SteamServerInit options = new Steamworks.SteamServerInit("Barotrauma", "Barotrauma")
{
GamePort = (ushort)server.Port,
QueryPort = (ushort)server.QueryPort,
Secure = false
QueryPort = isPublic ? (ushort)server.QueryPort : (ushort)0,
Mode = isPublic ? Steamworks.InitServerMode.Authentication : Steamworks.InitServerMode.NoAuthentication
};
//options.QueryShareGamePort();
@@ -46,7 +44,7 @@ namespace Barotrauma.Steam
return false;
}
var contentPackages = GameMain.Config.SelectedContentPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
var contentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
// These server state variables may be changed at any time. Note that there is no longer a mechanism
// to send the player count. The player count is maintained by steam and you should use the player
@@ -55,12 +53,13 @@ namespace Barotrauma.Steam
Steamworks.SteamServer.MaxPlayers = server.ServerSettings.MaxPlayers;
Steamworks.SteamServer.Passworded = server.ServerSettings.HasPassword;
Steamworks.SteamServer.MapName = GameMain.NetLobbyScreen?.SelectedSub?.DisplayName ?? "";
Steamworks.SteamServer.SetKey("haspassword", server.ServerSettings.HasPassword.ToString());
Steamworks.SteamServer.SetKey("message", GameMain.Server.ServerSettings.ServerMessageText);
Steamworks.SteamServer.SetKey("version", GameMain.Version.ToString());
Steamworks.SteamServer.SetKey("playercount", GameMain.Server.ConnectedClients.Count.ToString());
Steamworks.SteamServer.SetKey("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
Steamworks.SteamServer.SetKey("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.MD5hash.Hash)));
Steamworks.SteamServer.SetKey("contentpackageurl", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopUrl ?? "")));
Steamworks.SteamServer.SetKey("contentpackageid", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopId)));
Steamworks.SteamServer.SetKey("usingwhitelist", (server.ServerSettings.Whitelist != null && server.ServerSettings.Whitelist.Enabled).ToString());
Steamworks.SteamServer.SetKey("modeselectionmode", server.ServerSettings.ModeSelectionMode.ToString());
Steamworks.SteamServer.SetKey("subselectionmode", server.ServerSettings.SubSelectionMode.ToString());
@@ -70,6 +69,7 @@ namespace Barotrauma.Steam
Steamworks.SteamServer.SetKey("traitors", server.ServerSettings.TraitorsEnabled.ToString());
Steamworks.SteamServer.SetKey("gamestarted", server.GameStarted.ToString());
Steamworks.SteamServer.SetKey("gamemode", server.ServerSettings.GameModeIdentifier);
Steamworks.SteamServer.SetKey("playstyle", server.ServerSettings.PlayStyle.ToString());
Steamworks.SteamServer.DedicatedServer = true;
@@ -82,20 +82,18 @@ namespace Barotrauma
switch (voteType)
{
case VoteType.Sub:
string subName = inc.ReadString();
SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
int equalityCheckVal = inc.ReadInt32();
SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == equalityCheckVal);
sender.SetVote(voteType, sub);
break;
case VoteType.Mode:
string modeIdentifier = inc.ReadString();
GameModePreset mode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier);
if (!mode.Votable) break;
if (!mode.Votable) { break; }
sender.SetVote(voteType, mode);
break;
case VoteType.EndRound:
if (!sender.HasSpawned) return;
if (!sender.HasSpawned) { return; }
sender.SetVote(voteType, inc.ReadBoolean());
GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound));