Unstable v0.19.3.0

This commit is contained in:
Juan Pablo Arce
2022-09-02 15:10:56 -03:00
parent 28789616bd
commit 3f2c843247
336 changed files with 7152 additions and 7739 deletions
@@ -1,9 +1,9 @@
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
{
@@ -11,6 +11,8 @@ namespace Barotrauma.Networking
{
private static UInt32 LastIdentifier = 0;
public bool Expired => ExpirationTime is { } expirationTime && DateTime.Now > expirationTime;
public BannedPlayer(
string name, Either<Address, AccountId> addressOrAccountId, string reason, DateTime? expirationTime)
{
@@ -24,20 +26,33 @@ namespace Barotrauma.Networking
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;
}
@@ -70,11 +85,46 @@ namespace Barotrauma.Networking
bannedPlayers.Add(new BannedPlayer(name, address, reason, expirationTime));
}
}
Save();
File.Delete(LegacySavePath);
}
public void RemoveExpired()
private void LoadBanList()
{
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
XDocument? doc = XMLExtensions.TryLoadXml(SavePath);
if (doc?.Root is null) { return; }
static Option<BannedPlayer> loadFromElement(XElement element)
{
var accountId = AccountId.Parse(element.GetAttributeString("accountid", ""));
var address = Address.Parse(element.GetAttributeString("address", ""));
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)
@@ -107,17 +157,7 @@ namespace Barotrauma.Networking
public void BanPlayer(string name, Either<Address, AccountId> addressOrAccountId, string reason, TimeSpan? duration)
{
var existingBan = bannedPlayers.Find(bp => bp.AddressOrAccountId == addressOrAccountId);
if (existingBan != null)
{
if (!duration.HasValue) { return; }
DebugConsole.Log("Set \"" + name + "\"'s ban duration to " + duration.Value);
existingBan.ExpirationTime = DateTime.Now + duration.Value;
Save();
return;
}
System.Diagnostics.Debug.Assert(!name.Contains(','));
if (existingBan != null) { bannedPlayers.Remove(existingBan); }
string logMsg = "Banned " + name;
if (!string.IsNullOrEmpty(reason)) { logMsg += ", reason: " + reason; }
@@ -132,7 +172,6 @@ namespace Barotrauma.Networking
}
bannedPlayers.Add(new BannedPlayer(name, addressOrAccountId, reason, expirationTime));
Save();
}
@@ -168,27 +207,32 @@ namespace Barotrauma.Networking
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.AddressOrAccountId.ToString());
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)
@@ -200,12 +244,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);
@@ -213,29 +257,29 @@ namespace Barotrauma.Networking
{
BannedPlayer bannedPlayer = bannedPlayers[i];
outMsg.Write(bannedPlayer.Name);
outMsg.Write(bannedPlayer.UniqueIdentifier);
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)
{
if (bannedPlayer.AddressOrAccountId.TryGet(out Address endpoint))
{
outMsg.Write(true); outMsg.WritePadBits();
outMsg.Write(endpoint.StringRepresentation);
outMsg.WriteBoolean(true); outMsg.WritePadBits();
outMsg.WriteString(endpoint.StringRepresentation);
}
else
{
outMsg.Write(false); outMsg.WritePadBits();
outMsg.Write(((SteamId)bannedPlayer.AddressOrAccountId).StringRepresentation);
outMsg.WriteBoolean(false); outMsg.WritePadBits();
outMsg.WriteString(((SteamId)bannedPlayer.AddressOrAccountId).StringRepresentation);
}
}
}
@@ -262,7 +306,7 @@ namespace Barotrauma.Networking
for (int i = 0; i < removeCount; i++)
{
UInt32 id = incMsg.ReadUInt32();
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
BannedPlayer? bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
if (bannedPlayer != null)
{
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.AddressOrAccountId + ")", ServerLog.MessageType.ConsoleUsage);
@@ -202,24 +202,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.AccountId.TryUnwrap(out var accountId) ? accountId.StringRepresentation : SenderClient.SessionId.ToString());
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);
@@ -227,7 +227,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;
@@ -107,8 +112,17 @@ 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(SessionId, true, true);
@@ -151,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)
@@ -162,19 +191,45 @@ namespace Barotrauma.Networking
return true;
}
public bool EndpointMatches(Endpoint 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));
}
}
public void SetPermissions(ClientPermissions permissions, IEnumerable<DebugConsole.Command> permittedConsoleCommands)
{
this.Permissions = permissions;
this.PermittedConsoleCommands.Clear();
foreach (var command in permittedConsoleCommands)
{
this.PermittedConsoleCommands.Add(command);
}
this.PermittedConsoleCommands.UnionWith(permittedConsoleCommands);
}
public void GivePermission(ClientPermissions 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 const int MaxPacketsPerUpdate = 4;
public const 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);
@@ -323,7 +323,7 @@ namespace Barotrauma.Networking
}
else
{
newClient.SetPermissions(ClientPermissions.None, new List<DebugConsole.Command>());
newClient.SetPermissions(ClientPermissions.None, Enumerable.Empty<DebugConsole.Command>());
}
}
@@ -383,7 +383,7 @@ namespace Barotrauma.Networking
character.KillDisconnectedTimer += deltaTime;
character.SetStun(1.0f);
Client owner = connectedClients.Find(c => (c.Character == null || c.Character == character) && c.EndpointMatches(character.OwnerClientEndpoint));
Client owner = connectedClients.Find(c => (c.Character == null || c.Character == character) && c.AddressMatches(character.OwnerClientAddress));
if ((OwnerConnection == null || owner?.Connection != OwnerConnection) && character.KillDisconnectedTimer > serverSettings.KillDisconnectedTime)
{
@@ -675,18 +675,18 @@ namespace Barotrauma.Networking
ConnectedClients.ForEach(c =>
{
IWriteMessage pingReq = new WriteOnlyMessage();
pingReq.Write((byte)ServerPacketHeader.PING_REQUEST);
pingReq.Write((byte)lastPingData.Length);
pingReq.Write(lastPingData, 0, lastPingData.Length);
pingReq.WriteByte((byte)ServerPacketHeader.PING_REQUEST);
pingReq.WriteByte((byte)lastPingData.Length);
pingReq.WriteBytes(lastPingData, 0, lastPingData.Length);
serverPeer.Send(pingReq, c.Connection, DeliveryMethod.Unreliable);
IWriteMessage pingInf = new WriteOnlyMessage();
pingInf.Write((byte)ServerPacketHeader.CLIENT_PINGS);
pingInf.Write((byte)ConnectedClients.Count);
pingInf.WriteByte((byte)ServerPacketHeader.CLIENT_PINGS);
pingInf.WriteByte((byte)ConnectedClients.Count);
ConnectedClients.ForEach(c2 =>
{
pingInf.Write(c2.SessionId);
pingInf.Write(c2.Ping);
pingInf.WriteByte(c2.SessionId);
pingInf.WriteUInt16(c2.Ping);
});
serverPeer.Send(pingInf, c.Connection, DeliveryMethod.Unreliable);
});
@@ -1475,21 +1475,22 @@ namespace Barotrauma.Networking
case ClientPermissions.SelectSub:
bool isShuttle = inc.ReadBoolean();
inc.ReadPadBits();
UInt16 subIndex = inc.ReadUInt16();
string subHash = inc.ReadString();
var subList = GameMain.NetLobbyScreen.GetSubList();
if (subIndex >= subList.Count)
var sub = GameMain.NetLobbyScreen.GetSubList().FirstOrDefault(s => s.MD5Hash.StringRepresentation == subHash);
if (sub == null)
{
DebugConsole.NewMessage($"Client \"{ClientLogName(sender)}\" attempted to select a sub, index out of bounds ({subIndex})", Color.Red);
DebugConsole.NewMessage($"Client \"{ClientLogName(sender)}\" attempted to select a sub, could not find a sub with the MD5 hash \"{subHash}\".", Color.Red);
}
else
{
if (isShuttle)
{
GameMain.NetLobbyScreen.SelectedShuttle = subList[subIndex];
GameMain.NetLobbyScreen.SelectedShuttle = sub;
}
else
{
GameMain.NetLobbyScreen.SelectedSub = subList[subIndex];
GameMain.NetLobbyScreen.SelectedSub = sub;
}
}
break;
@@ -1503,11 +1504,11 @@ namespace Barotrauma.Networking
const int MaxSaves = 255;
var saveInfos = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer, includeInCompatible: false);
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
msg.Write((byte)Math.Min(saveInfos.Count, MaxSaves));
msg.WriteByte((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
msg.WriteByte((byte)Math.Min(saveInfos.Count, MaxSaves));
for (int i = 0; i < saveInfos.Count && i < MaxSaves; i++)
{
msg.Write(saveInfos[i]);
msg.WriteNetSerializableStruct(saveInfos[i]);
}
serverPeer.Send(msg, sender.Connection, DeliveryMethod.Reliable);
}
@@ -1610,21 +1611,21 @@ namespace Barotrauma.Networking
DebugConsole.NewMessage("Sending initial lobby update", Color.Gray);
}
outmsg.Write(c.SessionId);
outmsg.WriteByte(c.SessionId);
var subList = GameMain.NetLobbyScreen.GetSubList();
outmsg.Write((UInt16)subList.Count);
outmsg.WriteUInt16((UInt16)subList.Count);
for (int i = 0; i < subList.Count; i++)
{
var sub = subList[i];
outmsg.Write(sub.Name);
outmsg.Write(sub.MD5Hash.ToString());
outmsg.Write((byte)sub.SubmarineClass);
outmsg.Write(sub.RequiredContentPackagesInstalled);
outmsg.WriteString(sub.Name);
outmsg.WriteString(sub.MD5Hash.ToString());
outmsg.WriteByte((byte)sub.SubmarineClass);
outmsg.WriteBoolean(sub.RequiredContentPackagesInstalled);
}
outmsg.Write(GameStarted);
outmsg.Write(serverSettings.AllowSpectating);
outmsg.WriteBoolean(GameStarted);
outmsg.WriteBoolean(serverSettings.AllowSpectating);
c.WritePermissions(outmsg);
}
@@ -1697,23 +1698,23 @@ namespace Barotrauma.Networking
}
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME);
outmsg.WriteByte((byte)ServerPacketHeader.UPDATE_INGAME);
outmsg.Write((float)NetTime.Now);
outmsg.WriteSingle((float)NetTime.Now);
outmsg.Write((byte)ServerNetObject.SYNC_IDS);
outmsg.Write(c.LastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server
outmsg.Write(c.LastSentEntityEventID);
outmsg.WriteByte((byte)ServerNetObject.SYNC_IDS);
outmsg.WriteUInt16(c.LastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server
outmsg.WriteUInt16(c.LastSentEntityEventID);
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode)
{
outmsg.Write(true);
outmsg.WriteBoolean(true);
outmsg.WritePadBits();
campaign.ServerWrite(outmsg, c);
}
else
{
outmsg.Write(false);
outmsg.WriteBoolean(false);
outmsg.WritePadBits();
}
@@ -1739,8 +1740,8 @@ namespace Barotrauma.Networking
}
IWriteMessage tempBuffer = new ReadWriteMessage();
tempBuffer.Write(entity is Item); tempBuffer.WritePadBits();
tempBuffer.Write(entity is MapEntity me ? me.Prefab.UintIdentifier : (UInt32)0);
tempBuffer.WriteBoolean(entity is Item); tempBuffer.WritePadBits();
tempBuffer.WriteUInt32(entity is MapEntity me ? me.Prefab.UintIdentifier : (UInt32)0);
entityPositionSync.ServerWritePosition(tempBuffer, c);
//no more room in this packet
@@ -1749,9 +1750,9 @@ namespace Barotrauma.Networking
break;
}
outmsg.Write((byte)ServerNetObject.ENTITY_POSITION);
outmsg.WriteByte((byte)ServerNetObject.ENTITY_POSITION);
outmsg.WritePadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
outmsg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
outmsg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
outmsg.WritePadBits();
c.PositionUpdateLastSent[entity] = (float)NetTime.Now;
@@ -1759,7 +1760,7 @@ namespace Barotrauma.Networking
}
positionUpdateBytes = outmsg.LengthBytes - positionUpdateBytes;
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
outmsg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
if (outmsg.LengthBytes > MsgConstants.MTU)
{
@@ -1779,8 +1780,8 @@ namespace Barotrauma.Networking
for (int i = 0; i < NetConfig.MaxEventPacketsPerUpdate; i++)
{
outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME);
outmsg.Write((float)Lidgren.Network.NetTime.Now);
outmsg.WriteByte((byte)ServerPacketHeader.UPDATE_INGAME);
outmsg.WriteSingle((float)Lidgren.Network.NetTime.Now);
int eventManagerBytes = outmsg.LengthBytes;
entityEventManager.Write(c, outmsg, out List<NetEntityEvent> sentEvents);
@@ -1791,7 +1792,7 @@ namespace Barotrauma.Networking
break;
}
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
outmsg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
if (outmsg.LengthBytes > MsgConstants.MTU)
{
@@ -1821,10 +1822,10 @@ namespace Barotrauma.Networking
bool hasChanged = NetIdUtils.IdMoreRecent(LastClientListUpdateID, c.LastRecvClientListUpdate);
if (!hasChanged) { return; }
outmsg.Write((byte)ServerNetObject.CLIENT_LIST);
outmsg.Write(LastClientListUpdateID);
outmsg.WriteByte((byte)ServerNetObject.CLIENT_LIST);
outmsg.WriteUInt16(LastClientListUpdateID);
outmsg.Write((byte)connectedClients.Count);
outmsg.WriteByte((byte)connectedClients.Count);
foreach (Client client in connectedClients)
{
var tempClientData = new TempClient
@@ -1850,7 +1851,7 @@ namespace Barotrauma.Networking
IsDownloading = FileSender.ActiveTransfers.Any(t => t.Connection == client.Connection)
};
outmsg.Write(tempClientData);
outmsg.WriteNetSerializableStruct(tempClientData);
outmsg.WritePadBits();
}
}
@@ -1860,27 +1861,32 @@ namespace Barotrauma.Networking
bool isInitialUpdate = false;
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ServerPacketHeader.UPDATE_LOBBY);
outmsg.WriteByte((byte)ServerPacketHeader.UPDATE_LOBBY);
outmsg.Write((byte)ServerNetObject.SYNC_IDS);
outmsg.WriteByte((byte)ServerNetObject.SYNC_IDS);
int settingsBytes = outmsg.LengthBytes;
int initialUpdateBytes = 0;
if (ServerSettings.UnsentFlags() != ServerSettings.NetFlags.None)
{
GameMain.NetLobbyScreen.LastUpdateID++;
}
IWriteMessage settingsBuf = null;
if (NetIdUtils.IdMoreRecent(GameMain.NetLobbyScreen.LastUpdateID, c.LastRecvLobbyUpdate))
{
outmsg.Write(true);
outmsg.WriteBoolean(true);
outmsg.WritePadBits();
outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID);
outmsg.WriteUInt16(GameMain.NetLobbyScreen.LastUpdateID);
settingsBuf = new ReadWriteMessage();
serverSettings.ServerWrite(settingsBuf, c);
outmsg.Write((UInt16)settingsBuf.LengthBytes);
outmsg.Write(settingsBuf.Buffer, 0, settingsBuf.LengthBytes);
outmsg.WriteUInt16((UInt16)settingsBuf.LengthBytes);
outmsg.WriteBytes(settingsBuf.Buffer, 0, settingsBuf.LengthBytes);
outmsg.Write(c.LastRecvLobbyUpdate < 1);
outmsg.WriteBoolean(c.LastRecvLobbyUpdate < 1);
if (c.LastRecvLobbyUpdate < 1)
{
isInitialUpdate = true;
@@ -1888,42 +1894,42 @@ namespace Barotrauma.Networking
ClientWriteInitial(c, outmsg);
initialUpdateBytes = outmsg.LengthBytes - initialUpdateBytes;
}
outmsg.Write(GameMain.NetLobbyScreen.SelectedSub.Name);
outmsg.Write(GameMain.NetLobbyScreen.SelectedSub.MD5Hash.ToString());
outmsg.Write(IsUsingRespawnShuttle());
outmsg.WriteString(GameMain.NetLobbyScreen.SelectedSub.Name);
outmsg.WriteString(GameMain.NetLobbyScreen.SelectedSub.MD5Hash.ToString());
outmsg.WriteBoolean(IsUsingRespawnShuttle());
var selectedShuttle = gameStarted && respawnManager != null && respawnManager.UsingShuttle ?
respawnManager.RespawnShuttle.Info :
GameMain.NetLobbyScreen.SelectedShuttle;
outmsg.Write(selectedShuttle.Name);
outmsg.Write(selectedShuttle.MD5Hash.ToString());
outmsg.WriteString(selectedShuttle.Name);
outmsg.WriteString(selectedShuttle.MD5Hash.ToString());
outmsg.Write(serverSettings.AllowSubVoting);
outmsg.Write(serverSettings.AllowModeVoting);
outmsg.WriteBoolean(serverSettings.AllowSubVoting);
outmsg.WriteBoolean(serverSettings.AllowModeVoting);
outmsg.Write(serverSettings.VoiceChatEnabled);
outmsg.WriteBoolean(serverSettings.VoiceChatEnabled);
outmsg.Write(serverSettings.AllowSpectating);
outmsg.WriteBoolean(serverSettings.AllowSpectating);
outmsg.WriteRangedInteger((int)serverSettings.TraitorsEnabled, 0, 2);
outmsg.WriteRangedInteger((int)GameMain.NetLobbyScreen.MissionType, 0, (int)MissionType.All);
outmsg.Write((byte)GameMain.NetLobbyScreen.SelectedModeIndex);
outmsg.Write(GameMain.NetLobbyScreen.LevelSeed);
outmsg.Write(serverSettings.SelectedLevelDifficulty);
outmsg.WriteByte((byte)GameMain.NetLobbyScreen.SelectedModeIndex);
outmsg.WriteString(GameMain.NetLobbyScreen.LevelSeed);
outmsg.WriteSingle(serverSettings.SelectedLevelDifficulty);
outmsg.Write((byte)serverSettings.BotCount);
outmsg.Write(serverSettings.BotSpawnMode == BotSpawnMode.Fill);
outmsg.WriteByte((byte)serverSettings.BotCount);
outmsg.WriteBoolean(serverSettings.BotSpawnMode == BotSpawnMode.Fill);
outmsg.Write(serverSettings.AutoRestart);
outmsg.WriteBoolean(serverSettings.AutoRestart);
if (serverSettings.AutoRestart)
{
outmsg.Write(autoRestartTimerRunning ? serverSettings.AutoRestartTimer : 0.0f);
outmsg.WriteSingle(autoRestartTimerRunning ? serverSettings.AutoRestartTimer : 0.0f);
}
}
else
{
outmsg.Write(false);
outmsg.WriteBoolean(false);
outmsg.WritePadBits();
}
settingsBytes = outmsg.LengthBytes - settingsBytes;
@@ -1933,18 +1939,18 @@ namespace Barotrauma.Networking
if (outmsg.LengthBytes < MsgConstants.MTU - 500 &&
campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode)
{
outmsg.Write(true);
outmsg.WriteBoolean(true);
outmsg.WritePadBits();
campaign.ServerWrite(outmsg, c);
}
else
{
outmsg.Write(false);
outmsg.WriteBoolean(false);
outmsg.WritePadBits();
}
campaignBytes = outmsg.LengthBytes - campaignBytes;
outmsg.Write(c.LastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server
outmsg.WriteUInt16(c.LastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server
int clientListBytes = outmsg.LengthBytes;
if (outmsg.LengthBytes < MsgConstants.MTU - 500)
@@ -1957,7 +1963,7 @@ namespace Barotrauma.Networking
WriteChatMessages(outmsg, c);
chatMessageBytes = outmsg.LengthBytes - chatMessageBytes;
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
outmsg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
bool messageTooLarge = outmsg.LengthBytes > MsgConstants.MTU;
if (messageTooLarge && !isInitialUpdate)
@@ -2071,21 +2077,21 @@ namespace Barotrauma.Networking
if (connectedClients.Any())
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.QUERY_STARTGAME);
msg.WriteByte((byte)ServerPacketHeader.QUERY_STARTGAME);
msg.Write(selectedSub.Name);
msg.Write(selectedSub.MD5Hash.StringRepresentation);
msg.WriteString(selectedSub.Name);
msg.WriteString(selectedSub.MD5Hash.StringRepresentation);
msg.Write(IsUsingRespawnShuttle());
msg.Write(selectedShuttle.Name);
msg.Write(selectedShuttle.MD5Hash.StringRepresentation);
msg.WriteBoolean(IsUsingRespawnShuttle());
msg.WriteString(selectedShuttle.Name);
msg.WriteString(selectedShuttle.MD5Hash.StringRepresentation);
var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
msg.Write(campaign == null ? (byte)0 : campaign.CampaignID);
msg.Write(campaign == null ? (UInt16)0 : campaign.LastSaveID);
msg.WriteByte(campaign == null ? (byte)0 : campaign.CampaignID);
msg.WriteUInt16(campaign == null ? (UInt16)0 : campaign.LastSaveID);
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
msg.Write(campaign == null ? (UInt16)0 : campaign.GetLastUpdateIdForFlag(flag));
msg.WriteUInt16(campaign == null ? (UInt16)0 : campaign.GetLastUpdateIdForFlag(flag));
}
connectedClients.ForEach(c => c.ReadyToStart = false);
@@ -2400,7 +2406,7 @@ namespace Barotrauma.Networking
mpCampaign.ClearSavedExperiencePoints(teamClients[i]);
}
spawnedCharacter.OwnerClientEndpoint = teamClients[i].Connection.Endpoint;
spawnedCharacter.OwnerClientAddress = teamClients[i].Connection.Endpoint.Address;
spawnedCharacter.OwnerClientName = teamClients[i].Name;
}
@@ -2498,50 +2504,50 @@ namespace Barotrauma.Networking
MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode;
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.STARTGAME);
msg.Write(seed);
msg.Write(gameSession.GameMode.Preset.Identifier);
msg.WriteByte((byte)ServerPacketHeader.STARTGAME);
msg.WriteInt32(seed);
msg.WriteIdentifier(gameSession.GameMode.Preset.Identifier);
bool missionAllowRespawn = missionMode == null || !missionMode.Missions.Any(m => !m.AllowRespawn);
msg.Write(serverSettings.AllowRespawn && missionAllowRespawn);
msg.Write(serverSettings.AllowDisguises);
msg.Write(serverSettings.AllowRewiring);
msg.Write(serverSettings.AllowFriendlyFire);
msg.Write(serverSettings.LockAllDefaultWires);
msg.Write(serverSettings.AllowRagdollButton);
msg.Write(serverSettings.AllowLinkingWifiToChat);
msg.Write(serverSettings.MaximumMoneyTransferRequest);
msg.Write(IsUsingRespawnShuttle());
msg.Write((byte)serverSettings.LosMode);
msg.Write(includesFinalize); msg.WritePadBits();
msg.WriteBoolean(serverSettings.AllowRespawn && missionAllowRespawn);
msg.WriteBoolean(serverSettings.AllowDisguises);
msg.WriteBoolean(serverSettings.AllowRewiring);
msg.WriteBoolean(serverSettings.AllowFriendlyFire);
msg.WriteBoolean(serverSettings.LockAllDefaultWires);
msg.WriteBoolean(serverSettings.AllowRagdollButton);
msg.WriteBoolean(serverSettings.AllowLinkingWifiToChat);
msg.WriteInt32(serverSettings.MaximumMoneyTransferRequest);
msg.WriteBoolean(IsUsingRespawnShuttle());
msg.WriteByte((byte)serverSettings.LosMode);
msg.WriteBoolean(includesFinalize); msg.WritePadBits();
serverSettings.WriteMonsterEnabled(msg);
if (campaign == null)
{
msg.Write(levelSeed);
msg.Write(serverSettings.SelectedLevelDifficulty);
msg.Write(gameSession.SubmarineInfo.Name);
msg.Write(gameSession.SubmarineInfo.MD5Hash.StringRepresentation);
msg.WriteString(levelSeed);
msg.WriteSingle(serverSettings.SelectedLevelDifficulty);
msg.WriteString(gameSession.SubmarineInfo.Name);
msg.WriteString(gameSession.SubmarineInfo.MD5Hash.StringRepresentation);
var selectedShuttle = gameStarted && respawnManager != null && respawnManager.UsingShuttle ?
respawnManager.RespawnShuttle.Info : GameMain.NetLobbyScreen.SelectedShuttle;
msg.Write(selectedShuttle.Name);
msg.Write(selectedShuttle.MD5Hash.StringRepresentation);
msg.Write((byte)GameMain.GameSession.GameMode.Missions.Count());
msg.WriteString(selectedShuttle.Name);
msg.WriteString(selectedShuttle.MD5Hash.StringRepresentation);
msg.WriteByte((byte)GameMain.GameSession.GameMode.Missions.Count());
foreach (Mission mission in GameMain.GameSession.GameMode.Missions)
{
msg.Write(mission.Prefab.UintIdentifier);
msg.WriteUInt32(mission.Prefab.UintIdentifier);
}
}
else
{
int nextLocationIndex = campaign.Map.Locations.FindIndex(l => l.LevelData == campaign.NextLevel);
int nextConnectionIndex = campaign.Map.Connections.FindIndex(c => c.LevelData == campaign.NextLevel);
msg.Write(campaign.CampaignID);
msg.Write(campaign.LastSaveID);
msg.Write(nextLocationIndex);
msg.Write(nextConnectionIndex);
msg.Write(campaign.Map.SelectedLocationIndex);
msg.Write(campaign.MirrorLevel);
msg.WriteByte(campaign.CampaignID);
msg.WriteUInt16(campaign.LastSaveID);
msg.WriteInt32(nextLocationIndex);
msg.WriteInt32(nextConnectionIndex);
msg.WriteInt32(campaign.Map.SelectedLocationIndex);
msg.WriteBoolean(campaign.MirrorLevel);
}
if (includesFinalize)
@@ -2560,7 +2566,7 @@ namespace Barotrauma.Networking
private void SendRoundStartFinalize(Client client)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.STARTGAMEFINALIZE);
msg.WriteByte((byte)ServerPacketHeader.STARTGAMEFINALIZE);
WriteRoundStartFinalize(msg, client);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
@@ -2569,26 +2575,26 @@ namespace Barotrauma.Networking
{
//tell the client what content files they should preload
var contentToPreload = GameMain.GameSession.EventManager.GetFilesToPreload();
msg.Write((ushort)contentToPreload.Count());
msg.WriteUInt16((ushort)contentToPreload.Count());
foreach (ContentFile contentFile in contentToPreload)
{
msg.Write(contentFile.Path.Value);
msg.WriteString(contentFile.Path.Value);
}
msg.Write(Submarine.MainSub?.Info.EqualityCheckVal ?? 0);
msg.Write((byte)GameMain.GameSession.Missions.Count());
msg.WriteInt32(Submarine.MainSub?.Info.EqualityCheckVal ?? 0);
msg.WriteByte((byte)GameMain.GameSession.Missions.Count());
foreach (Mission mission in GameMain.GameSession.Missions)
{
msg.Write(mission.Prefab.Identifier);
msg.WriteIdentifier(mission.Prefab.Identifier);
}
foreach (Level.LevelGenStage stage in Enum.GetValues(typeof(Level.LevelGenStage)).OfType<Level.LevelGenStage>().OrderBy(s => s))
{
msg.Write(GameMain.GameSession.Level.EqualityCheckValues[stage]);
msg.WriteInt32(GameMain.GameSession.Level.EqualityCheckValues[stage]);
}
foreach (Mission mission in GameMain.GameSession.Missions)
{
mission.ServerWriteInitial(msg, client);
}
msg.Write(GameMain.GameSession.CrewManager != null);
msg.WriteBoolean(GameMain.GameSession.CrewManager != null);
GameMain.GameSession.CrewManager?.ServerWriteActiveOrders(msg);
}
@@ -2651,18 +2657,18 @@ namespace Barotrauma.Networking
if (connectedClients.Count > 0)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.ENDGAME);
msg.Write((byte)transitionType);
msg.Write(wasSaved);
msg.Write(endMessage);
msg.Write((byte)missions.Count);
msg.WriteByte((byte)ServerPacketHeader.ENDGAME);
msg.WriteByte((byte)transitionType);
msg.WriteBoolean(wasSaved);
msg.WriteString(endMessage);
msg.WriteByte((byte)missions.Count);
foreach (Mission mission in missions)
{
msg.Write(mission.Completed);
msg.WriteBoolean(mission.Completed);
}
msg.Write(GameMain.GameSession?.WinningTeam == null ? (byte)0 : (byte)GameMain.GameSession.WinningTeam);
msg.WriteByte(GameMain.GameSession?.WinningTeam == null ? (byte)0 : (byte)GameMain.GameSession.WinningTeam);
msg.Write((byte)traitorResults.Count);
msg.WriteByte((byte)traitorResults.Count);
foreach (var traitorResult in traitorResults)
{
traitorResult.ServerWrite(msg);
@@ -2868,7 +2874,7 @@ namespace Barotrauma.Networking
//reset karma to a neutral value, so if/when the ban is revoked the client wont get immediately punished by low karma again
previousPlayer.Karma = Math.Max(previousPlayer.Karma, 50.0f);
serverSettings.BanList.BanPlayer(previousPlayer.Name, previousPlayer.Endpoint, reason, duration);
serverSettings.BanList.BanPlayer(previousPlayer.Name, previousPlayer.Address, reason, duration);
if (previousPlayer.AccountInfo.AccountId.TryUnwrap(out var accountId))
{
serverSettings.BanList.BanPlayer(previousPlayer.Name, accountId, reason, duration);
@@ -3252,9 +3258,9 @@ namespace Barotrauma.Networking
public void SendCancelTransferMsg(FileSender.FileTransferOut transfer)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.FILE_TRANSFER);
msg.Write((byte)FileTransferMessageType.Cancel);
msg.Write((byte)transfer.ID);
msg.WriteByte((byte)ServerPacketHeader.FILE_TRANSFER);
msg.WriteByte((byte)FileTransferMessageType.Cancel);
msg.WriteByte((byte)transfer.ID);
serverPeer.Send(msg, transfer.Connection, DeliveryMethod.ReliableOrdered);
}
@@ -3290,7 +3296,7 @@ namespace Barotrauma.Networking
Client.UpdateKickVotes(connectedClients);
var kickVoteEligibleClients = connectedClients.Where(c => (DateTime.Now - c.JoinTime).TotalSeconds > ServerSettings.DisallowKickVoteTime);
int minimumKickVotes = Math.Max(2, (int)(kickVoteEligibleClients.Count() * serverSettings.KickVoteRequiredRatio));
float minimumKickVotes = Math.Max(2.0f, kickVoteEligibleClients.Count() * serverSettings.KickVoteRequiredRatio);
var clientsToKick = connectedClients.FindAll(c =>
c.Connection != OwnerConnection &&
!c.HasPermission(ClientPermissions.Kick) &&
@@ -3299,12 +3305,10 @@ namespace Barotrauma.Networking
c.KickVoteCount >= minimumKickVotes);
foreach (Client c in clientsToKick)
{
//reset the client's kick votes (they can rejoin after their ban expires)
c.ResetVotes();
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(c));
if (previousPlayer != null)
{
//reset the client's kick votes (they can rejoin after their ban expires)
previousPlayer.KickVoters.Clear();
}
previousPlayer?.KickVoters.Clear();
SendChatMessage($"ServerMessage.KickedFromServer~[client]={c.Name}", ChatMessageType.Server, null, changeType: PlayerConnectionChangeType.Kicked);
KickClient(c, "ServerMessage.KickedByVote");
@@ -3330,10 +3334,10 @@ namespace Barotrauma.Networking
if (!recipients.Any()) { return; }
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.UPDATE_LOBBY);
msg.Write((byte)ServerNetObject.VOTE);
msg.WriteByte((byte)ServerPacketHeader.UPDATE_LOBBY);
msg.WriteByte((byte)ServerNetObject.VOTE);
Voting.ServerWrite(msg);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
msg.WriteByte((byte)ServerNetObject.END_OF_MESSAGE);
foreach (var c in recipients)
{
@@ -3427,7 +3431,7 @@ namespace Barotrauma.Networking
if (recipient?.Connection == null) { return; }
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.PERMISSIONS);
msg.WriteByte((byte)ServerPacketHeader.PERMISSIONS);
client.WritePermissions(msg);
serverPeer.Send(msg, recipient.Connection, DeliveryMethod.Reliable);
}
@@ -3462,9 +3466,9 @@ namespace Barotrauma.Networking
client.GivenAchievements.Add(achievementIdentifier);
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.ACHIEVEMENT);
msg.Write(achievementIdentifier);
msg.Write(0);
msg.WriteByte((byte)ServerPacketHeader.ACHIEVEMENT);
msg.WriteIdentifier(achievementIdentifier);
msg.WriteInt32(0);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
@@ -3474,9 +3478,9 @@ namespace Barotrauma.Networking
if (client.GivenAchievements.Contains(achievementIdentifier)) { return; }
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.ACHIEVEMENT);
msg.Write(achievementIdentifier);
msg.Write(amount);
msg.WriteByte((byte)ServerPacketHeader.ACHIEVEMENT);
msg.WriteIdentifier(achievementIdentifier);
msg.WriteInt32(amount);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
@@ -3485,10 +3489,10 @@ namespace Barotrauma.Networking
{
if (client == null) { return; }
var msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.TRAITOR_MESSAGE);
msg.Write((byte)messageType);
msg.Write(missionIdentifier);
msg.Write(message);
msg.WriteByte((byte)ServerPacketHeader.TRAITOR_MESSAGE);
msg.WriteByte((byte)messageType);
msg.WriteIdentifier(missionIdentifier);
msg.WriteString(message);
serverPeer.Send(msg, client.Connection, DeliveryMethod.ReliableOrdered);
}
@@ -3497,8 +3501,8 @@ namespace Barotrauma.Networking
if (!connectedClients.Any()) { return; }
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.CHEATS_ENABLED);
msg.Write(DebugConsole.CheatsEnabled);
msg.WriteByte((byte)ServerPacketHeader.CHEATS_ENABLED);
msg.WriteBoolean(DebugConsole.CheatsEnabled);
msg.WritePadBits();
foreach (Client c in connectedClients)
@@ -3515,7 +3519,7 @@ namespace Barotrauma.Networking
if (client.Character != null)
{
client.Character.IsRemotePlayer = false;
client.Character.OwnerClientEndpoint = null;
client.Character.OwnerClientAddress = null;
client.Character.OwnerClientName = null;
}
@@ -3542,7 +3546,7 @@ namespace Barotrauma.Networking
newCharacter.Info.Character = newCharacter;
}
newCharacter.OwnerClientEndpoint = client.Connection.Endpoint;
newCharacter.OwnerClientAddress = client.Connection.Endpoint.Address;
newCharacter.OwnerClientName = client.Name;
newCharacter.IsRemotePlayer = true;
newCharacter.Enabled = true;
@@ -3900,9 +3904,9 @@ namespace Barotrauma.Networking
foreach (var client in connectedClients)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.MISSION);
msg.WriteByte((byte)ServerPacketHeader.MISSION);
int missionIndex = GameMain.GameSession.GetMissionIndex(mission);
msg.Write((byte)(missionIndex == -1 ? 255: missionIndex));
msg.WriteByte((byte)(missionIndex == -1 ? 255: missionIndex));
mission?.ServerWrite(msg);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
@@ -3963,7 +3967,7 @@ namespace Barotrauma.Networking
class PreviousPlayer
{
public string Name;
public Endpoint Endpoint;
public Address Address;
public AccountInfo AccountInfo;
public float Karma;
public int KarmaKickCount;
@@ -3972,14 +3976,14 @@ namespace Barotrauma.Networking
public PreviousPlayer(Client c)
{
Name = c.Name;
Endpoint = c.Connection.Endpoint;
Address = c.Connection.Endpoint.Address;
AccountInfo = c.AccountInfo;
}
public bool MatchesClient(Client c)
{
if (c.AccountInfo.AccountId.IsSome() && AccountInfo.AccountId.IsSome()) { return c.AccountInfo.AccountId == AccountInfo.AccountId; }
return c.EndpointMatches(Endpoint);
return c.AddressMatches(Address);
}
}
}
@@ -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));
@@ -7,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.AccountId.TryUnwrap(out var accountId) ? accountId.StringRepresentation : SenderClient.SessionId.ToString());
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(false); //text color (no custom text colors for order messages)
msg.WriteBoolean(false); //text color (no custom text colors for order messages)
msg.WritePadBits();
WriteOrder(msg);
}
@@ -1,4 +1,5 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Net;
using System.Linq;
@@ -7,10 +8,10 @@ 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;
@@ -20,6 +21,25 @@ namespace Barotrauma.Networking
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>();
@@ -32,21 +52,7 @@ namespace Barotrauma.Networking
{
if (netServer != null) { return; }
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);
incomingLidgrenMessages.Clear();
netServer = new NetServer(netPeerConfiguration);
@@ -62,7 +68,7 @@ namespace Barotrauma.Networking
}
}
public override void Close(string msg = null)
public override void Close(string? msg = null)
{
if (netServer == null) { return; }
@@ -90,7 +96,9 @@ namespace Barotrauma.Networking
public override void Update(float deltaTime)
{
if (netServer == null) { return; }
if (netServer is null) { return; }
ToolBox.ThrowIfNull(incomingLidgrenMessages);
if (OnOwnerDetermined != null && OwnerConnection != null)
{
@@ -99,7 +107,7 @@ namespace Barotrauma.Networking
}
netServer.ReadMessages(incomingLidgrenMessages);
//process incoming connections first
foreach (NetIncomingMessage inc in incomingLidgrenMessages.Where(m => m.MessageType == NetIncomingMessageType.ConnectionApproval))
{
@@ -126,7 +134,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
@@ -138,7 +146,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 ||
@@ -146,6 +155,7 @@ namespace Barotrauma.Networking
{
continue;
}
UpdatePendingClient(pendingClient);
if (i >= pendingClients.Count || pendingClients[i] != pendingClient) { i--; }
}
@@ -155,7 +165,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
@@ -178,7 +190,7 @@ namespace Barotrauma.Networking
private void HandleConnection(NetIncomingMessage inc)
{
if (netServer == null) { return; }
if (connectedClients.Count >= serverSettings.MaxPlayers)
{
inc.SenderConnection.Deny(DisconnectReason.ServerFull.ToString());
@@ -188,13 +200,13 @@ namespace Barotrauma.Networking
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($"{DisconnectReason.Banned}/ {banReason}");
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(inc.SenderConnection));
pendingClients.Add(pendingClient);
@@ -203,51 +215,52 @@ namespace Barotrauma.Networking
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 LidgrenConnection conn))
{
if (pendingClient != null)
{
RemovePendingClient(pendingClient, DisconnectReason.AuthenticationRequired, "Received data message from unauthenticated client");
}
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($"{DisconnectReason.AuthenticationRequired}/ Received data message from unauthenticated client");
}
return;
}
if (pendingClient != null) { pendingClients.Remove(pendingClient); }
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, $"{DisconnectReason.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);
OnMessageReceived?.Invoke(conn, packet.GetReadMessage(packetHeader.IsCompressed(), conn));
}
}
private void HandleStatusChanged(NetIncomingMessage inc)
{
if (netServer == null) { return; }
@@ -255,30 +268,30 @@ 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($"{DisconnectReason.ServerShutdown}/ Owner disconnected");
}
else
{
disconnectMsg = $"ServerMessage.HasDisconnected~[client]={GameMain.Server.ConnectedClients.First(c => c.Connection == conn).Name}";
Disconnect(conn, disconnectMsg);
#warning TODO: kill off disconnect in layer 1
Disconnect(conn, $"ServerMessage.HasDisconnected~[client]={GameMain.Server.ConnectedClients.First(c => c.Connection == conn).Name}");
}
}
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}");
}
}
break;
}
}
@@ -292,25 +305,23 @@ namespace Barotrauma.Networking
{
if (netServer == null) { return; }
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 == 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.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId) as LidgrenConnection;
if (connection != null)
{
Disconnect(connection, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication status changed: " + status.ToString());
}
Disconnect(connection, $"{DisconnectReason.SteamAuthenticationFailed}/ Steam authentication status changed: {status}");
}
return;
}
LidgrenConnection pendingConnection = pendingClient.Connection as LidgrenConnection;
string banReason;
if (serverSettings.BanList.IsBanned(pendingConnection.Endpoint, 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))
{
@@ -326,8 +337,7 @@ namespace Barotrauma.Networking
}
else
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam authentication failed: " + status.ToString());
return;
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, $"Steam authentication failed: {status}");
}
}
@@ -335,86 +345,62 @@ 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.Endpoint.StringRepresentation);
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.Endpoint.StringRepresentation+": " + 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, string? msg = null)
{
if (netServer == null) { return; }
if (!(conn is LidgrenConnection lidgrenConn)) { return; }
if (connectedClients.Contains(lidgrenConn))
{
lidgrenConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(lidgrenConn);
OnDisconnect?.Invoke(conn, msg);
if (conn.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId }) { Steam.SteamManager.StopAuthSession(steamId); }
if (conn.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId }) { SteamManager.StopAuthSession(steamId); }
}
lidgrenConn.NetConnection.Disconnect(msg ?? "Disconnected");
}
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.Endpoint.StringRepresentation + ": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
DebugConsole.NewMessage($"Failed to send message to {conn.Endpoint}: {result}", Microsoft.Xna.Framework.Color.Yellow);
}
}
@@ -430,7 +416,7 @@ namespace Barotrauma.Networking
}
}
protected override void ProcessAuthTicket(string name, Option<int> ownKey, Option<SteamId> steamId, PendingClient pendingClient, byte[] ticket)
protected override void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient)
{
if (pendingClient.AccountInfo.AccountId.IsNone())
{
@@ -438,19 +424,19 @@ namespace Barotrauma.Networking
#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 ((!SteamManager.IsInitialized || (ticket?.Length ?? 0) == 0)
&& !requireSteamAuth)
if ((!SteamManager.IsInitialized || !hasSteamAuth) && !requireSteamAuth)
{
pendingClient.Name = name;
pendingClient.OwnerKey = ownKey;
pendingClient.Name = packet.Name;
pendingClient.OwnerKey = packet.OwnerKey;
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
else
{
if (!steamId.TryUnwrap(out var id))
if (!packet.SteamId.TryUnwrap(out var id) || !(id is SteamId steamId))
{
if (requireSteamAuth)
{
@@ -460,36 +446,42 @@ namespace Barotrauma.Networking
}
else
{
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, id);
Steamworks.BeginAuthResult authSessionStartState = SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{
if (requireSteamAuth)
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: " + authSessionStartState.ToString());
return;
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, $"Steam auth session failed to start: {authSessionStartState}");
}
else
{
steamId = Option<SteamId>.None();
packet.SteamId = Option<AccountId>.None();
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
}
}
pendingClient.Connection.SetAccountInfo(new AccountInfo(steamId.Select(uid => (AccountId)uid)));
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.AccountInfo.AccountId != steamId.Select(uid => (AccountId)uid))
if (pendingClient.AccountInfo.AccountId != packet.SteamId.Select(uid => (AccountId)uid))
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
return;
}
}
}
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());
}
}
}
}
@@ -1,39 +1,41 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
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, string clientName);
public delegate void DisconnectCallback(NetworkConnection connection, string? reason);
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 Option<int> ownerKey;
public NetworkConnection OwnerConnection { get; protected set; }
public MessageCallback? OnMessageReceived;
public DisconnectCallback? OnDisconnect;
public InitializationCompleteCallback? OnInitializationComplete;
public ShutdownCallback? OnShutdown;
public OwnerDeterminedCallback? OnOwnerDetermined;
public abstract void InitializeSteamServerCallbacks();
public abstract void Start();
public abstract void Close(string msg = null);
public abstract void Close(string? msg = null);
public abstract void Update(float deltaTime);
protected class PendingClient
protected sealed class PendingClient
{
public string Name;
public string? Name;
public Option<int> OwnerKey;
public NetworkConnection Connection;
public readonly NetworkConnection Connection;
public ConnectionInitialization InitializationStep;
public double UpdateTime;
public double TimeOut;
@@ -60,76 +62,69 @@ 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 steamIdVal = inc.ReadUInt64();
Option<SteamId> steamId = steamIdVal != 0
? Option<SteamId>.Some(new SteamId(steamIdVal))
: Option<SteamId>.None();
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, "");
return;
}
string version = inc.ReadString();
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
bool isCompatibleVersion = NetworkMember.IsCompatible(authPacket.GameVersion, GameMain.Version.ToString()) ?? false;
if (!isCompatibleVersion)
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={authPacket.GameVersion}");
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);
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 != 0 ? Option<int>.Some(ownerKey) : Option<int>.None(), 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;
}
@@ -138,13 +133,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);
return;
}
}
pendingClient.UpdateTime = Timing.TotalTime;
break;
case ConnectionInitialization.ContentPackageOrder:
@@ -154,25 +149,25 @@ namespace Barotrauma.Networking
}
}
protected abstract void ProcessAuthTicket(string name, Option<int> ownKey, Option<SteamId> steamId, PendingClient pendingClient, byte[] ticket);
protected abstract void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient);
protected void BanPendingClient(PendingClient pendingClient, string banReason, TimeSpan? duration)
{
void banAccountId(AccountId accountId)
{
serverSettings.BanList.BanPlayer(pendingClient.Name, accountId, banReason, duration);
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", accountId, banReason, duration);
}
if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var id)) { banAccountId(id); }
pendingClient.AccountInfo.OtherMatchingIds.ForEach(banAccountId);
serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.Connection.Endpoint, banReason, duration);
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)
{
bool isAccountIdBanned(AccountId accountId, out string banReason)
bool isAccountIdBanned(AccountId accountId, out string? banReason)
{
banReason = default;
return serverSettings.BanList.IsBanned(accountId, out banReason);
}
@@ -182,16 +177,18 @@ namespace Barotrauma.Networking
foreach (var otherId in pendingClient.AccountInfo.OtherMatchingIds)
{
if (isBanned) { break; }
isBanned |= isAccountIdBanned(otherId, out banReason);
}
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)
{
if (IsPendingClientBanned(pendingClient, out string banReason))
if (IsPendingClientBanned(pendingClient, out string? banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
return;
@@ -220,52 +217,61 @@ namespace Barotrauma.Networking
}
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:
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)
.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) { }
protected void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
protected void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string? msg)
{
if (pendingClients.Contains(pendingClient))
{
Disconnect(pendingClient.Connection, reason + "/" + msg);
Disconnect(pendingClient.Connection, $"{reason}/{msg}");
pendingClients.Remove(pendingClient);
@@ -279,6 +285,6 @@ namespace Barotrauma.Networking
}
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, string? msg = null);
}
}
}
@@ -1,21 +1,20 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Networking
{
class SteamP2PServerPeer : ServerPeer
internal sealed class SteamP2PServerPeer : ServerPeer
{
private bool started;
private readonly SteamId ownerSteamId;
private UInt64 ownerKey64 => unchecked((UInt64)ownerKey.Fallback(0));
private SteamId ReadSteamId(IReadMessage inc)
=> new SteamId(inc.ReadUInt64() ^ ownerKey64);
private void WriteSteamId(IWriteMessage msg, SteamId val)
=> msg.Write(val.Value ^ ownerKey64);
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)
{
@@ -33,23 +32,22 @@ namespace Barotrauma.Networking
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(string? msg = null)
{
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--)
{
@@ -82,7 +80,7 @@ namespace Barotrauma.Networking
//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)
{
@@ -103,7 +101,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
@@ -118,36 +116,36 @@ namespace Barotrauma.Networking
if (i >= pendingClients.Count || pendingClients[i] != pendingClient) { i--; }
}
}
private void HandleDataMessage(IReadMessage inc)
{
if (!started) { return; }
SteamId senderSteamId = ReadSteamId(inc);
SteamId ownerSteamId = ReadSteamId(inc);
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
SteamId sentOwnerSteamId = ReadSteamId(inc);
var (deliveryMethod, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
if (packetHeader.IsServerMessage())
{
DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString());
DebugConsole.ThrowError($"Got server message from {senderSteamId}");
return;
}
if (senderSteamId != this.ownerSteamId) //sender is remote, handle disconnects and heartbeats
if (senderSteamId != ownerSteamId) //sender is remote, handle disconnects and heartbeats
{
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;
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 (serverSettings.BanList.IsBanned(senderSteamId, out string banReason) ||
serverSettings.BanList.IsBanned(sentOwnerSteamId, out banReason))
{
if (pendingClient != null)
{
@@ -155,9 +153,8 @@ namespace Barotrauma.Networking
}
else if (connectedClient != null)
{
Disconnect(connectedClient, DisconnectReason.Banned.ToString() + "/ "+ banReason);
Disconnect(connectedClient, $"{DisconnectReason.Banned}/ {banReason}");
}
return;
}
else if (packetHeader.IsDisconnectMessage())
{
@@ -171,7 +168,6 @@ namespace Barotrauma.Networking
string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={GameMain.Server.ConnectedClients.First(c => c.Connection == connectedClient).Name}";
Disconnect(connectedClient, disconnectMsg, false);
}
return;
}
else if (packetHeader.IsHeartbeatMessage())
{
@@ -182,12 +178,15 @@ namespace Barotrauma.Networking
{
if (pendingClient != null)
{
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, ownerSteamId));
ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null));
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
ReadConnectionInitializationStep(
pendingClient,
new ReadWriteMessage(inc.Buffer, inc.BitPosition, inc.LengthBits, false),
initialization ?? throw new Exception("Initialization step missing"));
}
else
else if (initialization.HasValue)
{
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
ConnectionInitialization initializationStep = initialization.Value;
if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClients.Add(new PendingClient(new SteamP2PConnection(senderSteamId)));
@@ -196,51 +195,53 @@ namespace Barotrauma.Networking
}
else if (connectedClient != null)
{
UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, connectedClient);
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, connectedClient);
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(this.ownerSteamId)
var packet = INetSerializableStruct.Read<SteamP2PInitializationOwnerPacket>(inc);
OwnerConnection = new SteamP2PConnection(ownerSteamId)
{
Language = GameSettings.CurrentConfig.Language
};
OwnerConnection.SetAccountInfo(new AccountInfo(this.ownerSteamId, this.ownerSteamId));
OwnerConnection.SetAccountInfo(new AccountInfo(ownerSteamId, ownerSteamId));
OnInitializationComplete?.Invoke(OwnerConnection, ownerName);
OnInitializationComplete?.Invoke(OwnerConnection, packet.OwnerName);
}
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);
OnMessageReceived?.Invoke(OwnerConnection!, msg);
}
}
}
@@ -249,59 +250,67 @@ 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 SteamP2PConnection steamP2PConn)) { return; }
if (!connectedClients.Contains(steamP2PConn) && conn != OwnerConnection)
{
DebugConsole.ThrowError("Tried to send message to unauthenticated connection: " + steamp2pConn.AccountInfo.AccountId.ToString());
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {steamP2PConn.AccountInfo.AccountId}");
return;
}
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || !(connAccountId is SteamId connSteamId)) { return; }
IWriteMessage msgToSend = new WriteOnlyMessage();
byte[] msgData = new byte[16];
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
WriteSteamId(msgToSend, connSteamId);
msgToSend.Write((byte)deliveryMethod);
msgToSend.Write((byte)((isCompressed ? PacketHeader.IsCompressed : PacketHeader.None) | PacketHeader.IsServerMessage));
msgToSend.Write((UInt16)length);
msgToSend.Write(msgData, 0, length);
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
ChildServerRelay.Write(bufToSend);
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(SteamId steamId, string msg)
private void SendDisconnectMessage(SteamId 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);
var headers = new PeerPacketHeaders
{
DeliveryMethod = DeliveryMethod.Reliable,
PacketHeader = PacketHeader.IsDisconnectMessage | PacketHeader.IsServerMessage,
Initialization = null
};
var packet = new PeerDisconnectPacket
{
Message = msg
};
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
ChildServerRelay.Write(bufToSend);
SendMsgInternal(steamId, headers, packet);
}
private void Disconnect(NetworkConnection conn, string msg, bool sendDisconnectMessage)
private void Disconnect(NetworkConnection conn, string? msg, bool sendDisconnectMessage)
{
if (!started) { return; }
if (!(conn is SteamP2PConnection steamp2pConn)) { return; }
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || !(connAccountId is SteamId connSteamId)) { return; }
if (sendDisconnectMessage) { SendDisconnectMessage(connSteamId, msg); }
if (connectedClients.Contains(steamp2pConn))
{
steamp2pConn.Status = NetworkConnectionStatus.Disconnected;
@@ -315,32 +324,41 @@ namespace Barotrauma.Networking
}
}
public override void Disconnect(NetworkConnection conn, string msg = null)
public override void Disconnect(NetworkConnection conn, string? msg = null)
{
Disconnect(conn, msg, true);
}
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
{
var connSteamId = conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var id } }
? id : null;
var connSteamId = conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var id } } ? id : null;
if (connSteamId is null) { return; }
SendMsgInternal(connSteamId, headers, body);
}
private void SendMsgInternal(SteamId connSteamId, PeerPacketHeaders headers, INetSerializableStruct? body)
{
IWriteMessage msgToSend = new WriteOnlyMessage();
WriteSteamId(msgToSend, connSteamId);
msgToSend.Write((byte)deliveryMethod);
msgToSend.Write(msg.Buffer, 0, msg.LengthBytes);
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
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, Option<int> ownKey, Option<SteamId> steamId, PendingClient pendingClient, byte[] ticket)
protected override void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient)
{
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
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;
@@ -444,7 +439,7 @@ namespace Barotrauma.Networking
}
clients[i].Character = character;
character.OwnerClientEndpoint = clients[i].Connection.Endpoint;
character.OwnerClientAddress = clients[i].Connection.Endpoint.Address;
character.OwnerClientName = clients[i].Name;
GameServer.Log(
$"Respawning {GameServer.ClientLogName(clients[i])} ({clients[i].Connection.Endpoint}) as {characterInfos[i].Job.Name}", ServerLog.MessageType.Spawning);
@@ -561,7 +556,7 @@ namespace Barotrauma.Networking
{
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);
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.End, SkillReductionOnDeath);
}
}
@@ -572,20 +567,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;
@@ -27,20 +27,26 @@ namespace Barotrauma.Networking
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()
{
@@ -56,16 +62,16 @@ 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);
@@ -74,21 +80,21 @@ namespace Barotrauma.Networking
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);
@@ -103,16 +109,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();
}
}
@@ -174,7 +182,6 @@ namespace Barotrauma.Networking
if (propertiesChanged)
{
UpdateFlag(NetFlags.Properties);
LastPropertyUpdateId = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID + 1);
}
changed |= propertiesChanged;
}
@@ -55,8 +55,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);
@@ -254,7 +254,7 @@ namespace Barotrauma
break;
case VoteType.Kick:
byte kickedClientID = inc.ReadByte();
if ((DateTime.Now - sender.JoinTime).TotalSeconds > GameMain.Server.ServerSettings.DisallowKickVoteTime)
if ((DateTime.Now - sender.JoinTime).TotalSeconds < GameMain.Server.ServerSettings.DisallowKickVoteTime)
{
GameMain.Server.SendDirectChatMessage($"ServerMessage.kickvotedisallowed", sender);
}
@@ -328,66 +328,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.SessionId);
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.SessionId);
msg.WriteByte(c.SessionId);
}
msg.Write((byte)eligibleClients.Count());
msg.WriteByte((byte)eligibleClients.Count());
switch (ActiveVote.State)
{
case VoteState.Started:
msg.Write(ActiveVote.VoteStarter.SessionId);
msg.Write((byte)GameMain.Server.ServerSettings.VoteTimeout);
msg.WriteByte(ActiveVote.VoteStarter.SessionId);
msg.WriteByte((byte)GameMain.Server.ServerSettings.VoteTimeout);
switch (ActiveVote.VoteType)
{
@@ -395,14 +395,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?.SessionId ?? 0);
msg.Write(transferVote.To?.SessionId ?? 0);
msg.Write(transferVote.TransferAmount);
msg.WriteByte(transferVote.From?.SessionId ?? 0);
msg.WriteByte(transferVote.To?.SessionId ?? 0);
msg.WriteInt32(transferVote.TransferAmount);
break;
}
@@ -412,16 +412,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;
@@ -430,10 +430,10 @@ namespace Barotrauma
}
var readyClients = GameMain.Server.ConnectedClients.Where(c => c.GetVote<bool>(VoteType.StartRound));
msg.Write((byte)readyClients.Count());
msg.WriteByte((byte)readyClients.Count());
foreach (Client c in readyClients)
{
msg.Write(c.SessionId);
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;
}
}
}
}