Merge branch 'dev' of https://github.com/Regalis11/Barotrauma into unstable
This commit is contained in:
@@ -1,93 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#nullable enable
|
||||
using System;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Barotrauma.Steam;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class BannedPlayer
|
||||
{
|
||||
private static UInt16 LastIdentifier = 0;
|
||||
private static UInt32 LastIdentifier = 0;
|
||||
|
||||
public BannedPlayer(string name, string endPoint, string reason, DateTime? expirationTime)
|
||||
public bool Expired => ExpirationTime is { } expirationTime && DateTime.Now > expirationTime;
|
||||
|
||||
public BannedPlayer(
|
||||
string name, Either<Address, AccountId> addressOrAccountId, string reason, DateTime? expirationTime)
|
||||
{
|
||||
this.Name = name;
|
||||
this.EndPoint = endPoint;
|
||||
ParseEndPointAsSteamId();
|
||||
this.AddressOrAccountId = addressOrAccountId;
|
||||
this.Reason = reason;
|
||||
this.ExpirationTime = expirationTime;
|
||||
this.UniqueIdentifier = LastIdentifier; LastIdentifier++;
|
||||
|
||||
this.IsRangeBan = EndPoint.IndexOf(".x") > -1;
|
||||
}
|
||||
|
||||
public BannedPlayer(string name, ulong steamID, string reason, DateTime? expirationTime)
|
||||
{
|
||||
this.Name = name;
|
||||
this.SteamID = steamID;
|
||||
this.Reason = reason;
|
||||
this.ExpirationTime = expirationTime;
|
||||
this.UniqueIdentifier = LastIdentifier; LastIdentifier++;
|
||||
|
||||
this.IsRangeBan = false;
|
||||
|
||||
this.EndPoint = "";
|
||||
}
|
||||
|
||||
public bool CompareTo(string endpointCompare)
|
||||
{
|
||||
if (string.IsNullOrEmpty(EndPoint) || string.IsNullOrEmpty(endpointCompare)) { return false; }
|
||||
if (!IsRangeBan)
|
||||
{
|
||||
return endpointCompare == EndPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
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(EndPoint) || ipCompare == null) { return false; }
|
||||
if (ipCompare.IsIPv4MappedToIPv6 && CompareTo(ipCompare.MapToIPv4NoThrow().ToString()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return CompareTo(ipCompare.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
partial class BanList
|
||||
{
|
||||
const string SavePath = "Data/bannedplayers.txt";
|
||||
private const string SavePath = "Data/bannedplayers.xml";
|
||||
private const string LegacySavePath = "Data/bannedplayers.txt";
|
||||
|
||||
partial void InitProjectSpecific()
|
||||
{
|
||||
if (!File.Exists(SavePath)) { return; }
|
||||
if (!File.Exists(SavePath))
|
||||
{
|
||||
LoadLegacyBanList();
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadBanList();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadLegacyBanList()
|
||||
{
|
||||
if (!File.Exists(LegacySavePath)) { return; }
|
||||
|
||||
string[] lines;
|
||||
try
|
||||
{
|
||||
lines = File.ReadAllLines(SavePath);
|
||||
lines = File.ReadAllLines(LegacySavePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to open the list of banned players in " + SavePath, e);
|
||||
DebugConsole.ThrowError($"Failed to open the list of banned players in {LegacySavePath}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
string[] separatedLine = line.Split(',');
|
||||
if (separatedLine.Length < 2) continue;
|
||||
if (separatedLine.Length < 2) { continue; }
|
||||
|
||||
string name = separatedLine[0];
|
||||
string identifier = separatedLine[1];
|
||||
string endpointStr = separatedLine[1];
|
||||
|
||||
DateTime? expirationTime = null;
|
||||
if (separatedLine.Length > 2 && !string.IsNullOrEmpty(separatedLine[2]))
|
||||
@@ -96,99 +71,105 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
expirationTime = parsedTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = $"Failed to parse the ban duration of \"{name}\" ({separatedLine[2]}) from the legacy ban list file (text file which has now been changed to XML). Considering the ban permanent.";
|
||||
DebugConsole.ThrowError(error);
|
||||
GameServer.AddPendingMessageToOwner(error, ChatMessageType.Error);
|
||||
}
|
||||
}
|
||||
string reason = separatedLine.Length > 3 ? string.Join(",", separatedLine.Skip(3)) : "";
|
||||
|
||||
if (expirationTime.HasValue && DateTime.Now > expirationTime.Value) continue;
|
||||
if (expirationTime.HasValue && DateTime.Now > expirationTime.Value) { continue; }
|
||||
|
||||
if (identifier.Contains(".") || identifier.Contains(":"))
|
||||
if (AccountId.Parse(endpointStr).TryUnwrap(out var accountId))
|
||||
{
|
||||
//identifier is an ip
|
||||
bannedPlayers.Add(new BannedPlayer(name, identifier, reason, expirationTime));
|
||||
bannedPlayers.Add(new BannedPlayer(name, accountId, reason, expirationTime));
|
||||
}
|
||||
else
|
||||
else if (Address.Parse(endpointStr).TryUnwrap(out var address))
|
||||
{
|
||||
//identifier should be a steam id
|
||||
if (ulong.TryParse(identifier, out ulong steamID))
|
||||
{
|
||||
bannedPlayers.Add(new BannedPlayer(name, steamID, reason, expirationTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Error in banlist: \"" + identifier + "\" is not a valid IP or a Steam ID");
|
||||
}
|
||||
bannedPlayers.Add(new BannedPlayer(name, address, reason, expirationTime));
|
||||
}
|
||||
}
|
||||
|
||||
Save();
|
||||
File.Delete(LegacySavePath);
|
||||
}
|
||||
|
||||
public bool IsBanned(IPAddress IP, ulong steamID, ulong ownerSteamID, out string reason)
|
||||
private void LoadBanList()
|
||||
{
|
||||
reason = string.Empty;
|
||||
if (IPAddress.IsLoopback(IP)) { return false; }
|
||||
var bannedPlayer = bannedPlayers.Find(bp =>
|
||||
bp.CompareTo(IP) ||
|
||||
(steamID > 0 && (bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID)) ||
|
||||
(ownerSteamID > 0 && (bp.SteamID == ownerSteamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == ownerSteamID)));
|
||||
reason = bannedPlayer?.Reason;
|
||||
return bannedPlayer != null;
|
||||
}
|
||||
XDocument? doc = XMLExtensions.TryLoadXml(SavePath);
|
||||
|
||||
if (doc?.Root is null) { return; }
|
||||
|
||||
public bool IsBanned(IPAddress IP, out string reason)
|
||||
{
|
||||
reason = string.Empty;
|
||||
if (IPAddress.IsLoopback(IP)) { return false; }
|
||||
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
|
||||
var bannedPlayer = bannedPlayers.Find(bp => bp.CompareTo(IP));
|
||||
reason = bannedPlayer?.Reason;
|
||||
return bannedPlayer != null;
|
||||
}
|
||||
|
||||
public bool IsBanned(ulong steamID, out string reason)
|
||||
{
|
||||
reason = string.Empty;
|
||||
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
|
||||
var bannedPlayer = bannedPlayers.Find(bp =>
|
||||
steamID > 0 &&
|
||||
(bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID));
|
||||
reason = bannedPlayer?.Reason;
|
||||
return bannedPlayer != null;
|
||||
}
|
||||
|
||||
public void BanPlayer(string name, IPAddress ip, string reason, TimeSpan? duration)
|
||||
{
|
||||
string ipStr = ip.IsIPv4MappedToIPv6 ? ip.MapToIPv4NoThrow().ToString() : ip.ToString();
|
||||
BanPlayer(name, ipStr, 0, reason, duration);
|
||||
}
|
||||
|
||||
public void BanPlayer(string name, string endPoint, string reason, TimeSpan? duration)
|
||||
{
|
||||
BanPlayer(name, endPoint, 0, reason, duration);
|
||||
}
|
||||
|
||||
public void BanPlayer(string name, ulong steamID, string reason, TimeSpan? duration)
|
||||
{
|
||||
if (steamID == 0) { return; }
|
||||
BanPlayer(name, "", steamID, reason, duration);
|
||||
}
|
||||
|
||||
private void BanPlayer(string name, string endPoint, ulong steamID, string reason, TimeSpan? duration)
|
||||
{
|
||||
var existingBan = bannedPlayers.Find(bp => bp.EndPoint == endPoint && bp.SteamID == steamID);
|
||||
if (existingBan != null)
|
||||
static Option<BannedPlayer> loadFromElement(XElement element)
|
||||
{
|
||||
if (!duration.HasValue) return;
|
||||
var accountId = AccountId.Parse(element.GetAttributeString("accountid", ""));
|
||||
var address = Address.Parse(element.GetAttributeString("address", ""));
|
||||
|
||||
DebugConsole.Log("Set \"" + name + "\"'s ban duration to " + duration.Value);
|
||||
existingBan.ExpirationTime = DateTime.Now + duration.Value;
|
||||
Save();
|
||||
return;
|
||||
var name = element.GetAttributeString("name", "")!;
|
||||
var reason = element.GetAttributeString("reason", "")!;
|
||||
DateTime? expirationTime = DateTime.FromBinary(unchecked((long)element.GetAttributeUInt64("expirationtime", 0)));
|
||||
|
||||
if (expirationTime < DateTime.Now) { expirationTime = null; }
|
||||
|
||||
if (accountId.IsNone() && address.IsNone()) { return Option<BannedPlayer>.None(); }
|
||||
|
||||
Either<Address, AccountId> addressOrAccountId = accountId.TryUnwrap(out var accId)
|
||||
? (Either<Address, AccountId>)accId
|
||||
: address.TryUnwrap(out var addr)
|
||||
? addr
|
||||
: throw new InvalidCastException();
|
||||
|
||||
return Option<BannedPlayer>.Some(new BannedPlayer(name, addressOrAccountId, reason, expirationTime));
|
||||
}
|
||||
|
||||
bannedPlayers.AddRange(doc.Root.Elements().Select(loadFromElement)
|
||||
.OfType<Some<BannedPlayer>>().Select(o => o.Value));
|
||||
}
|
||||
|
||||
private void RemoveExpired()
|
||||
{
|
||||
bannedPlayers.RemoveAll(bp => bp.Expired);
|
||||
}
|
||||
|
||||
public bool IsBanned(Endpoint endpoint, out string reason)
|
||||
=> IsBanned(endpoint.Address, out reason);
|
||||
|
||||
public bool IsBanned(Address address, out string reason)
|
||||
{
|
||||
RemoveExpired();
|
||||
if (address.IsLocalHost)
|
||||
{
|
||||
reason = string.Empty;
|
||||
return false;
|
||||
}
|
||||
var bannedPlayer = bannedPlayers.Find(bp => bp.AddressOrAccountId.TryGet(out Address adr) && address.Equals(adr));
|
||||
reason = bannedPlayer?.Reason ?? string.Empty;
|
||||
return bannedPlayer != null;
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.Assert(!name.Contains(','));
|
||||
public bool IsBanned(AccountId accountId, out string reason)
|
||||
{
|
||||
RemoveExpired();
|
||||
var bannedPlayer = bannedPlayers.Find(bp => bp.AddressOrAccountId.TryGet(out AccountId id) && accountId.Equals(id));
|
||||
reason = bannedPlayer?.Reason ?? string.Empty;
|
||||
return bannedPlayer != null;
|
||||
}
|
||||
|
||||
public void BanPlayer(string name, Endpoint endpoint, string reason, TimeSpan? duration)
|
||||
=> BanPlayer(name, endpoint.Address, reason, duration);
|
||||
|
||||
public void BanPlayer(string name, Either<Address, AccountId> addressOrAccountId, string reason, TimeSpan? duration)
|
||||
{
|
||||
if (addressOrAccountId.TryGet(out Address address) && address.IsLocalHost) { return; }
|
||||
|
||||
var existingBan = bannedPlayers.Find(bp => bp.AddressOrAccountId == addressOrAccountId);
|
||||
if (existingBan != null) { bannedPlayers.Remove(existingBan); }
|
||||
|
||||
string logMsg = "Banned " + name;
|
||||
if (!string.IsNullOrEmpty(reason)) logMsg += ", reason: " + reason;
|
||||
if (duration.HasValue) logMsg += ", duration: " + duration.Value.ToString();
|
||||
if (!string.IsNullOrEmpty(reason)) { logMsg += ", reason: " + reason; }
|
||||
if (duration.HasValue) { logMsg += ", duration: " + duration.Value.ToString(); }
|
||||
|
||||
DebugConsole.Log(logMsg);
|
||||
|
||||
@@ -198,46 +179,19 @@ namespace Barotrauma.Networking
|
||||
expirationTime = DateTime.Now + duration.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(endPoint))
|
||||
{
|
||||
bannedPlayers.Add(new BannedPlayer(name, endPoint, reason, expirationTime));
|
||||
}
|
||||
else if (steamID > 0)
|
||||
{
|
||||
bannedPlayers.Add(new BannedPlayer(name, steamID, reason, expirationTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to ban a client (no valid IP or Steam ID given)");
|
||||
return;
|
||||
}
|
||||
|
||||
bannedPlayers.Add(new BannedPlayer(name, addressOrAccountId, reason, expirationTime));
|
||||
Save();
|
||||
}
|
||||
|
||||
public void UnbanPlayer(string name)
|
||||
public void UnbanPlayer(Endpoint endpoint)
|
||||
=> UnbanPlayer(endpoint.Address);
|
||||
|
||||
public void UnbanPlayer(Either<Address, AccountId> addressOrAccountId)
|
||||
{
|
||||
name = name.ToLower();
|
||||
var player = bannedPlayers.Find(bp => bp.Name.ToLower() == name);
|
||||
var player = bannedPlayers.Find(bp => bp.AddressOrAccountId == addressOrAccountId);
|
||||
if (player == null)
|
||||
{
|
||||
DebugConsole.Log("Could not unban player \"" + name + "\". Matching player not found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveBan(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnbanEndPoint(string endPoint)
|
||||
{
|
||||
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 endpoint \"" + endPoint + "\". Matching player not found.");
|
||||
DebugConsole.Log("Could not unban endpoint \"" + addressOrAccountId + "\". Matching player not found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -255,49 +209,38 @@ namespace Barotrauma.Networking
|
||||
Save();
|
||||
}
|
||||
|
||||
private void RangeBan(BannedPlayer banned)
|
||||
{
|
||||
banned.EndPoint = ToRange(banned.EndPoint);
|
||||
|
||||
BannedPlayer bp;
|
||||
while ((bp = bannedPlayers.Find(x => banned.CompareTo(x.EndPoint))) != null)
|
||||
{
|
||||
//remove all specific bans that are now covered by the rangeban
|
||||
bannedPlayers.Remove(bp);
|
||||
}
|
||||
|
||||
bannedPlayers.Add(banned);
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
GameServer.Log("Saving banlist", ServerLog.MessageType.ServerMessage);
|
||||
|
||||
GameMain.Server?.ServerSettings?.UpdateFlag(ServerSettings.NetFlags.Properties);
|
||||
|
||||
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
|
||||
RemoveExpired();
|
||||
|
||||
List<string> lines = new List<string>();
|
||||
foreach (BannedPlayer banned in bannedPlayers)
|
||||
static XElement saveToElement(BannedPlayer bannedPlayer)
|
||||
{
|
||||
string line = banned.Name;
|
||||
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;
|
||||
XElement retVal = new XElement("ban");
|
||||
retVal.SetAttributeValue("name", bannedPlayer.Name);
|
||||
retVal.SetAttributeValue("reason", bannedPlayer.Reason);
|
||||
if (bannedPlayer.AddressOrAccountId.TryGet(out AccountId accountId))
|
||||
{
|
||||
retVal.SetAttributeValue("accountid", accountId.StringRepresentation);
|
||||
}
|
||||
else if (bannedPlayer.AddressOrAccountId.TryGet(out Address address))
|
||||
{
|
||||
retVal.SetAttributeValue("address", address.StringRepresentation);
|
||||
}
|
||||
if (bannedPlayer.ExpirationTime is { } expirationTime)
|
||||
{
|
||||
retVal.SetAttributeValue("expirationtime", unchecked((ulong)expirationTime.ToBinary()));
|
||||
}
|
||||
|
||||
lines.Add(line);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(SavePath, lines);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Saving the list of banned players to " + SavePath + " failed", e);
|
||||
}
|
||||
XDocument doc = new XDocument(new XElement("bannedplayers"));
|
||||
bannedPlayers.Select(saveToElement).ForEach(doc.Root!.Add);
|
||||
doc.SaveSafe(SavePath);
|
||||
}
|
||||
|
||||
public void ServerAdminWrite(IWriteMessage outMsg, Client c)
|
||||
@@ -309,12 +252,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (!c.HasPermission(ClientPermissions.Ban))
|
||||
{
|
||||
outMsg.Write(false); outMsg.WritePadBits();
|
||||
outMsg.WriteBoolean(false); outMsg.WritePadBits();
|
||||
return;
|
||||
}
|
||||
|
||||
outMsg.Write(true);
|
||||
outMsg.Write(c.Connection == GameMain.Server.OwnerConnection);
|
||||
outMsg.WriteBoolean(true);
|
||||
outMsg.WriteBoolean(c.Connection == GameMain.Server.OwnerConnection);
|
||||
|
||||
outMsg.WritePadBits();
|
||||
outMsg.WriteVariableUInt32((UInt32)bannedPlayers.Count);
|
||||
@@ -322,27 +265,33 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
BannedPlayer bannedPlayer = bannedPlayers[i];
|
||||
|
||||
outMsg.Write(bannedPlayer.Name);
|
||||
outMsg.Write(bannedPlayer.UniqueIdentifier);
|
||||
outMsg.Write(bannedPlayer.IsRangeBan);
|
||||
outMsg.Write(bannedPlayer.ExpirationTime != null);
|
||||
outMsg.WriteString(bannedPlayer.Name);
|
||||
outMsg.WriteUInt32(bannedPlayer.UniqueIdentifier);
|
||||
outMsg.WriteBoolean(bannedPlayer.ExpirationTime != null);
|
||||
outMsg.WritePadBits();
|
||||
if (bannedPlayer.ExpirationTime != null)
|
||||
{
|
||||
double hoursFromNow = (bannedPlayer.ExpirationTime.Value - DateTime.Now).TotalHours;
|
||||
outMsg.Write(hoursFromNow);
|
||||
outMsg.WriteDouble(hoursFromNow);
|
||||
}
|
||||
|
||||
outMsg.Write(bannedPlayer.Reason ?? "");
|
||||
outMsg.WriteString(bannedPlayer.Reason ?? "");
|
||||
|
||||
if (c.Connection == GameMain.Server.OwnerConnection)
|
||||
{
|
||||
outMsg.Write(bannedPlayer.EndPoint);
|
||||
outMsg.Write(bannedPlayer.SteamID);
|
||||
if (bannedPlayer.AddressOrAccountId.TryGet(out Address endpoint))
|
||||
{
|
||||
outMsg.WriteBoolean(true); outMsg.WritePadBits();
|
||||
outMsg.WriteString(endpoint.StringRepresentation);
|
||||
}
|
||||
else
|
||||
{
|
||||
outMsg.WriteBoolean(false); outMsg.WritePadBits();
|
||||
outMsg.WriteString(((SteamId)bannedPlayer.AddressOrAccountId).StringRepresentation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Error while writing banlist. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
|
||||
@@ -355,38 +304,25 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!c.HasPermission(ClientPermissions.Ban))
|
||||
{
|
||||
UInt16 removeCount = incMsg.ReadUInt16();
|
||||
incMsg.BitPosition += removeCount * 4 * 8;
|
||||
UInt16 rangeBanCount = incMsg.ReadUInt16();
|
||||
incMsg.BitPosition += rangeBanCount * 4 * 8;
|
||||
UInt32 removeCount = incMsg.ReadVariableUInt32();
|
||||
incMsg.BitPosition += (int)removeCount * 4 * 8;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt16 removeCount = incMsg.ReadUInt16();
|
||||
UInt32 removeCount = incMsg.ReadVariableUInt32();
|
||||
for (int i = 0; i < removeCount; i++)
|
||||
{
|
||||
UInt16 id = incMsg.ReadUInt16();
|
||||
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
|
||||
UInt32 id = incMsg.ReadUInt32();
|
||||
BannedPlayer? bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
|
||||
if (bannedPlayer != null)
|
||||
{
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.EndPoint + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.AddressOrAccountId + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
RemoveBan(bannedPlayer);
|
||||
}
|
||||
}
|
||||
Int16 rangeBanCount = incMsg.ReadInt16();
|
||||
for (int i = 0; i < rangeBanCount; i++)
|
||||
{
|
||||
UInt16 id = incMsg.ReadUInt16();
|
||||
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
|
||||
if (bannedPlayer != null)
|
||||
{
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " rangebanned " + bannedPlayer.Name + " (" + bannedPlayer.EndPoint + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
RangeBan(bannedPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
return removeCount > 0 || rangeBanCount > 0;
|
||||
return removeCount > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,24 +212,24 @@ namespace Barotrauma.Networking
|
||||
|
||||
public virtual void ServerWrite(IWriteMessage msg, Client c)
|
||||
{
|
||||
msg.Write((byte)ServerNetObject.CHAT_MESSAGE);
|
||||
msg.Write(NetStateID);
|
||||
msg.WriteByte((byte)ServerNetObject.CHAT_MESSAGE);
|
||||
msg.WriteUInt16(NetStateID);
|
||||
msg.WriteRangedInteger((int)Type, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
|
||||
msg.Write((byte)ChangeType);
|
||||
msg.Write(Text);
|
||||
msg.WriteByte((byte)ChangeType);
|
||||
msg.WriteString(Text);
|
||||
|
||||
msg.Write(SenderName);
|
||||
msg.Write(SenderClient != null);
|
||||
msg.WriteString(SenderName);
|
||||
msg.WriteBoolean(SenderClient != null);
|
||||
if (SenderClient != null)
|
||||
{
|
||||
msg.Write((SenderClient.SteamID != 0) ? SenderClient.SteamID : SenderClient.ID);
|
||||
msg.WriteString(SenderClient.AccountId.TryUnwrap(out var accountId) ? accountId.StringRepresentation : SenderClient.SessionId.ToString());
|
||||
}
|
||||
msg.Write(Sender != null && c.InGame);
|
||||
msg.WriteBoolean(Sender != null && c.InGame);
|
||||
if (Sender != null && c.InGame)
|
||||
{
|
||||
msg.Write(Sender.ID);
|
||||
msg.WriteUInt16(Sender.ID);
|
||||
}
|
||||
msg.Write(customTextColor != null);
|
||||
msg.WriteBoolean(customTextColor != null);
|
||||
if (customTextColor != null)
|
||||
{
|
||||
msg.WriteColorR8G8B8A8(customTextColor.Value);
|
||||
@@ -237,7 +237,7 @@ namespace Barotrauma.Networking
|
||||
msg.WritePadBits();
|
||||
if (Type == ChatMessageType.ServerMessageBoxInGame)
|
||||
{
|
||||
msg.Write(IconStyle);
|
||||
msg.WriteString(IconStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,16 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
public bool VoiceEnabled = true;
|
||||
|
||||
public UInt16 LastRecvClientListUpdate = 0;
|
||||
public UInt16 LastRecvClientListUpdate
|
||||
= NetIdUtils.GetIdOlderThan(GameMain.Server.LastClientListUpdateID);
|
||||
|
||||
public UInt16 LastSentServerSettingsUpdate = 0;
|
||||
public UInt16 LastRecvServerSettingsUpdate = 0;
|
||||
public UInt16 LastSentServerSettingsUpdate
|
||||
= NetIdUtils.GetIdOlderThan(GameMain.Server.ServerSettings.LastUpdateIdForFlag[ServerSettings.NetFlags.Properties]);
|
||||
public UInt16 LastRecvServerSettingsUpdate
|
||||
= NetIdUtils.GetIdOlderThan(GameMain.Server.ServerSettings.LastUpdateIdForFlag[ServerSettings.NetFlags.Properties]);
|
||||
|
||||
public UInt16 LastRecvLobbyUpdate = 0;
|
||||
public UInt16 LastRecvLobbyUpdate
|
||||
= NetIdUtils.GetIdOlderThan(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
|
||||
public UInt16 LastSentChatMsgID = 0; //last msg this client said
|
||||
public UInt16 LastRecvChatMsgID = 0; //last msg this client knows about
|
||||
@@ -21,7 +25,8 @@ namespace Barotrauma.Networking
|
||||
public UInt16 LastSentEntityEventID = 0;
|
||||
public UInt16 LastRecvEntityEventID = 0;
|
||||
|
||||
public readonly Dictionary<MultiPlayerCampaign.NetFlags, UInt16> LastRecvCampaignUpdate = new Dictionary<MultiPlayerCampaign.NetFlags, ushort>();
|
||||
public readonly Dictionary<MultiPlayerCampaign.NetFlags, UInt16> LastRecvCampaignUpdate
|
||||
= new Dictionary<MultiPlayerCampaign.NetFlags, UInt16>();
|
||||
public UInt16 LastRecvCampaignSave = 0;
|
||||
|
||||
public (UInt16 saveId, float time) LastCampaignSaveSendTime;
|
||||
@@ -57,11 +62,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
public bool ReadyToStart;
|
||||
|
||||
public List<JobVariant> JobPreferences;
|
||||
public List<JobVariant> JobPreferences { get; set; }
|
||||
public JobVariant AssignedJob;
|
||||
|
||||
public float DeleteDisconnectedTimer;
|
||||
|
||||
public DateTime JoinTime;
|
||||
|
||||
private CharacterInfo characterInfo;
|
||||
public CharacterInfo CharacterInfo
|
||||
{
|
||||
@@ -105,15 +112,26 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private List<Client> kickVoters;
|
||||
|
||||
public int KickVoteCount
|
||||
{
|
||||
get { return kickVoters.Count; }
|
||||
}
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
kickVoters = new List<Client>();
|
||||
|
||||
JobPreferences = new List<JobVariant>();
|
||||
|
||||
VoipQueue = new VoipQueue(ID, true, true);
|
||||
VoipQueue = new VoipQueue(SessionId, true, true);
|
||||
GameMain.Server.VoipServer.RegisterQueue(VoipQueue);
|
||||
|
||||
//initialize to infinity, gets set to a proper value when initializing midround syncing
|
||||
MidRoundSyncTimeOut = double.PositiveInfinity;
|
||||
|
||||
JoinTime = DateTime.Now;
|
||||
}
|
||||
|
||||
partial void DisposeProjSpecific()
|
||||
@@ -147,7 +165,22 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) { return false; }
|
||||
|
||||
char[] disallowedChars = new char[] { ';', ',', '<', '>', '/', '\\', '[', ']', '"', '?' };
|
||||
char[] disallowedChars =
|
||||
{
|
||||
//',', //previously disallowed because of the ban list format
|
||||
|
||||
';',
|
||||
'<',
|
||||
'>',
|
||||
|
||||
'/', //disallowed because of server messages using forward slash as a delimiter (TODO: implement escaping)
|
||||
|
||||
'\\',
|
||||
'[',
|
||||
']',
|
||||
'"',
|
||||
'?'
|
||||
};
|
||||
if (name.Any(c => disallowedChars.Contains(c))) { return false; }
|
||||
|
||||
foreach (char character in name)
|
||||
@@ -158,34 +191,88 @@ namespace Barotrauma.Networking
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool EndpointMatches(string endPoint)
|
||||
public bool AddressMatches(Address address)
|
||||
{
|
||||
return Connection.EndpointMatches(endPoint);
|
||||
return Connection.Endpoint.Address.Equals(address);
|
||||
}
|
||||
|
||||
public void AddKickVote(Client voter)
|
||||
{
|
||||
if (voter != null && !kickVoters.Contains(voter)) { kickVoters.Add(voter); }
|
||||
}
|
||||
|
||||
public void RemoveKickVote(Client voter)
|
||||
{
|
||||
kickVoters.Remove(voter);
|
||||
}
|
||||
|
||||
public bool HasKickVoteFrom(Client voter)
|
||||
{
|
||||
return kickVoters.Contains(voter);
|
||||
}
|
||||
|
||||
public bool HasKickVoteFromSessionId(int id)
|
||||
{
|
||||
return kickVoters.Any(k => k.SessionId == id);
|
||||
}
|
||||
|
||||
public static void UpdateKickVotes(IReadOnlyList<Client> connectedClients)
|
||||
{
|
||||
foreach (Client client in connectedClients)
|
||||
{
|
||||
client.kickVoters.RemoveAll(voter => !connectedClients.Contains(voter));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset what this client has voted for and the kick votes given to this client
|
||||
/// </summary>
|
||||
public void ResetVotes(bool resetKickVotes)
|
||||
{
|
||||
for (int i = 0; i < votes.Length; i++)
|
||||
{
|
||||
votes[i] = null;
|
||||
}
|
||||
if (resetKickVotes)
|
||||
{
|
||||
kickVoters.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetPermissions(ClientPermissions permissions, IEnumerable<DebugConsole.Command> permittedConsoleCommands)
|
||||
{
|
||||
this.Permissions = permissions;
|
||||
this.PermittedConsoleCommands.Clear();
|
||||
foreach (var command in permittedConsoleCommands)
|
||||
Permissions = permissions;
|
||||
PermittedConsoleCommands.Clear();
|
||||
PermittedConsoleCommands.UnionWith(permittedConsoleCommands);
|
||||
if (Permissions.HasFlag(ClientPermissions.ManageSettings))
|
||||
{
|
||||
this.PermittedConsoleCommands.Add(command);
|
||||
//ensure the client has the up-to-date server settings
|
||||
GameMain.Server?.ServerSettings?.ForcePropertyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void GivePermission(ClientPermissions permission)
|
||||
{
|
||||
if (!this.Permissions.HasFlag(permission)) this.Permissions |= permission;
|
||||
if (!Permissions.HasFlag(permission))
|
||||
{
|
||||
Permissions |= permission;
|
||||
if (permission.HasFlag(ClientPermissions.ManageSettings))
|
||||
{
|
||||
//ensure the client has the up-to-date server settings
|
||||
GameMain.Server?.ServerSettings?.ForcePropertyUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePermission(ClientPermissions permission)
|
||||
{
|
||||
this.Permissions &= ~permission;
|
||||
Permissions &= ~permission;
|
||||
}
|
||||
|
||||
public bool HasPermission(ClientPermissions permission)
|
||||
{
|
||||
return this.Permissions.HasFlag(permission);
|
||||
return Permissions.HasFlag(permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,23 +32,23 @@ namespace Barotrauma
|
||||
if (GameMain.Server is null) { return; }
|
||||
if (!(extraData is SpawnOrRemove entities)) { throw new Exception($"Malformed {nameof(EntitySpawner)} event: expected {nameof(SpawnOrRemove)}"); }
|
||||
|
||||
message.Write(entities is RemoveEntity);
|
||||
message.WriteBoolean(entities is RemoveEntity);
|
||||
if (entities is RemoveEntity)
|
||||
{
|
||||
message.Write(entities.ID);
|
||||
message.WriteUInt16(entities.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (entities.Entity)
|
||||
{
|
||||
case Item item:
|
||||
message.Write((byte)SpawnableType.Item);
|
||||
message.WriteByte((byte)SpawnableType.Item);
|
||||
DebugConsole.Log(
|
||||
$"Writing item spawn data {item} (ID: {entities.ID})");
|
||||
item.WriteSpawnData(message, entities.ID, entities.InventoryID, entities.ItemContainerIndex, entities.SlotIndex);
|
||||
break;
|
||||
case Character character:
|
||||
message.Write((byte)SpawnableType.Character);
|
||||
message.WriteByte((byte)SpawnableType.Character);
|
||||
DebugConsole.Log(
|
||||
$"Writing character spawn data: {character} (ID: {entities.ID})");
|
||||
character.WriteSpawnData(message, entities.ID, restrictMessageSize: true);
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public static int MaxPacketsPerUpdate = 4;
|
||||
public static int MaxPacketsPerUpdate = 10;
|
||||
public float PacketsPerUpdate { get; set; } = 1.0f;
|
||||
|
||||
public byte[] Data { get; }
|
||||
@@ -219,27 +219,27 @@ namespace Barotrauma.Networking
|
||||
if (!transfer.Acknowledged)
|
||||
{
|
||||
message = new WriteOnlyMessage();
|
||||
message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
|
||||
message.WriteByte((byte)ServerPacketHeader.FILE_TRANSFER);
|
||||
|
||||
//if the recipient is the owner of the server (= a client running the server from the main exe)
|
||||
//we don't need to send anything, the client can just read the file directly
|
||||
if (transfer.Connection == GameMain.Server.OwnerConnection)
|
||||
{
|
||||
message.Write((byte)FileTransferMessageType.TransferOnSameMachine);
|
||||
message.Write((byte)transfer.ID);
|
||||
message.Write((byte)transfer.FileType);
|
||||
message.Write(transfer.FilePath);
|
||||
message.WriteByte((byte)FileTransferMessageType.TransferOnSameMachine);
|
||||
message.WriteByte((byte)transfer.ID);
|
||||
message.WriteByte((byte)transfer.FileType);
|
||||
message.WriteString(transfer.FilePath);
|
||||
peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
|
||||
transfer.Status = FileTransferStatus.Finished;
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Write((byte)FileTransferMessageType.Initiate);
|
||||
message.Write((byte)transfer.ID);
|
||||
message.Write((byte)transfer.FileType);
|
||||
message.WriteByte((byte)FileTransferMessageType.Initiate);
|
||||
message.WriteByte((byte)transfer.ID);
|
||||
message.WriteByte((byte)transfer.FileType);
|
||||
//message.Write((ushort)chunkLen);
|
||||
message.Write(transfer.Data.Length);
|
||||
message.Write(transfer.FileName);
|
||||
message.WriteInt32(transfer.Data.Length);
|
||||
message.WriteString(transfer.FileName);
|
||||
peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
|
||||
|
||||
transfer.Status = FileTransferStatus.Sending;
|
||||
@@ -262,13 +262,13 @@ namespace Barotrauma.Networking
|
||||
int sendByteCount = (remaining > chunkLen ? chunkLen : (int)remaining);
|
||||
|
||||
message = new WriteOnlyMessage();
|
||||
message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
|
||||
message.Write((byte)FileTransferMessageType.Data);
|
||||
message.WriteByte((byte)ServerPacketHeader.FILE_TRANSFER);
|
||||
message.WriteByte((byte)FileTransferMessageType.Data);
|
||||
|
||||
message.Write((byte)transfer.ID);
|
||||
message.Write(transfer.SentOffset);
|
||||
message.WriteByte((byte)transfer.ID);
|
||||
message.WriteInt32(transfer.SentOffset);
|
||||
|
||||
message.Write((ushort)sendByteCount);
|
||||
message.WriteUInt16((ushort)sendByteCount);
|
||||
int chunkDestPos = message.BytePosition;
|
||||
message.BitPosition += sendByteCount * 8;
|
||||
message.LengthBits = Math.Max(message.LengthBits, message.BitPosition);
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Barotrauma.Networking
|
||||
string resultFileName
|
||||
= dir.StartsWith(ContentPackage.LocalModsDir)
|
||||
? $"Local_{mod.Name}"
|
||||
: $"Workshop_{mod.Name}_{mod.SteamWorkshopId}";
|
||||
: $"Workshop_{mod.Name}_{(mod.UgcId.TryUnwrap(out var ugcId) ? ugcId.ToString() : "NULL")}";
|
||||
resultFileName = ToolBox.RemoveInvalidFileNameChars(resultFileName.Replace('\\', '_').Replace('/', '_'));
|
||||
resultFileName = $"{resultFileName}{Extension}";
|
||||
return Path.Combine(UploadFolder, resultFileName);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -188,9 +188,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedConstruction != null)
|
||||
if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedItem != null)
|
||||
{
|
||||
if (client.Character.SelectedConstruction.GetComponent<Steering>() != null)
|
||||
if (client.Character.SelectedItem.GetComponent<Steering>() != null)
|
||||
{
|
||||
AdjustKarma(client.Character, SteerSubKarmaIncrease * deltaTime, "Steering the sub");
|
||||
}
|
||||
|
||||
+8
-8
@@ -246,7 +246,7 @@ namespace Barotrauma.Networking
|
||||
" (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago, " +
|
||||
(lastSentToAnyoneTime - firstEventToResend.CreateTime).ToString("0.##") + " s older than last event sent to anyone)" +
|
||||
" Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
|
||||
server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncOldEvent + "/ServerMessage.ExcessiveDesyncOldEvent");
|
||||
server.DisconnectClient(c, PeerDisconnectPacket.WithReason(DisconnectReason.ExcessiveDesyncOldEvent));
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -260,7 +260,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
DebugConsole.NewMessage(c.Name + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
|
||||
server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncRemovedEvent + "/ServerMessage.ExcessiveDesyncRemovedEvent");
|
||||
server.DisconnectClient(c, PeerDisconnectPacket.WithReason(DisconnectReason.ExcessiveDesyncRemovedEvent));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -269,7 +269,7 @@ namespace Barotrauma.Networking
|
||||
foreach (Client timedOutClient in timedOutClients)
|
||||
{
|
||||
GameServer.Log("Disconnecting client " + GameServer.ClientLogName(timedOutClient) + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
|
||||
GameMain.Server.DisconnectClient(timedOutClient, "", DisconnectReason.SyncTimeout + "/ServerMessage.SyncTimeout");
|
||||
GameMain.Server.DisconnectClient(timedOutClient, PeerDisconnectPacket.WithReason(DisconnectReason.SyncTimeout));
|
||||
}
|
||||
|
||||
bufferedEvents.RemoveAll(b => b.IsProcessed);
|
||||
@@ -344,15 +344,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (client.NeedsMidRoundSync)
|
||||
{
|
||||
msg.Write((byte)ServerNetObject.ENTITY_EVENT_INITIAL);
|
||||
msg.Write(client.UnreceivedEntityEventCount);
|
||||
msg.Write(client.FirstNewEventID);
|
||||
msg.WriteByte((byte)ServerNetObject.ENTITY_EVENT_INITIAL);
|
||||
msg.WriteUInt16(client.UnreceivedEntityEventCount);
|
||||
msg.WriteUInt16(client.FirstNewEventID);
|
||||
|
||||
Write(msg, eventsToSync, out sentEvents, client);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write((byte)ServerNetObject.ENTITY_EVENT);
|
||||
msg.WriteByte((byte)ServerNetObject.ENTITY_EVENT);
|
||||
Write(msg, eventsToSync, out sentEvents, client);
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
ReadWriteMessage buffer = new ReadWriteMessage();
|
||||
byte[] temp = msg.ReadBytes(msgLength - 2);
|
||||
buffer.Write(temp, 0, msgLength - 2);
|
||||
buffer.WriteBytes(temp, 0, msgLength - 2);
|
||||
buffer.BitPosition = 0;
|
||||
BufferEvent(new BufferedEvent(sender, sender.Character, characterStateID, entity, buffer));
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Barotrauma.Steam;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -6,21 +7,21 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
public override void ServerWrite(IWriteMessage msg, Client c)
|
||||
{
|
||||
msg.Write((byte)ServerNetObject.CHAT_MESSAGE);
|
||||
msg.Write(NetStateID);
|
||||
msg.WriteByte((byte)ServerNetObject.CHAT_MESSAGE);
|
||||
msg.WriteUInt16(NetStateID);
|
||||
msg.WriteRangedInteger((int)ChatMessageType.Order, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
|
||||
msg.Write(SenderName);
|
||||
msg.Write(SenderClient != null);
|
||||
msg.WriteString(SenderName);
|
||||
msg.WriteBoolean(SenderClient != null);
|
||||
if (SenderClient != null)
|
||||
{
|
||||
msg.Write((SenderClient.SteamID != 0) ? SenderClient.SteamID : SenderClient.ID);
|
||||
msg.WriteString(SenderClient.AccountId.TryUnwrap(out var accountId) ? accountId.StringRepresentation : SenderClient.SessionId.ToString());
|
||||
}
|
||||
msg.Write(Sender != null && c.InGame);
|
||||
msg.WriteBoolean(Sender != null && c.InGame);
|
||||
if (Sender != null && c.InGame)
|
||||
{
|
||||
msg.Write(Sender.ID);
|
||||
}
|
||||
msg.Write(false); //text color (no custom text colors for order messages)
|
||||
msg.WriteUInt16(Sender.ID);
|
||||
}
|
||||
msg.WriteBoolean(false); //text color (no custom text colors for order messages)
|
||||
msg.WritePadBits();
|
||||
WriteOrder(msg);
|
||||
}
|
||||
|
||||
+166
-167
@@ -1,24 +1,45 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using Barotrauma.Steam;
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class LidgrenServerPeer : ServerPeer
|
||||
internal sealed class LidgrenServerPeer : ServerPeer
|
||||
{
|
||||
private NetPeerConfiguration netPeerConfiguration;
|
||||
private NetServer netServer;
|
||||
private readonly NetPeerConfiguration netPeerConfiguration;
|
||||
private NetServer? netServer;
|
||||
|
||||
private readonly List<NetIncomingMessage> incomingLidgrenMessages;
|
||||
|
||||
public LidgrenServerPeer(int? ownKey, ServerSettings settings)
|
||||
public LidgrenServerPeer(Option<int> ownKey, ServerSettings settings, Callbacks callbacks) : base(callbacks)
|
||||
{
|
||||
serverSettings = settings;
|
||||
|
||||
netServer = null;
|
||||
|
||||
netPeerConfiguration = new NetPeerConfiguration("barotrauma")
|
||||
{
|
||||
AcceptIncomingConnections = true,
|
||||
AutoExpandMTU = false,
|
||||
MaximumConnections = NetConfig.MaxPlayers * 2,
|
||||
EnableUPnP = serverSettings.EnableUPnP,
|
||||
Port = serverSettings.Port
|
||||
};
|
||||
|
||||
netPeerConfiguration.DisableMessageType(
|
||||
NetIncomingMessageType.DebugMessage
|
||||
| NetIncomingMessageType.WarningMessage
|
||||
| NetIncomingMessageType.Receipt
|
||||
| NetIncomingMessageType.ErrorMessage
|
||||
| NetIncomingMessageType.Error
|
||||
| NetIncomingMessageType.UnconnectedData);
|
||||
|
||||
netPeerConfiguration.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
|
||||
|
||||
connectedClients = new List<NetworkConnection>();
|
||||
pendingClients = new List<PendingClient>();
|
||||
|
||||
@@ -31,25 +52,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (netServer != null) { return; }
|
||||
|
||||
var address = serverSettings.ListenIPAddress;
|
||||
if (address == IPAddress.Any) address = IPAddress.IPv6Any;
|
||||
|
||||
netPeerConfiguration = new NetPeerConfiguration("barotrauma")
|
||||
{
|
||||
AcceptIncomingConnections = true,
|
||||
AutoExpandMTU = false,
|
||||
MaximumConnections = NetConfig.MaxPlayers * 2,
|
||||
EnableUPnP = serverSettings.EnableUPnP,
|
||||
Port = serverSettings.Port,
|
||||
LocalAddress = address
|
||||
};
|
||||
|
||||
netPeerConfiguration.DisableMessageType(NetIncomingMessageType.DebugMessage |
|
||||
NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt |
|
||||
NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error |
|
||||
NetIncomingMessageType.UnconnectedData);
|
||||
|
||||
netPeerConfiguration.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
|
||||
incomingLidgrenMessages.Clear();
|
||||
|
||||
netServer = new NetServer(netPeerConfiguration);
|
||||
|
||||
@@ -65,21 +68,21 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close(string msg = null)
|
||||
public override void Close()
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
for (int i = pendingClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
RemovePendingClient(pendingClients[i], DisconnectReason.ServerShutdown, msg);
|
||||
RemovePendingClient(pendingClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
|
||||
}
|
||||
|
||||
for (int i = connectedClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Disconnect(connectedClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
|
||||
Disconnect(connectedClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
|
||||
}
|
||||
|
||||
netServer.Shutdown(msg ?? DisconnectReason.ServerShutdown.ToString());
|
||||
netServer.Shutdown(PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown).ToLidgrenStringRepresentation());
|
||||
|
||||
pendingClients.Clear();
|
||||
connectedClients.Clear();
|
||||
@@ -88,21 +91,17 @@ namespace Barotrauma.Networking
|
||||
|
||||
Steamworks.SteamServer.OnValidateAuthTicketResponse -= OnAuthChange;
|
||||
|
||||
OnShutdown?.Invoke();
|
||||
callbacks.OnShutdown.Invoke();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
if (netServer is null) { return; }
|
||||
|
||||
if (OnOwnerDetermined != null && OwnerConnection != null)
|
||||
{
|
||||
OnOwnerDetermined?.Invoke(OwnerConnection);
|
||||
OnOwnerDetermined = null;
|
||||
}
|
||||
ToolBox.ThrowIfNull(incomingLidgrenMessages);
|
||||
|
||||
netServer.ReadMessages(incomingLidgrenMessages);
|
||||
|
||||
|
||||
//process incoming connections first
|
||||
foreach (NetIncomingMessage inc in incomingLidgrenMessages.Where(m => m.MessageType == NetIncomingMessageType.ConnectionApproval))
|
||||
{
|
||||
@@ -129,7 +128,7 @@ namespace Barotrauma.Networking
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
|
||||
GameAnalyticsManager.AddErrorEventOnce("LidgrenServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce($"LidgrenServerPeer.Update:ClientReadException{e.TargetSite}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#else
|
||||
@@ -141,7 +140,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
PendingClient pendingClient = pendingClients[i];
|
||||
|
||||
var connection = pendingClient.Connection as LidgrenConnection;
|
||||
LidgrenConnection connection = (LidgrenConnection)pendingClient.Connection;
|
||||
|
||||
if (connection.NetConnection.Status == NetConnectionStatus.InitiatedConnect ||
|
||||
connection.NetConnection.Status == NetConnectionStatus.ReceivedInitiation ||
|
||||
connection.NetConnection.Status == NetConnectionStatus.RespondedAwaitingApproval ||
|
||||
@@ -149,6 +149,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UpdatePendingClient(pendingClient);
|
||||
if (i >= pendingClients.Count || pendingClients[i] != pendingClient) { i--; }
|
||||
}
|
||||
@@ -158,7 +159,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
private void InitUPnP()
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
if (netServer is null) { return; }
|
||||
|
||||
ToolBox.ThrowIfNull(netPeerConfiguration);
|
||||
|
||||
netServer.UPnP.ForwardPort(netPeerConfiguration.Port, "barotrauma");
|
||||
#if USE_STEAM
|
||||
@@ -193,71 +196,74 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (!skipDeny && connectedClients.Count >= serverSettings.MaxPlayers)
|
||||
{
|
||||
inc.SenderConnection.Deny(DisconnectReason.ServerFull.ToString());
|
||||
inc.SenderConnection.Deny(PeerDisconnectPacket.WithReason(DisconnectReason.ServerFull).ToLidgrenStringRepresentation());
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverSettings.BanList.IsBanned(inc.SenderConnection.RemoteEndPoint.Address, 0, 0, out string banReason))
|
||||
if (serverSettings.BanList.IsBanned(new LidgrenEndpoint(inc.SenderConnection.RemoteEndPoint), out string banReason))
|
||||
{
|
||||
//IP banned: deny immediately
|
||||
inc.SenderConnection.Deny(DisconnectReason.Banned.ToString() + "/ " + banReason);
|
||||
inc.SenderConnection.Deny(PeerDisconnectPacket.Banned(banReason).ToLidgrenStringRepresentation());
|
||||
return;
|
||||
}
|
||||
|
||||
PendingClient pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
|
||||
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
|
||||
|
||||
if (pendingClient == null)
|
||||
if (pendingClient is null)
|
||||
{
|
||||
pendingClient = new PendingClient(new LidgrenConnection("PENDING", inc.SenderConnection, 0));
|
||||
pendingClient = new PendingClient(new LidgrenConnection(inc.SenderConnection));
|
||||
pendingClients.Add(pendingClient);
|
||||
}
|
||||
|
||||
inc.SenderConnection.Approve();
|
||||
}
|
||||
|
||||
private void HandleDataMessage(NetIncomingMessage inc)
|
||||
private void HandleDataMessage(NetIncomingMessage lidgrenMsg)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
|
||||
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == lidgrenMsg.SenderConnection);
|
||||
|
||||
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
|
||||
IReadMessage inc = lidgrenMsg.ToReadMessage();
|
||||
|
||||
if (packetHeader.IsConnectionInitializationStep() && pendingClient != null)
|
||||
var (_, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
if (packetHeader.IsConnectionInitializationStep() && pendingClient != null && initialization.HasValue)
|
||||
{
|
||||
ReadConnectionInitializationStep(pendingClient, new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
|
||||
ReadConnectionInitializationStep(pendingClient, inc, initialization.Value);
|
||||
}
|
||||
else if (!packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
LidgrenConnection conn = connectedClients.Find(c => (c is LidgrenConnection l) && l.NetConnection == inc.SenderConnection) as LidgrenConnection;
|
||||
if (conn == null)
|
||||
if (connectedClients.Find(c => c is LidgrenConnection l && l.NetConnection == lidgrenMsg.SenderConnection) is not LidgrenConnection conn)
|
||||
{
|
||||
if (pendingClient != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.AuthenticationRequired, "Received data message from unauthenticated client");
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationRequired));
|
||||
}
|
||||
else if (inc.SenderConnection.Status != NetConnectionStatus.Disconnected &&
|
||||
inc.SenderConnection.Status != NetConnectionStatus.Disconnecting)
|
||||
else if (lidgrenMsg.SenderConnection.Status != NetConnectionStatus.Disconnected &&
|
||||
lidgrenMsg.SenderConnection.Status != NetConnectionStatus.Disconnecting)
|
||||
{
|
||||
inc.SenderConnection.Disconnect(DisconnectReason.AuthenticationRequired.ToString() + "/ Received data message from unauthenticated client");
|
||||
lidgrenMsg.SenderConnection.Disconnect(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationRequired).ToLidgrenStringRepresentation());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingClient != null) { pendingClients.Remove(pendingClient); }
|
||||
if (serverSettings.BanList.IsBanned(conn.IPEndPoint.Address, conn.SteamID, conn.OwnerSteamID, out string banReason))
|
||||
|
||||
if (serverSettings.BanList.IsBanned(conn.Endpoint, out string banReason)
|
||||
|| (conn.AccountInfo.AccountId.TryUnwrap(out var accountId) && serverSettings.BanList.IsBanned(accountId, out banReason))
|
||||
|| conn.AccountInfo.OtherMatchingIds.Any(id => serverSettings.BanList.IsBanned(id, out banReason)))
|
||||
{
|
||||
Disconnect(conn, DisconnectReason.Banned.ToString() + "/ " + banReason);
|
||||
Disconnect(conn, PeerDisconnectPacket.Banned(banReason));
|
||||
return;
|
||||
}
|
||||
UInt16 length = inc.ReadUInt16();
|
||||
|
||||
//DebugConsole.NewMessage(isCompressed + " " + isConnectionInitializationStep + " " + (int)incByte + " " + length);
|
||||
|
||||
IReadMessage msg = new ReadOnlyMessage(inc.Data, packetHeader.IsCompressed(), inc.PositionInBytes, length, conn);
|
||||
OnMessageReceived?.Invoke(conn, msg);
|
||||
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
|
||||
callbacks.OnMessageReceived.Invoke(conn, packet.GetReadMessage(packetHeader.IsCompressed(), conn));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void HandleStatusChanged(NetIncomingMessage inc)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
@@ -265,30 +271,29 @@ namespace Barotrauma.Networking
|
||||
switch (inc.SenderConnection.Status)
|
||||
{
|
||||
case NetConnectionStatus.Disconnected:
|
||||
string disconnectMsg;
|
||||
LidgrenConnection conn = connectedClients.Select(c => c as LidgrenConnection).FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
|
||||
LidgrenConnection? conn = connectedClients.Cast<LidgrenConnection>().FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
|
||||
if (conn != null)
|
||||
{
|
||||
if (conn == OwnerConnection)
|
||||
{
|
||||
DebugConsole.NewMessage("Owner disconnected: closing the server...");
|
||||
GameServer.Log("Owner disconnected: closing the server...", ServerLog.MessageType.ServerMessage);
|
||||
Close(DisconnectReason.ServerShutdown.ToString() + "/ Owner disconnected");
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
disconnectMsg = $"ServerMessage.HasDisconnected~[client]={conn.Name}";
|
||||
Disconnect(conn, disconnectMsg);
|
||||
Disconnect(conn, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == 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}");
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -298,44 +303,45 @@ namespace Barotrauma.Networking
|
||||
Steamworks.SteamServer.OnValidateAuthTicketResponse += OnAuthChange;
|
||||
}
|
||||
|
||||
private void OnAuthChange(Steamworks.SteamId steamID, Steamworks.SteamId ownerID, Steamworks.AuthResponse status)
|
||||
private void OnAuthChange(Steamworks.SteamId steamId, Steamworks.SteamId ownerId, Steamworks.AuthResponse status)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
PendingClient pendingClient = pendingClients.Find(c => c.SteamID == steamID);
|
||||
DebugConsole.Log(steamID + " validation: " + status+", "+(pendingClient!=null));
|
||||
|
||||
if (pendingClient == null)
|
||||
PendingClient? pendingClient = pendingClients.Find(c => c.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId);
|
||||
DebugConsole.Log($"{steamId} validation: {status}, {(pendingClient != null)}");
|
||||
|
||||
if (pendingClient is null)
|
||||
{
|
||||
if (status != Steamworks.AuthResponse.OK)
|
||||
if (status == Steamworks.AuthResponse.OK) { return; }
|
||||
|
||||
if (connectedClients.Find(c
|
||||
=> c.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId)
|
||||
is LidgrenConnection connection)
|
||||
{
|
||||
LidgrenConnection connection = connectedClients.Find(c => c.SteamID == steamID) as LidgrenConnection;
|
||||
if (connection != null)
|
||||
{
|
||||
Disconnect(connection, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication status changed: " + status.ToString());
|
||||
}
|
||||
Disconnect(connection, PeerDisconnectPacket.SteamAuthError(status));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LidgrenConnection pendingConnection = pendingClient.Connection as LidgrenConnection;
|
||||
string banReason;
|
||||
if (serverSettings.BanList.IsBanned(pendingConnection.NetConnection.RemoteEndPoint.Address, steamID, ownerID, out banReason))
|
||||
LidgrenConnection pendingConnection = (LidgrenConnection)pendingClient.Connection;
|
||||
if (serverSettings.BanList.IsBanned(pendingConnection.Endpoint, out string banReason)
|
||||
|| serverSettings.BanList.IsBanned(new SteamId(steamId), out banReason)
|
||||
|| serverSettings.BanList.IsBanned(new SteamId(ownerId), out banReason))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == Steamworks.AuthResponse.OK)
|
||||
{
|
||||
pendingClient.OwnerSteamID = ownerID;
|
||||
pendingClient.Connection.SetAccountInfo(new AccountInfo(new SteamId(steamId), new SteamId(ownerId)));
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam authentication failed: " + status.ToString());
|
||||
return;
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.SteamAuthError(status));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,151 +349,144 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
if (!(conn is LidgrenConnection lidgrenConn)) return;
|
||||
if (!connectedClients.Contains(lidgrenConn))
|
||||
if (!connectedClients.Contains(conn))
|
||||
{
|
||||
DebugConsole.ThrowError("Tried to send message to unauthenticated connection: " + lidgrenConn.IPString);
|
||||
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {conn.Endpoint.StringRepresentation}");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
|
||||
|
||||
#if DEBUG
|
||||
ToolBox.ThrowIfNull(netPeerConfiguration);
|
||||
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Server.SimulatedDuplicatesChance;
|
||||
netPeerConfiguration.SimulatedMinimumLatency = GameMain.Server.SimulatedMinimumLatency;
|
||||
netPeerConfiguration.SimulatedRandomLatency = GameMain.Server.SimulatedRandomLatency;
|
||||
netPeerConfiguration.SimulatedLoss = GameMain.Server.SimulatedLoss;
|
||||
#endif
|
||||
|
||||
NetOutgoingMessage lidgrenMsg = netServer.CreateMessage();
|
||||
byte[] msgData = new byte[msg.LengthBytes];
|
||||
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
|
||||
lidgrenMsg.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
|
||||
lidgrenMsg.Write((UInt16)length);
|
||||
lidgrenMsg.Write(msgData, 0, length);
|
||||
|
||||
NetSendResult result = netServer.SendMessage(lidgrenMsg, lidgrenConn.NetConnection, lidgrenDeliveryMethod);
|
||||
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DebugConsole.NewMessage("Failed to send message to "+conn.Name+": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
|
||||
}
|
||||
DeliveryMethod = deliveryMethod,
|
||||
PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None,
|
||||
Initialization = null
|
||||
};
|
||||
var body = new PeerPacketMessage
|
||||
{
|
||||
Buffer = bufAux
|
||||
};
|
||||
SendMsgInternal(conn, headers, body);
|
||||
}
|
||||
|
||||
public override void Disconnect(NetworkConnection conn,string msg=null)
|
||||
|
||||
public override void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket)
|
||||
{
|
||||
if (netServer == null) { return; }
|
||||
|
||||
if (!(conn is LidgrenConnection lidgrenConn)) { return; }
|
||||
if (conn is not LidgrenConnection lidgrenConn) { return; }
|
||||
|
||||
if (connectedClients.Contains(lidgrenConn))
|
||||
{
|
||||
lidgrenConn.Status = NetworkConnectionStatus.Disconnected;
|
||||
connectedClients.Remove(lidgrenConn);
|
||||
OnDisconnect?.Invoke(conn, msg);
|
||||
Steam.SteamManager.StopAuthSession(conn.SteamID);
|
||||
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
|
||||
if (conn.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId }) { SteamManager.StopAuthSession(steamId); }
|
||||
}
|
||||
lidgrenConn.NetConnection.Disconnect(msg ?? "Disconnected");
|
||||
|
||||
lidgrenConn.NetConnection.Disconnect(peerDisconnectPacket.ToLidgrenStringRepresentation());
|
||||
}
|
||||
|
||||
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
|
||||
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
|
||||
{
|
||||
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;
|
||||
}
|
||||
IWriteMessage msgToSend = new WriteOnlyMessage();
|
||||
msgToSend.WriteNetSerializableStruct(headers);
|
||||
body?.Write(msgToSend);
|
||||
|
||||
NetOutgoingMessage lidgrenMsg = netServer.CreateMessage();
|
||||
lidgrenMsg.Write(msg.Buffer, 0, msg.LengthBytes);
|
||||
NetSendResult result = netServer.SendMessage(lidgrenMsg, lidgrenConn.NetConnection, lidgrenDeliveryMethod);
|
||||
NetSendResult result = ForwardToLidgren(msgToSend, conn, headers.DeliveryMethod);
|
||||
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
|
||||
{
|
||||
DebugConsole.NewMessage("Failed to send message to " + conn.Name + ": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
|
||||
DebugConsole.NewMessage($"Failed to send message to {conn.Endpoint}: {result}", 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)
|
||||
if (OwnerConnection == null
|
||||
&& pendingClient.Connection is LidgrenConnection l
|
||||
&& IPAddress.IsLoopback(l.NetConnection.RemoteEndPoint.Address)
|
||||
&& ownerKey.IsSome() && pendingClient.OwnerKey == ownerKey)
|
||||
{
|
||||
ownerKey = null;
|
||||
ownerKey = Option<int>.None();
|
||||
OwnerConnection = pendingClient.Connection;
|
||||
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket)
|
||||
protected override void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient)
|
||||
{
|
||||
if (pendingClient.SteamID == null)
|
||||
if (pendingClient.AccountInfo.AccountId.IsNone())
|
||||
{
|
||||
bool requireSteamAuth = GameSettings.CurrentConfig.RequireSteamAuthentication;
|
||||
#if DEBUG
|
||||
requireSteamAuth = false;
|
||||
#endif
|
||||
bool hasSteamAuth = packet.SteamAuthTicket.TryUnwrap(out var ticket);
|
||||
|
||||
//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)
|
||||
if ((!SteamManager.IsInitialized || !hasSteamAuth) && !requireSteamAuth)
|
||||
{
|
||||
pendingClient.Connection.Name = name;
|
||||
pendingClient.Name = name;
|
||||
pendingClient.OwnerKey = ownKey;
|
||||
pendingClient.Name = packet.Name;
|
||||
pendingClient.OwnerKey = packet.OwnerKey;
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
|
||||
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
|
||||
if (!packet.SteamId.TryUnwrap(out var id) || id is not SteamId steamId)
|
||||
{
|
||||
if (requireSteamAuth)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: " + authSessionStartState.ToString());
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.SteamAuthenticationFailed));
|
||||
return;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
Steamworks.BeginAuthResult authSessionStartState = SteamManager.StartAuthSession(ticket, steamId);
|
||||
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
|
||||
{
|
||||
steamId = 0;
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
if (requireSteamAuth)
|
||||
{
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.SteamAuthError(authSessionStartState));
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.SteamId = Option<AccountId>.None();
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
}
|
||||
}
|
||||
pendingClient.SteamID = steamId;
|
||||
pendingClient.Connection.Name = name;
|
||||
pendingClient.Name = name;
|
||||
pendingClient.OwnerKey = ownKey;
|
||||
|
||||
pendingClient.Connection.SetAccountInfo(new AccountInfo(packet.SteamId.Select(uid => (AccountId)uid)));
|
||||
pendingClient.Name = packet.Name;
|
||||
pendingClient.OwnerKey = packet.OwnerKey;
|
||||
pendingClient.AuthSessionStarted = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pendingClient.SteamID != steamId)
|
||||
if (pendingClient.AccountInfo.AccountId != packet.SteamId.Select(uid => (AccountId)uid))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
|
||||
return;
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.SteamAuthenticationFailed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NetSendResult ForwardToLidgren(IWriteMessage msg, NetworkConnection connection, DeliveryMethod deliveryMethod)
|
||||
{
|
||||
ToolBox.ThrowIfNull(netServer);
|
||||
|
||||
LidgrenConnection conn = (LidgrenConnection)connection;
|
||||
return netServer.SendMessage(msg.ToLidgren(netServer), conn.NetConnection, deliveryMethod.ToLidgren());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+133
-132
@@ -1,75 +1,61 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
abstract class ServerPeer
|
||||
internal abstract class ServerPeer
|
||||
{
|
||||
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
|
||||
public delegate void DisconnectCallback(NetworkConnection connection, string reason);
|
||||
public delegate void InitializationCompleteCallback(NetworkConnection connection);
|
||||
public delegate void ShutdownCallback();
|
||||
public delegate void OwnerDeterminedCallback(NetworkConnection connection);
|
||||
public readonly record struct Callbacks(
|
||||
Callbacks.MessageCallback OnMessageReceived,
|
||||
Callbacks.DisconnectCallback OnDisconnect,
|
||||
Callbacks.InitializationCompleteCallback OnInitializationComplete,
|
||||
Callbacks.ShutdownCallback OnShutdown,
|
||||
Callbacks.OwnerDeterminedCallback OnOwnerDetermined)
|
||||
{
|
||||
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
|
||||
public delegate void DisconnectCallback(NetworkConnection connection, PeerDisconnectPacket peerDisconnectPacket);
|
||||
public delegate void InitializationCompleteCallback(NetworkConnection connection, string? clientName);
|
||||
public delegate void ShutdownCallback();
|
||||
public delegate void OwnerDeterminedCallback(NetworkConnection connection);
|
||||
}
|
||||
|
||||
public MessageCallback OnMessageReceived;
|
||||
public DisconnectCallback OnDisconnect;
|
||||
public InitializationCompleteCallback OnInitializationComplete;
|
||||
public ShutdownCallback OnShutdown;
|
||||
public OwnerDeterminedCallback OnOwnerDetermined;
|
||||
|
||||
protected int? ownerKey;
|
||||
|
||||
public NetworkConnection OwnerConnection { get; protected set; }
|
||||
protected readonly Callbacks callbacks;
|
||||
|
||||
protected ServerPeer(Callbacks callbacks)
|
||||
{
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
public abstract void InitializeSteamServerCallbacks();
|
||||
|
||||
public abstract void Start();
|
||||
public abstract void Close(string msg = null);
|
||||
public abstract void Close();
|
||||
public abstract void Update(float deltaTime);
|
||||
|
||||
public class PendingClient
|
||||
protected sealed class PendingClient
|
||||
{
|
||||
public string Name;
|
||||
public int OwnerKey;
|
||||
public NetworkConnection Connection;
|
||||
public string? Name;
|
||||
public Option<int> OwnerKey;
|
||||
public readonly NetworkConnection Connection;
|
||||
public ConnectionInitialization InitializationStep;
|
||||
public double UpdateTime;
|
||||
public double TimeOut;
|
||||
public int Retries;
|
||||
private UInt64? steamId;
|
||||
public UInt64? SteamID
|
||||
{
|
||||
get { return steamId; }
|
||||
set
|
||||
{
|
||||
steamId = value;
|
||||
Connection.SetSteamIDIfUnknown(value ?? 0);
|
||||
}
|
||||
}
|
||||
private UInt64? ownerSteamId;
|
||||
public UInt64? OwnerSteamID
|
||||
{
|
||||
get { return ownerSteamId; }
|
||||
set
|
||||
{
|
||||
ownerSteamId = value;
|
||||
Connection.SetOwnerSteamIDIfUnknown(value ?? 0);
|
||||
}
|
||||
}
|
||||
public Int32? PasswordSalt;
|
||||
public bool AuthSessionStarted;
|
||||
|
||||
public AccountInfo AccountInfo => Connection.AccountInfo;
|
||||
|
||||
public PendingClient(NetworkConnection conn)
|
||||
{
|
||||
OwnerKey = 0;
|
||||
OwnerKey = Option<int>.None();
|
||||
Connection = conn;
|
||||
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
|
||||
Retries = 0;
|
||||
SteamID = null;
|
||||
OwnerSteamID = null;
|
||||
PasswordSalt = null;
|
||||
UpdateTime = Timing.TotalTime + Timing.Step * 3.0;
|
||||
TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
@@ -81,73 +67,70 @@ namespace Barotrauma.Networking
|
||||
TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
}
|
||||
}
|
||||
protected List<NetworkConnection> connectedClients;
|
||||
protected List<PendingClient> pendingClients;
|
||||
|
||||
protected ServerSettings serverSettings;
|
||||
protected List<NetworkConnection> connectedClients = null!;
|
||||
protected List<PendingClient> pendingClients = null!;
|
||||
protected ServerSettings serverSettings = null!;
|
||||
protected Option<int> ownerKey = null!;
|
||||
protected NetworkConnection? OwnerConnection;
|
||||
|
||||
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
|
||||
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc, ConnectionInitialization initializationStep)
|
||||
{
|
||||
pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;
|
||||
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
|
||||
|
||||
if (pendingClient.InitializationStep != initializationStep) return;
|
||||
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);
|
||||
var authPacket = INetSerializableStruct.Read<ClientSteamTicketAndVersionPacket>(inc);
|
||||
|
||||
if (!Client.IsValidName(name, serverSettings))
|
||||
if (!Client.IsValidName(authPacket.Name, serverSettings))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "");
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.InvalidName));
|
||||
return;
|
||||
}
|
||||
|
||||
string version = inc.ReadString();
|
||||
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
|
||||
bool isCompatibleVersion =
|
||||
Version.TryParse(authPacket.GameVersion, out var remoteVersion)
|
||||
&& NetworkMember.IsCompatible(remoteVersion, GameMain.Version);
|
||||
if (!isCompatibleVersion)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
|
||||
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.InvalidVersion());
|
||||
|
||||
GameServer.Log($"{name} ({steamId}) couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
|
||||
DebugConsole.NewMessage($"{name} ({steamId}) couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
|
||||
GameServer.Log($"{authPacket.Name} ({authPacket.SteamId}) couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
|
||||
DebugConsole.NewMessage($"{authPacket.Name} ({authPacket.SteamId}) couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
LanguageIdentifier language = inc.ReadIdentifier().ToLanguageIdentifier();
|
||||
pendingClient.Connection.Language = language;
|
||||
pendingClient.Connection.Language = authPacket.Language.ToLanguageIdentifier();
|
||||
|
||||
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
|
||||
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), authPacket.Name.ToLower()));
|
||||
if (nameTaken != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
|
||||
GameServer.Log($"{name} ({steamId}) couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.NameTaken));
|
||||
GameServer.Log($"{authPacket.Name} ({authPacket.SteamId}) 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);
|
||||
ProcessAuthTicket(authPacket, pendingClient);
|
||||
}
|
||||
|
||||
break;
|
||||
case ConnectionInitialization.Password:
|
||||
int pwLength = inc.ReadByte();
|
||||
byte[] incPassword = inc.ReadBytes(pwLength);
|
||||
if (pendingClient.PasswordSalt == null)
|
||||
var passwordPacket = INetSerializableStruct.Read<ClientPeerPasswordPacket>(inc);
|
||||
|
||||
if (pendingClient.PasswordSalt is null)
|
||||
{
|
||||
DebugConsole.ThrowError("Received password message from client without salt");
|
||||
return;
|
||||
}
|
||||
if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
|
||||
|
||||
if (serverSettings.IsPasswordCorrect(passwordPacket.Password, pendingClient.PasswordSalt.Value))
|
||||
{
|
||||
pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
|
||||
}
|
||||
@@ -156,13 +139,13 @@ namespace Barotrauma.Networking
|
||||
pendingClient.Retries++;
|
||||
if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
|
||||
{
|
||||
string banMsg = "Failed to enter correct password too many times";
|
||||
const string banMsg = "Failed to enter correct password too many times";
|
||||
BanPendingClient(pendingClient, banMsg, null);
|
||||
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banMsg));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pendingClient.UpdateTime = Timing.TotalTime;
|
||||
break;
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
@@ -172,37 +155,49 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket);
|
||||
protected abstract void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient);
|
||||
|
||||
protected void BanPendingClient(PendingClient pendingClient, string banReason, TimeSpan? duration)
|
||||
{
|
||||
if (pendingClient.Connection is LidgrenConnection l)
|
||||
void banAccountId(AccountId accountId)
|
||||
{
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, l.NetConnection.RemoteEndPoint.Address, banReason, duration);
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", accountId, banReason, duration);
|
||||
}
|
||||
else if (pendingClient.Connection is SteamP2PConnection s)
|
||||
|
||||
if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var id)) { banAccountId(id); }
|
||||
|
||||
pendingClient.AccountInfo.OtherMatchingIds.ForEach(banAccountId);
|
||||
if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var accountId))
|
||||
{
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, s.SteamID, banReason, duration);
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name, s.OwnerSteamID, banReason, duration);
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", accountId, banReason, duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", pendingClient.Connection.Endpoint, banReason, duration);
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsPendingClientBanned(PendingClient pendingClient, out string banReason)
|
||||
protected bool IsPendingClientBanned(PendingClient pendingClient, out string? banReason)
|
||||
{
|
||||
if (pendingClient.Connection is LidgrenConnection l)
|
||||
bool isAccountIdBanned(AccountId accountId, out string? banReason)
|
||||
{
|
||||
return serverSettings.BanList.IsBanned(l.NetConnection.RemoteEndPoint.Address, out banReason);
|
||||
return serverSettings.BanList.IsBanned(accountId, out banReason);
|
||||
}
|
||||
else if (pendingClient.Connection is SteamP2PConnection s)
|
||||
|
||||
banReason = default;
|
||||
bool isBanned = pendingClient.AccountInfo.AccountId.TryUnwrap(out var id)
|
||||
&& isAccountIdBanned(id, out banReason);
|
||||
foreach (var otherId in pendingClient.AccountInfo.OtherMatchingIds)
|
||||
{
|
||||
return serverSettings.BanList.IsBanned(s.SteamID, out banReason) ||
|
||||
serverSettings.BanList.IsBanned(s.OwnerSteamID, out banReason);
|
||||
if (isBanned) { break; }
|
||||
|
||||
isBanned |= isAccountIdBanned(otherId, out banReason);
|
||||
}
|
||||
banReason = null;
|
||||
return false;
|
||||
|
||||
return isBanned;
|
||||
}
|
||||
|
||||
protected abstract void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg);
|
||||
protected abstract void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body);
|
||||
|
||||
protected void UpdatePendingClient(PendingClient pendingClient)
|
||||
{
|
||||
@@ -213,12 +208,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (!skipRemove && connectedClients.Count >= serverSettings.MaxPlayers)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.ServerFull));
|
||||
}
|
||||
|
||||
if (IsPendingClientBanned(pendingClient, out string banReason))
|
||||
if (IsPendingClientBanned(pendingClient, out string? banReason))
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,80 +223,86 @@ namespace Barotrauma.Networking
|
||||
connectedClients.Add(newConnection);
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
CheckOwnership(pendingClient);
|
||||
callbacks.OnInitializationComplete.Invoke(newConnection, pendingClient.Name);
|
||||
|
||||
OnInitializationComplete?.Invoke(newConnection);
|
||||
CheckOwnership(pendingClient);
|
||||
}
|
||||
|
||||
pendingClient.TimeOut -= Timing.Step;
|
||||
if (pendingClient.TimeOut < 0.0)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Timeout));
|
||||
}
|
||||
|
||||
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);
|
||||
PeerPacketHeaders headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsConnectionInitializationStep | PacketHeader.IsServerMessage,
|
||||
Initialization = pendingClient.InitializationStep
|
||||
};
|
||||
|
||||
INetSerializableStruct? structToSend = null;
|
||||
|
||||
switch (pendingClient.InitializationStep)
|
||||
{
|
||||
case ConnectionInitialization.ContentPackageOrder:
|
||||
outMsg.Write(GameMain.Server.ServerName);
|
||||
|
||||
var mpContentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).ToList();
|
||||
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count);
|
||||
for (int i = 0; i < mpContentPackages.Count; i++)
|
||||
DateTime timeNow = DateTime.UtcNow;
|
||||
structToSend = new ServerPeerContentPackageOrderPacket
|
||||
{
|
||||
outMsg.Write(mpContentPackages[i].Name);
|
||||
byte[] hashBytes = mpContentPackages[i].Hash.ByteRepresentation;
|
||||
outMsg.WriteVariableUInt32((UInt32)hashBytes.Length);
|
||||
outMsg.Write(hashBytes, 0, hashBytes.Length);
|
||||
outMsg.Write(mpContentPackages[i].SteamWorkshopId);
|
||||
UInt32 installTimeDiffSeconds = (UInt32)((mpContentPackages[i].InstallTime ?? DateTime.UtcNow) - DateTime.UtcNow).TotalSeconds;
|
||||
outMsg.Write(installTimeDiffSeconds);
|
||||
}
|
||||
ServerName = GameMain.Server.ServerName,
|
||||
ContentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent || cp.Files.All(f => f is SubmarineFile))
|
||||
.Select(contentPackage => new ServerContentPackage(contentPackage, timeNow))
|
||||
.ToImmutableArray()
|
||||
};
|
||||
|
||||
break;
|
||||
case ConnectionInitialization.Password:
|
||||
outMsg.Write(pendingClient.PasswordSalt == null); outMsg.WritePadBits();
|
||||
if (pendingClient.PasswordSalt == null)
|
||||
structToSend = new ServerPeerPasswordPacket
|
||||
{
|
||||
pendingClient.PasswordSalt = Lidgren.Network.CryptoRandom.Instance.Next();
|
||||
outMsg.Write(pendingClient.PasswordSalt.Value);
|
||||
}
|
||||
else
|
||||
Salt = GetSalt(pendingClient),
|
||||
RetriesLeft = Option<int>.Some(pendingClient.Retries)
|
||||
};
|
||||
|
||||
static Option<int> GetSalt(PendingClient client)
|
||||
{
|
||||
outMsg.Write(pendingClient.Retries);
|
||||
if (client.PasswordSalt is { } salt) { return Option<int>.Some(salt); }
|
||||
|
||||
salt = Lidgren.Network.CryptoRandom.Instance.Next();
|
||||
client.PasswordSalt = salt;
|
||||
return Option<int>.Some(salt);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
SendMsgInternal(pendingClient.Connection, DeliveryMethod.Reliable, outMsg);
|
||||
SendMsgInternal(pendingClient.Connection, headers, structToSend);
|
||||
}
|
||||
|
||||
protected virtual void CheckOwnership(PendingClient pendingClient) { }
|
||||
|
||||
public void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
|
||||
protected void RemovePendingClient(PendingClient pendingClient, PeerDisconnectPacket peerDisconnectPacket)
|
||||
{
|
||||
if (pendingClients.Contains(pendingClient))
|
||||
{
|
||||
Disconnect(pendingClient.Connection, reason + "/" + msg);
|
||||
Disconnect(pendingClient.Connection, peerDisconnectPacket);
|
||||
|
||||
pendingClients.Remove(pendingClient);
|
||||
|
||||
if (pendingClient.AuthSessionStarted)
|
||||
if (pendingClient.AuthSessionStarted && pendingClient.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId })
|
||||
{
|
||||
Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value);
|
||||
pendingClient.SteamID = null;
|
||||
pendingClient.OwnerSteamID = null;
|
||||
Steam.SteamManager.StopAuthSession(steamId);
|
||||
pendingClient.Connection.SetAccountInfo(AccountInfo.None);
|
||||
pendingClient.AuthSessionStarted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true);
|
||||
public abstract void Disconnect(NetworkConnection conn, string msg = null);
|
||||
public abstract void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
+148
-147
@@ -1,70 +1,61 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class SteamP2PServerPeer : ServerPeer
|
||||
internal sealed class SteamP2PServerPeer : ServerPeer
|
||||
{
|
||||
private bool started;
|
||||
|
||||
public UInt64 OwnerSteamID
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
private readonly SteamId ownerSteamId;
|
||||
|
||||
private UInt64 ownerKey64 => unchecked((UInt64)ownerKey.Value);
|
||||
|
||||
private UInt64 ReadSteamId(IReadMessage inc)
|
||||
=> inc.ReadUInt64() ^ ownerKey64;
|
||||
private void WriteSteamId(IWriteMessage msg, UInt64 val)
|
||||
=> msg.Write(val ^ ownerKey64);
|
||||
private UInt64 ownerKey64 => unchecked((UInt64)ownerKey.Fallback(0));
|
||||
|
||||
public SteamP2PServerPeer(UInt64 steamId, int ownerKey, ServerSettings settings)
|
||||
private SteamId ReadSteamId(IReadMessage inc) => new SteamId(inc.ReadUInt64() ^ ownerKey64);
|
||||
private void WriteSteamId(IWriteMessage msg, SteamId val) => msg.WriteUInt64(val.Value ^ ownerKey64);
|
||||
|
||||
public SteamP2PServerPeer(SteamId steamId, int ownerKey, ServerSettings settings, Callbacks callbacks) : base(callbacks)
|
||||
{
|
||||
serverSettings = settings;
|
||||
|
||||
connectedClients = new List<NetworkConnection>();
|
||||
pendingClients = new List<PendingClient>();
|
||||
|
||||
this.ownerKey = ownerKey;
|
||||
this.ownerKey = Option<int>.Some(ownerKey);
|
||||
|
||||
OwnerSteamID = steamId;
|
||||
ownerSteamId = steamId;
|
||||
|
||||
started = false;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
WriteSteamId(outMsg, OwnerSteamID);
|
||||
outMsg.Write((byte)DeliveryMethod.Reliable);
|
||||
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep | PacketHeader.IsServerMessage));
|
||||
|
||||
byte[] msgToSend = (byte[])outMsg.Buffer.Clone();
|
||||
Array.Resize(ref msgToSend, outMsg.LengthBytes);
|
||||
ChildServerRelay.Write(msgToSend);
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsConnectionInitializationStep | PacketHeader.IsServerMessage,
|
||||
Initialization = null
|
||||
};
|
||||
SendMsgInternal(ownerSteamId, headers, null);
|
||||
|
||||
started = true;
|
||||
}
|
||||
|
||||
public override void Close(string msg = null)
|
||||
public override void Close()
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
if (OwnerConnection != null) OwnerConnection.Status = NetworkConnectionStatus.Disconnected;
|
||||
if (OwnerConnection != null) { OwnerConnection.Status = NetworkConnectionStatus.Disconnected; }
|
||||
|
||||
for (int i = pendingClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
RemovePendingClient(pendingClients[i], DisconnectReason.ServerShutdown, msg);
|
||||
RemovePendingClient(pendingClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
|
||||
}
|
||||
|
||||
for (int i = connectedClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Disconnect(connectedClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
|
||||
Disconnect(connectedClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
|
||||
}
|
||||
|
||||
pendingClients.Clear();
|
||||
@@ -72,27 +63,21 @@ namespace Barotrauma.Networking
|
||||
|
||||
ChildServerRelay.ShutDown();
|
||||
|
||||
OnShutdown?.Invoke();
|
||||
callbacks.OnShutdown.Invoke();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
if (OnOwnerDetermined != null && OwnerConnection != null)
|
||||
{
|
||||
OnOwnerDetermined?.Invoke(OwnerConnection);
|
||||
OnOwnerDetermined = null;
|
||||
}
|
||||
|
||||
//backwards for loop so we can remove elements while iterating
|
||||
for (int i = connectedClients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
SteamP2PConnection conn = connectedClients[i] as SteamP2PConnection;
|
||||
SteamP2PConnection conn = (SteamP2PConnection)connectedClients[i];
|
||||
conn.Decay(deltaTime);
|
||||
if (conn.Timeout < 0.0)
|
||||
{
|
||||
Disconnect(conn, "Timed out");
|
||||
Disconnect(conn, PeerDisconnectPacket.WithReason(DisconnectReason.Timeout));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +94,7 @@ namespace Barotrauma.Networking
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
|
||||
GameAnalyticsManager.AddErrorEventOnce("SteamP2PServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce($"SteamP2PServerPeer.Update:ClientReadException{e.TargetSite}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#else
|
||||
@@ -124,130 +109,132 @@ namespace Barotrauma.Networking
|
||||
if (i >= pendingClients.Count || pendingClients[i] != pendingClient) { i--; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void HandleDataMessage(IReadMessage inc)
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
UInt64 senderSteamId = ReadSteamId(inc);
|
||||
UInt64 ownerSteamId = ReadSteamId(inc);
|
||||
SteamId senderSteamId = ReadSteamId(inc);
|
||||
SteamId sentOwnerSteamId = ReadSteamId(inc);
|
||||
|
||||
var (deliveryMethod, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
|
||||
|
||||
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
|
||||
|
||||
if (packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString());
|
||||
DebugConsole.ThrowError($"Got server message from {senderSteamId}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (senderSteamId != OwnerSteamID) //sender is remote, handle disconnects and heartbeats
|
||||
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) as SteamP2PConnection;
|
||||
|
||||
bool connectionMatches(NetworkConnection conn) =>
|
||||
conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var steamId } }
|
||||
&& steamId == senderSteamId;
|
||||
|
||||
PendingClient? pendingClient = pendingClients.Find(c => connectionMatches(c.Connection));
|
||||
SteamP2PConnection? connectedClient = connectedClients.Find(connectionMatches) as SteamP2PConnection;
|
||||
|
||||
pendingClient?.Heartbeat();
|
||||
connectedClient?.Heartbeat();
|
||||
|
||||
string banReason;
|
||||
if (serverSettings.BanList.IsBanned(senderSteamId, out banReason) ||
|
||||
serverSettings.BanList.IsBanned(ownerSteamId, out banReason))
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
if (!initialization.HasValue) { return; }
|
||||
ConnectionInitialization initializationStep = initialization.Value;
|
||||
|
||||
if (pendingClient != null)
|
||||
{
|
||||
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
|
||||
ReadConnectionInitializationStep(
|
||||
pendingClient,
|
||||
new ReadWriteMessage(inc.Buffer, inc.BitPosition, inc.LengthBits, false),
|
||||
initializationStep);
|
||||
}
|
||||
else if (initializationStep == ConnectionInitialization.ConnectionStarted)
|
||||
{
|
||||
pendingClient = new PendingClient(new SteamP2PConnection(senderSteamId));
|
||||
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
|
||||
pendingClients.Add(pendingClient);
|
||||
}
|
||||
}
|
||||
else if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason) ||
|
||||
serverSettings.BanList.IsBanned(sentOwnerSteamId, out banReason))
|
||||
{
|
||||
if (pendingClient != null)
|
||||
{
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
|
||||
}
|
||||
else if (connectedClient != null)
|
||||
{
|
||||
Disconnect(connectedClient, DisconnectReason.Banned.ToString() + "/ "+ banReason);
|
||||
Disconnect(connectedClient, PeerDisconnectPacket.Banned(banReason));
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
if (pendingClient != null)
|
||||
{
|
||||
string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}";
|
||||
RemovePendingClient(pendingClient, DisconnectReason.Unknown, disconnectMsg);
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
||||
}
|
||||
else if (connectedClient != null)
|
||||
{
|
||||
string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={connectedClient.Name}";
|
||||
Disconnect(connectedClient, disconnectMsg, false);
|
||||
Disconnect(connectedClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (packetHeader.IsHeartbeatMessage())
|
||||
{
|
||||
//message exists solely as a heartbeat, ignore its contents
|
||||
return;
|
||||
}
|
||||
else if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
|
||||
if (pendingClient != null)
|
||||
{
|
||||
if (ownerSteamId != 0)
|
||||
{
|
||||
pendingClient.Connection.SetOwnerSteamIDIfUnknown(ownerSteamId);
|
||||
}
|
||||
ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
|
||||
if (initializationStep == ConnectionInitialization.ConnectionStarted)
|
||||
{
|
||||
pendingClients.Add(new PendingClient(new SteamP2PConnection("PENDING", senderSteamId)) { SteamID = senderSteamId });
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (connectedClient != null)
|
||||
{
|
||||
UInt16 length = inc.ReadUInt16();
|
||||
|
||||
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, connectedClient);
|
||||
OnMessageReceived?.Invoke(connectedClient, msg);
|
||||
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
|
||||
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, connectedClient);
|
||||
callbacks.OnMessageReceived.Invoke(connectedClient, msg);
|
||||
}
|
||||
}
|
||||
else //sender is owner
|
||||
{
|
||||
if (OwnerConnection != null) { (OwnerConnection as SteamP2PConnection).Heartbeat(); }
|
||||
(OwnerConnection as SteamP2PConnection)?.Heartbeat();
|
||||
|
||||
if (packetHeader.IsDisconnectMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received disconnect message from owner");
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetHeader.IsServerMessage())
|
||||
{
|
||||
DebugConsole.ThrowError("Received server message from owner");
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetHeader.IsConnectionInitializationStep())
|
||||
{
|
||||
if (OwnerConnection == null)
|
||||
if (OwnerConnection is null)
|
||||
{
|
||||
string ownerName = inc.ReadString();
|
||||
OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID)
|
||||
var packet = INetSerializableStruct.Read<SteamP2PInitializationOwnerPacket>(inc);
|
||||
OwnerConnection = new SteamP2PConnection(ownerSteamId)
|
||||
{
|
||||
Language = GameSettings.CurrentConfig.Language
|
||||
};
|
||||
OwnerConnection.SetOwnerSteamIDIfUnknown(OwnerSteamID);
|
||||
OwnerConnection.SetAccountInfo(new AccountInfo(ownerSteamId, ownerSteamId));
|
||||
|
||||
OnInitializationComplete?.Invoke(OwnerConnection);
|
||||
callbacks.OnInitializationComplete.Invoke(OwnerConnection, packet.OwnerName);
|
||||
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetHeader.IsHeartbeatMessage())
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt16 length = inc.ReadUInt16();
|
||||
|
||||
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, OwnerConnection);
|
||||
OnMessageReceived?.Invoke(OwnerConnection, msg);
|
||||
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
|
||||
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, OwnerConnection);
|
||||
callbacks.OnMessageReceived.Invoke(OwnerConnection!, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,90 +243,104 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
throw new InvalidOperationException("Called InitializeSteamServerCallbacks on SteamP2PServerPeer!");
|
||||
}
|
||||
|
||||
|
||||
public override void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true)
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
if (!(conn is SteamP2PConnection steamp2pConn)) return;
|
||||
if (!connectedClients.Contains(steamp2pConn) && conn != OwnerConnection)
|
||||
if (conn is not SteamP2PConnection steamP2PConn) { return; }
|
||||
|
||||
if (!connectedClients.Contains(steamP2PConn) && conn != OwnerConnection)
|
||||
{
|
||||
DebugConsole.ThrowError("Tried to send message to unauthenticated connection: " + steamp2pConn.SteamID.ToString());
|
||||
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {steamP2PConn.AccountInfo.AccountId}");
|
||||
return;
|
||||
}
|
||||
|
||||
IWriteMessage msgToSend = new WriteOnlyMessage();
|
||||
byte[] msgData = new byte[16];
|
||||
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
|
||||
WriteSteamId(msgToSend, conn.SteamID);
|
||||
msgToSend.Write((byte)deliveryMethod);
|
||||
msgToSend.Write((byte)((isCompressed ? PacketHeader.IsCompressed : PacketHeader.None) | PacketHeader.IsServerMessage));
|
||||
msgToSend.Write((UInt16)length);
|
||||
msgToSend.Write(msgData, 0, length);
|
||||
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || connAccountId is not SteamId) { return; }
|
||||
|
||||
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
|
||||
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
|
||||
ChildServerRelay.Write(bufToSend);
|
||||
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
|
||||
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = deliveryMethod,
|
||||
PacketHeader = (isCompressed ? PacketHeader.IsCompressed : PacketHeader.None)
|
||||
| PacketHeader.IsServerMessage,
|
||||
Initialization = null
|
||||
};
|
||||
var body = new PeerPacketMessage
|
||||
{
|
||||
Buffer = bufAux
|
||||
};
|
||||
SendMsgInternal(steamP2PConn, headers, body);
|
||||
}
|
||||
|
||||
private void SendDisconnectMessage(UInt64 steamId, string msg)
|
||||
{
|
||||
if (!started) { return; }
|
||||
if (string.IsNullOrWhiteSpace(msg)) { return; }
|
||||
|
||||
IWriteMessage msgToSend = new WriteOnlyMessage();
|
||||
WriteSteamId(msgToSend, steamId);
|
||||
msgToSend.Write((byte)DeliveryMethod.Reliable);
|
||||
msgToSend.Write((byte)(PacketHeader.IsDisconnectMessage | PacketHeader.IsServerMessage));
|
||||
msgToSend.Write(msg);
|
||||
|
||||
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
|
||||
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
|
||||
ChildServerRelay.Write(bufToSend);
|
||||
}
|
||||
|
||||
private void Disconnect(NetworkConnection conn, string msg, bool sendDisconnectMessage)
|
||||
private void SendDisconnectMessage(SteamId steamId, PeerDisconnectPacket peerDisconnectPacket)
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
if (!(conn is SteamP2PConnection steamp2pConn)) { return; }
|
||||
if (sendDisconnectMessage) { SendDisconnectMessage(steamp2pConn.SteamID, msg); }
|
||||
var headers = new PeerPacketHeaders
|
||||
{
|
||||
DeliveryMethod = DeliveryMethod.Reliable,
|
||||
PacketHeader = PacketHeader.IsDisconnectMessage | PacketHeader.IsServerMessage,
|
||||
Initialization = null
|
||||
};
|
||||
|
||||
SendMsgInternal(steamId, headers, peerDisconnectPacket);
|
||||
}
|
||||
|
||||
public override void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket)
|
||||
{
|
||||
if (!started) { return; }
|
||||
|
||||
if (conn is not SteamP2PConnection steamp2pConn) { return; }
|
||||
|
||||
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || connAccountId is not SteamId connSteamId) { return; }
|
||||
|
||||
SendDisconnectMessage(connSteamId, peerDisconnectPacket);
|
||||
|
||||
if (connectedClients.Contains(steamp2pConn))
|
||||
{
|
||||
steamp2pConn.Status = NetworkConnectionStatus.Disconnected;
|
||||
connectedClients.Remove(steamp2pConn);
|
||||
OnDisconnect?.Invoke(conn, msg);
|
||||
Steam.SteamManager.StopAuthSession(conn.SteamID);
|
||||
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
|
||||
Steam.SteamManager.StopAuthSession(connSteamId);
|
||||
}
|
||||
else if (steamp2pConn == OwnerConnection)
|
||||
{
|
||||
//TODO: fix?
|
||||
throw new InvalidOperationException("Cannot disconnect owner peer");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Disconnect(NetworkConnection conn, string msg = null)
|
||||
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
|
||||
{
|
||||
Disconnect(conn, msg, true);
|
||||
}
|
||||
var connSteamId = conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var id } } ? id : null;
|
||||
if (connSteamId is null) { return; }
|
||||
|
||||
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
|
||||
SendMsgInternal(connSteamId, headers, body);
|
||||
}
|
||||
|
||||
private void SendMsgInternal(SteamId connSteamId, PeerPacketHeaders headers, INetSerializableStruct? body)
|
||||
{
|
||||
IWriteMessage msgToSend = new WriteOnlyMessage();
|
||||
WriteSteamId(msgToSend, 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);
|
||||
WriteSteamId(msgToSend, connSteamId);
|
||||
msgToSend.WriteNetSerializableStruct(headers);
|
||||
body?.Write(msgToSend);
|
||||
|
||||
ForwardToOwnerProcess(msgToSend);
|
||||
}
|
||||
|
||||
private static void ForwardToOwnerProcess(IWriteMessage msg)
|
||||
{
|
||||
byte[] bufToSend = (byte[])msg.Buffer.Clone();
|
||||
Array.Resize(ref bufToSend, msg.LengthBytes);
|
||||
ChildServerRelay.Write(bufToSend);
|
||||
}
|
||||
|
||||
protected override void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket)
|
||||
protected override void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient)
|
||||
{
|
||||
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
|
||||
|
||||
pendingClient.Connection.Name = name;
|
||||
pendingClient.Name = name;
|
||||
pendingClient.Name = packet.Name;
|
||||
pendingClient.AuthSessionStarted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,6 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
partial class RespawnManager : Entity, IServerSerializable
|
||||
{
|
||||
/// <summary>
|
||||
/// How much skills drop towards the job's default skill levels when respawning midround in the campaign
|
||||
/// </summary>
|
||||
const float SkillReductionOnCampaignMidroundRespawn = 0.75f;
|
||||
|
||||
private DateTime despawnTime;
|
||||
|
||||
private float shuttleEmptyTimer;
|
||||
@@ -132,7 +127,7 @@ namespace Barotrauma.Networking
|
||||
return characterToRespawnCount >= GetMinCharactersToRespawn();
|
||||
}
|
||||
|
||||
partial void UpdateWaiting(float deltaTime)
|
||||
partial void UpdateWaiting(float _)
|
||||
{
|
||||
if (RespawnShuttle != null)
|
||||
{
|
||||
@@ -465,26 +460,28 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
clients[i].Character = character;
|
||||
character.OwnerClientEndPoint = clients[i].Connection.EndPointString;
|
||||
character.OwnerClientAddress = clients[i].Connection.Endpoint.Address;
|
||||
character.OwnerClientName = clients[i].Name;
|
||||
GameServer.Log(string.Format("Respawning {0} ({1}) as {2}", GameServer.ClientLogName(clients[i]), clients[i].Connection?.EndPointString, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
|
||||
GameServer.Log(
|
||||
$"Respawning {GameServer.ClientLogName(clients[i])} ({clients[i].Connection.Endpoint}) as {characterInfos[i].Job.Name}", ServerLog.MessageType.Spawning);
|
||||
}
|
||||
|
||||
if (RespawnShuttle != null)
|
||||
{
|
||||
Vector2 pos = cargoSp == null ? character.Position : cargoSp.Position;
|
||||
List<Item> newRespawnItems = new List<Item>();
|
||||
Vector2 pos = cargoSp?.Position ?? character.Position;
|
||||
if (divingSuitPrefab != null)
|
||||
{
|
||||
var divingSuit = new Item(divingSuitPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(divingSuit));
|
||||
respawnItems.Add(divingSuit);
|
||||
newRespawnItems.Add(divingSuit);
|
||||
|
||||
if (oxyPrefab != null && divingSuit.GetComponent<ItemContainer>() != null)
|
||||
{
|
||||
var oxyTank = new Item(oxyPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(oxyTank));
|
||||
divingSuit.Combine(oxyTank, user: null);
|
||||
respawnItems.Add(oxyTank);
|
||||
newRespawnItems.Add(oxyTank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,13 +491,13 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
var scooter = new Item(scooterPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(scooter));
|
||||
respawnItems.Add(scooter);
|
||||
newRespawnItems.Add(scooter);
|
||||
if (batteryPrefab != null)
|
||||
{
|
||||
var battery = new Item(batteryPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(battery));
|
||||
scooter.Combine(battery, user: null);
|
||||
respawnItems.Add(battery);
|
||||
newRespawnItems.Add(battery);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -508,14 +505,31 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (shuttleItem.NonInteractable || shuttleItem.NonPlayerTeamInteractable) { continue; }
|
||||
var container = shuttleItem.GetComponent<ItemContainer>();
|
||||
if (container != null && container.Inventory.TryPutItem(respawnItem, user: null))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
respawnItems.Add(respawnItem);
|
||||
}
|
||||
}
|
||||
|
||||
var characterData = campaign?.GetClientCharacterData(clients[i]);
|
||||
if (characterData != null && Level.Loaded?.Type != LevelData.LevelType.Outpost && characterData.HasSpawned)
|
||||
{
|
||||
//we need to reapply the previous respawn penalty affliction or successive deaths won't make it stack
|
||||
characterData.ApplyHealthData(character, (AfflictionPrefab ap) => ap == GetRespawnPenaltyAfflictionPrefab());
|
||||
GiveRespawnPenaltyAffliction(character);
|
||||
}
|
||||
|
||||
if (characterData == null || characterData.HasSpawned)
|
||||
{
|
||||
//give the character the items they would've gotten if they had spawned in the main sub
|
||||
@@ -563,8 +577,8 @@ namespace Barotrauma.Networking
|
||||
foreach (Skill skill in characterInfo.Job.GetSkills())
|
||||
{
|
||||
var skillPrefab = characterInfo.Job.Prefab.Skills.Find(s => skill.Identifier == s.Identifier);
|
||||
if (skillPrefab == null) { continue; }
|
||||
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.Start, SkillReductionOnCampaignMidroundRespawn);
|
||||
if (skillPrefab == null || skill.Level < skillPrefab.LevelRange.End) { continue; }
|
||||
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.End, SkillReductionOnDeath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,20 +589,20 @@ namespace Barotrauma.Networking
|
||||
switch (CurrentState)
|
||||
{
|
||||
case State.Transporting:
|
||||
msg.Write(ReturnCountdownStarted);
|
||||
msg.Write(GameMain.Server.ServerSettings.MaxTransportTime);
|
||||
msg.Write((float)(ReturnTime - DateTime.Now).TotalSeconds);
|
||||
msg.WriteBoolean(ReturnCountdownStarted);
|
||||
msg.WriteSingle(GameMain.Server.ServerSettings.MaxTransportTime);
|
||||
msg.WriteSingle((float)(ReturnTime - DateTime.Now).TotalSeconds);
|
||||
break;
|
||||
case State.Waiting:
|
||||
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
|
||||
var matchingData = campaign?.GetClientCharacterData(c);
|
||||
bool forceSpawnInMainSub = matchingData != null && !matchingData.HasSpawned;
|
||||
msg.Write((ushort)pendingRespawnCount);
|
||||
msg.Write((ushort)requiredRespawnCount);
|
||||
msg.Write(IsRespawnPromptPendingForClient(c));
|
||||
msg.Write(RespawnCountdownStarted);
|
||||
msg.Write(forceSpawnInMainSub);
|
||||
msg.Write((float)(RespawnTime - DateTime.Now).TotalSeconds);
|
||||
msg.WriteUInt16((ushort)pendingRespawnCount);
|
||||
msg.WriteUInt16((ushort)requiredRespawnCount);
|
||||
msg.WriteBoolean(IsRespawnPromptPendingForClient(c));
|
||||
msg.WriteBoolean(RespawnCountdownStarted);
|
||||
msg.WriteBoolean(forceSpawnInMainSub);
|
||||
msg.WriteSingle((float)(RespawnTime - DateTime.Now).TotalSeconds);
|
||||
break;
|
||||
case State.Returning:
|
||||
break;
|
||||
|
||||
@@ -18,29 +18,39 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!PropEquals(lastSyncedValue, Value))
|
||||
{
|
||||
LastUpdateID = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
LastUpdateID = GameMain.NetLobbyScreen.LastUpdateID;
|
||||
lastSyncedValue = Value;
|
||||
}
|
||||
}
|
||||
public void ForceUpdate()
|
||||
{
|
||||
LastUpdateID = GameMain.NetLobbyScreen.LastUpdateID++;
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml";
|
||||
public static readonly char SubmarineSeparatorChar = '|';
|
||||
|
||||
public readonly Dictionary<NetFlags, UInt16> LastUpdateIdForFlag = new Dictionary<NetFlags, UInt16>();
|
||||
public UInt16 LastPropertyUpdateId { get; private set; } = 1;
|
||||
|
||||
public readonly Dictionary<NetFlags, UInt16> LastUpdateIdForFlag
|
||||
= ((NetFlags[])Enum.GetValues(typeof(NetFlags)))
|
||||
.Select(f => (f, (ushort)1))
|
||||
.ToDictionary();
|
||||
|
||||
public void UpdateFlag(NetFlags flag)
|
||||
=> LastUpdateIdForFlag[flag] = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID + 1);
|
||||
|
||||
public NetFlags UnsentFlags()
|
||||
=> LastUpdateIdForFlag.Keys
|
||||
.Where(k => NetIdUtils.IdMoreRecent(LastUpdateIdForFlag[k], GameMain.NetLobbyScreen.LastUpdateID))
|
||||
.Aggregate(NetFlags.None, (f1, f2) => f1 | f2);
|
||||
|
||||
private bool IsFlagRequired(Client c, NetFlags flag)
|
||||
=> NetIdUtils.IdMoreRecent(LastUpdateIdForFlag[flag], c.LastRecvLobbyUpdate);
|
||||
|
||||
public NetFlags GetRequiredFlags(Client c)
|
||||
=> LastUpdateIdForFlag.Keys
|
||||
.Where(k => IsFlagRequired(c, k))
|
||||
.Concat(NetFlags.None.ToEnumerable()) //prevents InvalidOperationException in Aggregate
|
||||
.Aggregate((f1, f2) => f1 | f2);
|
||||
.Aggregate(NetFlags.None, (f1, f2) => f1 | f2);
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
@@ -48,6 +58,15 @@ namespace Barotrauma.Networking
|
||||
LoadClientPermissions();
|
||||
}
|
||||
|
||||
public void ForcePropertyUpdate()
|
||||
{
|
||||
UpdateFlag(NetFlags.Properties);
|
||||
foreach (NetPropertyData property in netProperties.Values)
|
||||
{
|
||||
property.ForceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteNetProperties(IWriteMessage outMsg, Client c)
|
||||
{
|
||||
foreach (UInt32 key in netProperties.Keys)
|
||||
@@ -56,40 +75,39 @@ namespace Barotrauma.Networking
|
||||
property.SyncValue();
|
||||
if (NetIdUtils.IdMoreRecent(property.LastUpdateID, c.LastRecvLobbyUpdate))
|
||||
{
|
||||
outMsg.Write(key);
|
||||
outMsg.WriteUInt32(key);
|
||||
netProperties[key].Write(outMsg);
|
||||
}
|
||||
}
|
||||
outMsg.Write((UInt32)0);
|
||||
outMsg.WriteUInt32((UInt32)0);
|
||||
}
|
||||
|
||||
public void ServerAdminWrite(IWriteMessage outMsg, Client c)
|
||||
{
|
||||
c.LastSentServerSettingsUpdate = LastPropertyUpdateId;
|
||||
c.LastSentServerSettingsUpdate = LastUpdateIdForFlag[NetFlags.Properties];
|
||||
WriteNetProperties(outMsg, c);
|
||||
WriteMonsterEnabled(outMsg);
|
||||
BanList.ServerAdminWrite(outMsg, c);
|
||||
Whitelist.ServerAdminWrite(outMsg, c);
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage outMsg, Client c)
|
||||
{
|
||||
NetFlags requiredFlags = GetRequiredFlags(c);
|
||||
outMsg.Write((byte)requiredFlags);
|
||||
outMsg.WriteByte((byte)requiredFlags);
|
||||
if (requiredFlags.HasFlag(NetFlags.Name))
|
||||
{
|
||||
outMsg.Write(ServerName);
|
||||
outMsg.WriteString(ServerName);
|
||||
}
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.Message))
|
||||
{
|
||||
outMsg.Write(ServerMessageText);
|
||||
outMsg.WriteString(ServerMessageText);
|
||||
}
|
||||
outMsg.Write((byte)PlayStyle);
|
||||
outMsg.Write((byte)MaxPlayers);
|
||||
outMsg.Write(HasPassword);
|
||||
outMsg.Write(IsPublic);
|
||||
outMsg.Write(AllowFileTransfers);
|
||||
outMsg.WriteByte((byte)PlayStyle);
|
||||
outMsg.WriteByte((byte)MaxPlayers);
|
||||
outMsg.WriteBoolean(HasPassword);
|
||||
outMsg.WriteBoolean(IsPublic);
|
||||
outMsg.WriteBoolean(AllowFileTransfers);
|
||||
outMsg.WritePadBits();
|
||||
outMsg.WriteRangedInteger(TickRate, 1, 60);
|
||||
|
||||
@@ -104,16 +122,18 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
if (c.HasPermission(Networking.ClientPermissions.ManageSettings)
|
||||
&& !NetIdUtils.IdMoreRecentOrMatches(c.LastRecvServerSettingsUpdate, LastPropertyUpdateId))
|
||||
&& NetIdUtils.IdMoreRecent(
|
||||
newID: LastUpdateIdForFlag[NetFlags.Properties],
|
||||
oldID: c.LastRecvServerSettingsUpdate))
|
||||
{
|
||||
outMsg.Write(true);
|
||||
outMsg.WriteBoolean(true);
|
||||
outMsg.WritePadBits();
|
||||
|
||||
ServerAdminWrite(outMsg, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
outMsg.Write(false);
|
||||
outMsg.WriteBoolean(false);
|
||||
outMsg.WritePadBits();
|
||||
}
|
||||
}
|
||||
@@ -171,12 +191,10 @@ namespace Barotrauma.Networking
|
||||
propertiesChanged |= changedMonsterSettings;
|
||||
if (changedMonsterSettings) { ReadMonsterEnabled(incMsg); }
|
||||
propertiesChanged |= BanList.ServerAdminRead(incMsg, c);
|
||||
propertiesChanged |= Whitelist.ServerAdminRead(incMsg, c);
|
||||
|
||||
if (propertiesChanged)
|
||||
{
|
||||
UpdateFlag(NetFlags.Properties);
|
||||
LastPropertyUpdateId = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID + 1);
|
||||
}
|
||||
changed |= propertiesChanged;
|
||||
}
|
||||
@@ -192,27 +210,32 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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 = (Barotrauma.MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);
|
||||
GameMain.NetLobbyScreen.MissionType = (MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);
|
||||
|
||||
int traitorSetting = (int)TraitorsEnabled + incMsg.ReadByte() - 1;
|
||||
if (traitorSetting < 0) traitorSetting = 2;
|
||||
if (traitorSetting > 2) traitorSetting = 0;
|
||||
if (traitorSetting < 0) { traitorSetting = 2; }
|
||||
if (traitorSetting > 2) { traitorSetting = 0; }
|
||||
TraitorsEnabled = (YesNoMaybe)traitorSetting;
|
||||
|
||||
int botCount = BotCount + incMsg.ReadByte() - 1;
|
||||
if (botCount < 0) botCount = MaxBotCount;
|
||||
if (botCount > MaxBotCount) botCount = 0;
|
||||
if (botCount < 0) { botCount = MaxBotCount; }
|
||||
if (botCount > MaxBotCount) { botCount = 0; }
|
||||
BotCount = botCount;
|
||||
|
||||
int botSpawnMode = (int)BotSpawnMode + incMsg.ReadByte() - 1;
|
||||
if (botSpawnMode < 0) botSpawnMode = 1;
|
||||
if (botSpawnMode > 1) botSpawnMode = 0;
|
||||
if (botSpawnMode < 0) { botSpawnMode = 1; }
|
||||
if (botSpawnMode > 1) { botSpawnMode = 0; }
|
||||
BotSpawnMode = (BotSpawnMode)botSpawnMode;
|
||||
|
||||
float levelDifficulty = incMsg.ReadSingle();
|
||||
if (levelDifficulty >= 0.0f) SelectedLevelDifficulty = levelDifficulty;
|
||||
if (levelDifficulty >= 0.0f) { SelectedLevelDifficulty = levelDifficulty; }
|
||||
|
||||
UseRespawnShuttle = incMsg.ReadBoolean();
|
||||
bool changedUseRespawnShuttle = incMsg.ReadBoolean();
|
||||
bool useRespawnShuttle = incMsg.ReadBoolean();
|
||||
if (changedUseRespawnShuttle)
|
||||
{
|
||||
UseRespawnShuttle = useRespawnShuttle;
|
||||
}
|
||||
|
||||
bool changedAutoRestart = incMsg.ReadBoolean();
|
||||
bool autoRestart = incMsg.ReadBoolean();
|
||||
@@ -444,31 +467,27 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ClientPermissions.Clear();
|
||||
|
||||
if (!File.Exists(ClientPermissionsFile))
|
||||
{
|
||||
if (File.Exists("Data/clientpermissions.txt"))
|
||||
{
|
||||
LoadClientPermissionsOld("Data/clientpermissions.txt");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!File.Exists(ClientPermissionsFile)) { return; }
|
||||
|
||||
XDocument doc = XMLExtensions.TryLoadXml(ClientPermissionsFile);
|
||||
if (doc == null) { return; }
|
||||
foreach (XElement clientElement in doc.Root.Elements())
|
||||
{
|
||||
string clientName = clientElement.GetAttributeString("name", "");
|
||||
string clientEndPoint = clientElement.GetAttributeString("endpoint", null) ?? clientElement.GetAttributeString("ip", "");
|
||||
string steamIdStr = clientElement.GetAttributeString("steamid", "");
|
||||
string addressStr = clientElement.GetAttributeString("address", null)
|
||||
?? clientElement.GetAttributeString("endpoint", null)
|
||||
?? clientElement.GetAttributeString("ip", "");
|
||||
string accountIdStr = clientElement.GetAttributeString("accountid", null)
|
||||
?? clientElement.GetAttributeString("steamid", "");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(clientName))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have a name and an IP address.");
|
||||
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have a name.");
|
||||
continue;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(clientEndPoint) && string.IsNullOrWhiteSpace(steamIdStr))
|
||||
if (string.IsNullOrWhiteSpace(addressStr) && string.IsNullOrWhiteSpace(accountIdStr))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have an IP address or a Steam ID.");
|
||||
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have an endpoint or a Steam ID.");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -542,69 +561,33 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(steamIdStr))
|
||||
if (!string.IsNullOrEmpty(accountIdStr))
|
||||
{
|
||||
if (ulong.TryParse(steamIdStr, out ulong steamID))
|
||||
if (AccountId.Parse(accountIdStr).TryUnwrap(out var accountId))
|
||||
{
|
||||
ClientPermissions.Add(new SavedClientPermission(clientName, steamID, permissions, permittedCommands));
|
||||
ClientPermissions.Add(new SavedClientPermission(clientName, accountId, permissions, permittedCommands));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + steamIdStr + "\" is not a valid Steam ID.");
|
||||
continue;
|
||||
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + accountIdStr + "\" is not a valid account ID.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientPermissions.Add(new SavedClientPermission(clientName, clientEndPoint, permissions, permittedCommands));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method for loading old .txt client permission files to provide backwards compatibility
|
||||
/// </summary>
|
||||
private void LoadClientPermissionsOld(string file)
|
||||
{
|
||||
if (!File.Exists(file)) return;
|
||||
|
||||
string[] lines;
|
||||
try
|
||||
{
|
||||
lines = File.ReadAllLines(file);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to open client permission file " + ClientPermissionsFile, e);
|
||||
return;
|
||||
}
|
||||
|
||||
ClientPermissions.Clear();
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
string[] separatedLine = line.Split('|');
|
||||
if (separatedLine.Length < 3) { continue; }
|
||||
|
||||
string name = string.Join("|", separatedLine.Take(separatedLine.Length - 2));
|
||||
string ip = separatedLine[separatedLine.Length - 2];
|
||||
|
||||
ClientPermissions permissions;
|
||||
if (Enum.TryParse(separatedLine.Last(), out permissions))
|
||||
{
|
||||
ClientPermissions.Add(new SavedClientPermission(name, ip, permissions, new HashSet<DebugConsole.Command>()));
|
||||
if (Address.Parse(addressStr).TryUnwrap(out var address))
|
||||
{
|
||||
ClientPermissions.Add(new SavedClientPermission(clientName, address, permissions, permittedCommands));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + addressStr + "\" is not a valid endpoint.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveClientPermissions()
|
||||
{
|
||||
//delete old client permission file
|
||||
if (File.Exists("Data/clientpermissions.txt"))
|
||||
{
|
||||
File.Delete("Data/clientpermissions.txt");
|
||||
}
|
||||
|
||||
GameServer.Log("Saving client permissions", ServerLog.MessageType.ServerMessage);
|
||||
|
||||
XDocument doc = new XDocument(new XElement("ClientPermissions"));
|
||||
@@ -612,6 +595,7 @@ namespace Barotrauma.Networking
|
||||
foreach (SavedClientPermission clientPermission in ClientPermissions)
|
||||
{
|
||||
var matchingPreset = PermissionPreset.List.Find(p => p.MatchesPermissions(clientPermission.Permissions, clientPermission.PermittedCommands));
|
||||
#warning TODO: this is broken because of localization
|
||||
if (matchingPreset != null && matchingPreset.Name == "None")
|
||||
{
|
||||
continue;
|
||||
@@ -620,23 +604,14 @@ namespace Barotrauma.Networking
|
||||
XElement clientElement = new XElement("Client",
|
||||
new XAttribute("name", clientPermission.Name));
|
||||
|
||||
if (clientPermission.SteamID > 0)
|
||||
{
|
||||
clientElement.Add(new XAttribute("steamid", clientPermission.SteamID));
|
||||
}
|
||||
else
|
||||
{
|
||||
clientElement.Add(new XAttribute("endpoint", clientPermission.EndPoint));
|
||||
}
|
||||
clientElement.Add(clientPermission.AddressOrAccountId.TryGet(out AccountId accountId)
|
||||
? new XAttribute("accountid", accountId.StringRepresentation)
|
||||
: new XAttribute("address", ((Address)clientPermission.AddressOrAccountId).StringRepresentation));
|
||||
|
||||
if (matchingPreset == null)
|
||||
{
|
||||
clientElement.Add(new XAttribute("permissions", clientPermission.Permissions.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
clientElement.Add(new XAttribute("preset", matchingPreset.Name));
|
||||
}
|
||||
clientElement.Add(matchingPreset == null
|
||||
? new XAttribute("permissions", clientPermission.Permissions.ToString())
|
||||
: new XAttribute("preset", matchingPreset.Name));
|
||||
|
||||
if (clientPermission.Permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands))
|
||||
{
|
||||
foreach (DebugConsole.Command command in clientPermission.PermittedCommands)
|
||||
|
||||
@@ -56,8 +56,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
|
||||
msg.Write((byte)ServerPacketHeader.VOICE);
|
||||
msg.Write((byte)queue.QueueID);
|
||||
msg.WriteByte((byte)ServerPacketHeader.VOICE);
|
||||
msg.WriteByte((byte)queue.QueueID);
|
||||
queue.Write(msg);
|
||||
|
||||
netServer.Send(msg, recipient.Connection, DeliveryMethod.Unreliable);
|
||||
|
||||
@@ -216,6 +216,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetVotes(IEnumerable<Client> connectedClients, bool resetKickVotes)
|
||||
{
|
||||
foreach (Client client in connectedClients)
|
||||
{
|
||||
client.ResetVotes(resetKickVotes);
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerRead(IReadMessage inc, Client sender)
|
||||
{
|
||||
if (GameMain.Server == null || sender == null) { return; }
|
||||
@@ -254,15 +262,20 @@ namespace Barotrauma
|
||||
break;
|
||||
case VoteType.Kick:
|
||||
byte kickedClientID = inc.ReadByte();
|
||||
|
||||
Client kicked = GameMain.Server.ConnectedClients.Find(c => c.ID == kickedClientID);
|
||||
if (kicked != null && kicked.Connection != GameMain.Server.OwnerConnection && !kicked.HasKickVoteFrom(sender))
|
||||
if ((DateTime.Now - sender.JoinTime).TotalSeconds < GameMain.Server.ServerSettings.DisallowKickVoteTime)
|
||||
{
|
||||
kicked.AddKickVote(sender);
|
||||
Client.UpdateKickVotes(GameMain.Server.ConnectedClients);
|
||||
GameMain.Server.SendChatMessage($"ServerMessage.HasVotedToKick~[initiator]={sender.Name}~[target]={kicked.Name}", ChatMessageType.Server, null);
|
||||
GameMain.Server.SendDirectChatMessage($"ServerMessage.kickvotedisallowed", sender);
|
||||
}
|
||||
else
|
||||
{
|
||||
Client kicked = GameMain.Server.ConnectedClients.Find(c => c.SessionId == kickedClientID);
|
||||
if (kicked != null && kicked.Connection != GameMain.Server.OwnerConnection && !kicked.HasKickVoteFrom(sender))
|
||||
{
|
||||
kicked.AddKickVote(sender);
|
||||
Client.UpdateKickVotes(GameMain.Server.ConnectedClients);
|
||||
GameMain.Server.SendChatMessage($"ServerMessage.HasVotedToKick~[initiator]={sender.Name}~[target]={kicked.Name}", ChatMessageType.Server, null);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case VoteType.StartRound:
|
||||
bool ready = inc.ReadBoolean();
|
||||
@@ -287,9 +300,9 @@ namespace Barotrauma
|
||||
if (!ShouldRejectVote(sender, voteType))
|
||||
{
|
||||
pendingVotes.Enqueue(new TransferVote(sender,
|
||||
GameMain.Server.ConnectedClients.Find(c => c.ID == fromClientId),
|
||||
GameMain.Server.ConnectedClients.Find(c => c.SessionId == fromClientId),
|
||||
amount,
|
||||
GameMain.Server.ConnectedClients.Find(c => c.ID == toClientId)));
|
||||
GameMain.Server.ConnectedClients.Find(c => c.SessionId == toClientId)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -323,66 +336,66 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.Server == null) { return; }
|
||||
|
||||
msg.Write(GameMain.Server.ServerSettings.AllowSubVoting);
|
||||
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowSubVoting);
|
||||
if (GameMain.Server.ServerSettings.AllowSubVoting)
|
||||
{
|
||||
IReadOnlyDictionary<SubmarineInfo, int> voteList = GetVoteCounts<SubmarineInfo>(VoteType.Sub, GameMain.Server.ConnectedClients);
|
||||
msg.Write((byte)voteList.Count);
|
||||
msg.WriteByte((byte)voteList.Count);
|
||||
foreach (KeyValuePair<SubmarineInfo, int> vote in voteList)
|
||||
{
|
||||
msg.Write((byte)vote.Value);
|
||||
msg.Write(vote.Key.Name);
|
||||
msg.WriteByte((byte)vote.Value);
|
||||
msg.WriteString(vote.Key.Name);
|
||||
}
|
||||
}
|
||||
msg.Write(GameMain.Server.ServerSettings.AllowModeVoting);
|
||||
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowModeVoting);
|
||||
if (GameMain.Server.ServerSettings.AllowModeVoting)
|
||||
{
|
||||
IReadOnlyDictionary<GameModePreset, int> voteList = GetVoteCounts<GameModePreset>(VoteType.Mode, GameMain.Server.ConnectedClients);
|
||||
msg.Write((byte)voteList.Count);
|
||||
msg.WriteByte((byte)voteList.Count);
|
||||
foreach (KeyValuePair<GameModePreset, int> vote in voteList)
|
||||
{
|
||||
msg.Write((byte)vote.Value);
|
||||
msg.Write(vote.Key.Identifier);
|
||||
msg.WriteByte((byte)vote.Value);
|
||||
msg.WriteIdentifier(vote.Key.Identifier);
|
||||
}
|
||||
}
|
||||
msg.Write(GameMain.Server.ServerSettings.AllowEndVoting);
|
||||
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowEndVoting);
|
||||
if (GameMain.Server.ServerSettings.AllowEndVoting)
|
||||
{
|
||||
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound)));
|
||||
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned));
|
||||
msg.WriteByte((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound)));
|
||||
msg.WriteByte((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned));
|
||||
}
|
||||
|
||||
msg.Write(GameMain.Server.ServerSettings.AllowVoteKick);
|
||||
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowVoteKick);
|
||||
|
||||
msg.Write((byte)(ActiveVote?.State ?? VoteState.None));
|
||||
msg.WriteByte((byte)(ActiveVote?.State ?? VoteState.None));
|
||||
if (ActiveVote != null)
|
||||
{
|
||||
msg.Write((byte)ActiveVote.VoteType);
|
||||
msg.WriteByte((byte)ActiveVote.VoteType);
|
||||
if (ActiveVote.State != VoteState.None && ActiveVote.VoteType != VoteType.Unknown)
|
||||
{
|
||||
var eligibleClients = GameMain.Server.ConnectedClients.Where(c => c.InGame && c != ActiveVote.VoteStarter);
|
||||
|
||||
var yesClients = eligibleClients.Where(c => c.GetVote<int>(ActiveVote.VoteType) == 2);
|
||||
msg.Write((byte)yesClients.Count());
|
||||
msg.WriteByte((byte)yesClients.Count());
|
||||
foreach (Client c in yesClients)
|
||||
{
|
||||
msg.Write(c.ID);
|
||||
msg.WriteByte(c.SessionId);
|
||||
}
|
||||
|
||||
var noClients = eligibleClients.Where(c => c.GetVote<int>(ActiveVote.VoteType) == 1);
|
||||
msg.Write((byte)noClients.Count());
|
||||
msg.WriteByte((byte)noClients.Count());
|
||||
foreach (Client c in noClients)
|
||||
{
|
||||
msg.Write(c.ID);
|
||||
msg.WriteByte(c.SessionId);
|
||||
}
|
||||
|
||||
msg.Write((byte)eligibleClients.Count());
|
||||
msg.WriteByte((byte)eligibleClients.Count());
|
||||
|
||||
switch (ActiveVote.State)
|
||||
{
|
||||
case VoteState.Started:
|
||||
msg.Write(ActiveVote.VoteStarter.ID);
|
||||
msg.Write((byte)GameMain.Server.ServerSettings.VoteTimeout);
|
||||
msg.WriteByte(ActiveVote.VoteStarter.SessionId);
|
||||
msg.WriteByte((byte)GameMain.Server.ServerSettings.VoteTimeout);
|
||||
|
||||
switch (ActiveVote.VoteType)
|
||||
{
|
||||
@@ -390,14 +403,14 @@ namespace Barotrauma
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
case VoteType.SwitchSub:
|
||||
SubmarineVote vote = ActiveVote as SubmarineVote;
|
||||
msg.Write(vote.Sub.Name);
|
||||
msg.Write(vote.TransferItems);
|
||||
msg.WriteString(vote.Sub.Name);
|
||||
msg.WriteBoolean(vote.TransferItems);
|
||||
break;
|
||||
case VoteType.TransferMoney:
|
||||
var transferVote = (ActiveVote as TransferVote);
|
||||
msg.Write(transferVote.From?.ID ?? 0);
|
||||
msg.Write(transferVote.To?.ID ?? 0);
|
||||
msg.Write(transferVote.TransferAmount);
|
||||
msg.WriteByte(transferVote.From?.SessionId ?? 0);
|
||||
msg.WriteByte(transferVote.To?.SessionId ?? 0);
|
||||
msg.WriteInt32(transferVote.TransferAmount);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -407,16 +420,16 @@ namespace Barotrauma
|
||||
break;
|
||||
case VoteState.Passed:
|
||||
case VoteState.Failed:
|
||||
msg.Write(ActiveVote.State == VoteState.Passed);
|
||||
msg.WriteBoolean(ActiveVote.State == VoteState.Passed);
|
||||
switch (ActiveVote.VoteType)
|
||||
{
|
||||
case VoteType.PurchaseSub:
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
case VoteType.SwitchSub:
|
||||
var subVote = ActiveVote as SubmarineVote;
|
||||
msg.Write(subVote.Sub.Name);
|
||||
msg.Write(subVote.TransferItems);
|
||||
msg.Write((short)subVote.DeliveryFee);
|
||||
msg.WriteString(subVote.Sub.Name);
|
||||
msg.WriteBoolean(subVote.TransferItems);
|
||||
msg.WriteInt16((short)subVote.DeliveryFee);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -424,11 +437,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
var readyClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote<bool>(VoteType.StartRound));
|
||||
msg.Write((byte)readyClients.Count);
|
||||
var readyClients = GameMain.Server.ConnectedClients.Where(c => c.GetVote<bool>(VoteType.StartRound));
|
||||
msg.WriteByte((byte)readyClients.Count());
|
||||
foreach (Client c in readyClients)
|
||||
{
|
||||
msg.Write(c.ID);
|
||||
msg.WriteByte(c.SessionId);
|
||||
}
|
||||
|
||||
msg.WritePadBits();
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class WhiteListedPlayer
|
||||
{
|
||||
private static UInt16 LastIdentifier = 0;
|
||||
|
||||
public WhiteListedPlayer(string name,string ip)
|
||||
{
|
||||
Name = name;
|
||||
IP = ip;
|
||||
|
||||
UniqueIdentifier = LastIdentifier; LastIdentifier++;
|
||||
}
|
||||
}
|
||||
|
||||
partial class WhiteList
|
||||
{
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
if (!File.Exists(SavePath)) { return; }
|
||||
|
||||
string[] lines;
|
||||
try
|
||||
{
|
||||
lines = File.ReadAllLines(SavePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to open whitelist in " + SavePath, e);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (line[0] == '#')
|
||||
{
|
||||
string lineval = line.Substring(1, line.Length - 1);
|
||||
Int32.TryParse(lineval, out int intVal);
|
||||
if (lineval.ToLower() == "true" || intVal != 0)
|
||||
{
|
||||
Enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Enabled = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] separatedLine = line.Split(',');
|
||||
if (separatedLine.Length < 2) continue;
|
||||
|
||||
string name = string.Join(",", separatedLine.Take(separatedLine.Length - 1));
|
||||
string ip = separatedLine.Last();
|
||||
|
||||
whitelistedPlayers.Add(new WhiteListedPlayer(name, ip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
GameServer.Log("Saving whitelist", ServerLog.MessageType.ServerMessage);
|
||||
|
||||
GameMain.Server?.ServerSettings?.UpdateFlag(ServerSettings.NetFlags.Properties);
|
||||
|
||||
List<string> lines = new List<string>();
|
||||
|
||||
if (Enabled)
|
||||
{
|
||||
lines.Add("#true");
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.Add("#false");
|
||||
}
|
||||
foreach (WhiteListedPlayer wlp in whitelistedPlayers)
|
||||
{
|
||||
lines.Add(wlp.Name + "," + wlp.IP);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(SavePath, lines);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Saving the whitelist to " + SavePath + " failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWhiteListed(string name, IPAddress address)
|
||||
{
|
||||
if (!Enabled) return true;
|
||||
WhiteListedPlayer wlp = whitelistedPlayers.Find(p => p.Name == name);
|
||||
if (wlp == null) return false;
|
||||
if (!string.IsNullOrWhiteSpace(wlp.IP))
|
||||
{
|
||||
if (address.IsIPv4MappedToIPv6 && wlp.IP == address.MapToIPv4NoThrow().ToString())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return wlp.IP == address.ToString();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RemoveFromWhiteList(WhiteListedPlayer wlp)
|
||||
{
|
||||
GameServer.Log("Removing " + wlp.Name + " from whitelist", ServerLog.MessageType.ServerMessage);
|
||||
whitelistedPlayers.Remove(wlp);
|
||||
}
|
||||
|
||||
private void AddToWhiteList(string name, string ip)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) return;
|
||||
if (whitelistedPlayers.Any(x => x.Name.ToLower() == name.ToLower() && x.IP == ip)) return;
|
||||
whitelistedPlayers.Add(new WhiteListedPlayer(name, ip));
|
||||
}
|
||||
|
||||
public void ServerAdminWrite(IWriteMessage outMsg, Client c)
|
||||
{
|
||||
if (!c.HasPermission(ClientPermissions.ManageSettings))
|
||||
{
|
||||
outMsg.Write(false); outMsg.WritePadBits();
|
||||
return;
|
||||
}
|
||||
outMsg.Write(true);
|
||||
outMsg.Write(c.Connection == GameMain.Server.OwnerConnection);
|
||||
outMsg.Write(Enabled);
|
||||
|
||||
outMsg.WritePadBits();
|
||||
outMsg.WriteVariableUInt32((UInt32)whitelistedPlayers.Count);
|
||||
for (int i = 0; i < whitelistedPlayers.Count; i++)
|
||||
{
|
||||
WhiteListedPlayer whitelistedPlayer = whitelistedPlayers[i];
|
||||
|
||||
outMsg.Write(whitelistedPlayer.Name);
|
||||
outMsg.Write(whitelistedPlayer.UniqueIdentifier);
|
||||
if (c.Connection == GameMain.Server.OwnerConnection)
|
||||
{
|
||||
outMsg.Write(whitelistedPlayer.IP);
|
||||
//outMsg.Write(whitelistedPlayer.SteamID); //TODO: add steamid to whitelisted players
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ServerAdminRead(IReadMessage incMsg, Client c)
|
||||
{
|
||||
if (!c.HasPermission(ClientPermissions.ManageSettings))
|
||||
{
|
||||
bool enabled = incMsg.ReadBoolean(); incMsg.ReadPadBits();
|
||||
UInt16 removeCount = incMsg.ReadUInt16();
|
||||
incMsg.BitPosition += removeCount * 4 * 8;
|
||||
UInt16 addCount = incMsg.ReadUInt16();
|
||||
for (int i = 0; i < addCount; i++)
|
||||
{
|
||||
incMsg.ReadString(); //skip name
|
||||
incMsg.ReadString(); //skip ip
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool prevEnabled = Enabled;
|
||||
bool enabled = incMsg.ReadBoolean(); incMsg.ReadPadBits();
|
||||
Enabled = enabled;
|
||||
|
||||
UInt16 removeCount = incMsg.ReadUInt16();
|
||||
for (int i = 0; i < removeCount; i++)
|
||||
{
|
||||
UInt16 id = incMsg.ReadUInt16();
|
||||
WhiteListedPlayer whitelistedPlayer = whitelistedPlayers.Find(p => p.UniqueIdentifier == id);
|
||||
if (whitelistedPlayer != null)
|
||||
{
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " removed " + whitelistedPlayer.Name + " from whitelist (" + whitelistedPlayer.IP + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
RemoveFromWhiteList(whitelistedPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
UInt16 addCount = incMsg.ReadUInt16();
|
||||
for (int i = 0; i < addCount; i++)
|
||||
{
|
||||
string name = incMsg.ReadString();
|
||||
string ip = incMsg.ReadString();
|
||||
|
||||
GameServer.Log(GameServer.ClientLogName(c) + " added " + name + " to whitelist (" + ip + ")", ServerLog.MessageType.ConsoleUsage);
|
||||
AddToWhiteList(name, ip);
|
||||
}
|
||||
|
||||
bool changed = removeCount > 0 || addCount > 0 || prevEnabled != enabled;
|
||||
if (changed) { Save(); }
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user