Merge branch 'dev' of https://github.com/Regalis11/Barotrauma into unstable

This commit is contained in:
EvilFactory
2022-09-29 12:13:55 -03:00
602 changed files with 19759 additions and 16312 deletions
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -47,42 +46,40 @@ namespace Barotrauma
public void ServerWrite(IWriteMessage msg)
{
msg.Write(ID);
msg.Write(Name);
msg.Write(OriginalName);
msg.Write((byte)Head.Preset.TagSet.Count);
msg.WriteUInt16(ID);
msg.WriteString(Name);
msg.WriteString(OriginalName);
msg.WriteByte((byte)Head.Preset.TagSet.Count);
foreach (Identifier tag in Head.Preset.TagSet)
{
msg.Write(tag);
msg.WriteIdentifier(tag);
}
msg.Write((byte)Head.HairIndex);
msg.Write((byte)Head.BeardIndex);
msg.Write((byte)Head.MoustacheIndex);
msg.Write((byte)Head.FaceAttachmentIndex);
msg.WriteByte((byte)Head.HairIndex);
msg.WriteByte((byte)Head.BeardIndex);
msg.WriteByte((byte)Head.MoustacheIndex);
msg.WriteByte((byte)Head.FaceAttachmentIndex);
msg.WriteColorR8G8B8(Head.SkinColor);
msg.WriteColorR8G8B8(Head.HairColor);
msg.WriteColorR8G8B8(Head.FacialHairColor);
msg.Write(ragdollFileName);
msg.WriteString(ragdollFileName);
msg.WriteIdentifier(HumanPrefabIds.NpcIdentifier);
if (Job != null)
{
msg.Write(Job.Prefab.Identifier);
msg.Write((byte)Job.Variant);
var skills = Job.GetSkills();
msg.Write((byte)skills.Count());
foreach (Skill skill in skills)
msg.WriteUInt32(Job.Prefab.UintIdentifier);
msg.WriteByte((byte)Job.Variant);
foreach (SkillPrefab skillPrefab in Job.Prefab.Skills.OrderBy(s => s.Identifier))
{
msg.Write(skill.Identifier);
msg.Write(skill.Level);
msg.WriteSingle(Job.GetSkill(skillPrefab.Identifier).Level);
}
}
else
{
msg.Write("");
msg.Write((byte)0);
msg.WriteUInt32((uint)0);
msg.WriteByte((byte)0);
}
msg.Write((ushort)ExperiencePoints);
msg.WriteUInt16((ushort)ExperiencePoints);
msg.WriteRangedInteger(AdditionalTalentPoints, 0, MaxAdditionalTalentPoints);
}
}
@@ -8,7 +8,7 @@ namespace Barotrauma
{
partial class Character
{
public string OwnerClientEndPoint;
public Address OwnerClientAddress;
public string OwnerClientName;
public bool ClientDisconnected;
public float KillDisconnectedTimer;
@@ -305,25 +305,25 @@ namespace Barotrauma
public void ServerWritePosition(IWriteMessage msg, Client c)
{
msg.Write(ID);
msg.WriteUInt16(ID);
IWriteMessage tempBuffer = new WriteOnlyMessage();
if (this == c.Character)
{
tempBuffer.Write(true);
tempBuffer.WriteBoolean(true);
if (LastNetworkUpdateID < memInput.Count + 1)
{
tempBuffer.Write((UInt16)0);
tempBuffer.WriteUInt16((UInt16)0);
}
else
{
tempBuffer.Write((UInt16)(LastNetworkUpdateID - memInput.Count - 1));
tempBuffer.WriteUInt16((UInt16)(LastNetworkUpdateID - memInput.Count - 1));
}
}
else
{
tempBuffer.Write(false);
tempBuffer.WriteBoolean(false);
bool aiming = false;
bool use = false;
@@ -346,40 +346,41 @@ namespace Barotrauma
networkUpdateSent = true;
}
tempBuffer.Write(aiming);
tempBuffer.Write(shoot);
tempBuffer.Write(use);
tempBuffer.WriteBoolean(aiming);
tempBuffer.WriteBoolean(shoot);
tempBuffer.WriteBoolean(use);
if (AnimController is HumanoidAnimController)
{
tempBuffer.Write(((HumanoidAnimController)AnimController).Crouching);
tempBuffer.WriteBoolean(((HumanoidAnimController)AnimController).Crouching);
}
tempBuffer.Write(attack);
tempBuffer.WriteBoolean(attack);
Vector2 relativeCursorPos = cursorPosition - AimRefPosition;
tempBuffer.Write((UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI)));
tempBuffer.WriteUInt16((UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI)));
tempBuffer.Write(IsRagdolled || Stun > 0.0f || IsDead || IsIncapacitated);
tempBuffer.WriteBoolean(IsRagdolled || Stun > 0.0f || IsDead || IsIncapacitated);
tempBuffer.Write(AnimController.Dir > 0.0f);
tempBuffer.WriteBoolean(AnimController.Dir > 0.0f);
}
if (SelectedCharacter != null || SelectedConstruction != null)
if (SelectedCharacter != null || HasSelectedAnyItem)
{
tempBuffer.Write(true);
tempBuffer.Write(SelectedCharacter != null ? SelectedCharacter.ID : NullEntityID);
tempBuffer.Write(SelectedConstruction != null ? SelectedConstruction.ID : NullEntityID);
tempBuffer.WriteBoolean(true);
tempBuffer.WriteUInt16(SelectedCharacter != null ? SelectedCharacter.ID : NullEntityID);
tempBuffer.WriteUInt16(SelectedItem != null ? SelectedItem.ID : NullEntityID);
tempBuffer.WriteUInt16(SelectedSecondaryItem != null ? SelectedSecondaryItem.ID : NullEntityID);
if (SelectedCharacter != null)
{
tempBuffer.Write(AnimController.Anim == AnimController.Animation.CPR);
tempBuffer.WriteBoolean(AnimController.Anim == AnimController.Animation.CPR);
}
}
else
{
tempBuffer.Write(false);
tempBuffer.WriteBoolean(false);
}
tempBuffer.Write(SimPosition.X);
tempBuffer.Write(SimPosition.Y);
tempBuffer.WriteSingle(SimPosition.X);
tempBuffer.WriteSingle(SimPosition.Y);
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
AnimController.Collider.LinearVelocity = new Vector2(
MathHelper.Clamp(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel),
@@ -388,17 +389,17 @@ namespace Barotrauma
tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12);
bool fixedRotation = AnimController.Collider.FarseerBody.FixedRotation || !AnimController.Collider.PhysEnabled;
tempBuffer.Write(fixedRotation);
tempBuffer.WriteBoolean(fixedRotation);
if (!fixedRotation)
{
tempBuffer.Write(AnimController.Collider.Rotation);
tempBuffer.WriteSingle(AnimController.Collider.Rotation);
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
AnimController.Collider.AngularVelocity = NetConfig.Quantize(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8);
tempBuffer.WriteRangedSingle(MathHelper.Clamp(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel), -MaxAngularVel, MaxAngularVel, 8);
}
bool writeStatus = healthUpdateTimer <= 0.0f;
tempBuffer.Write(writeStatus);
tempBuffer.WriteBoolean(writeStatus);
if (writeStatus)
{
WriteStatus(tempBuffer);
@@ -409,7 +410,7 @@ namespace Barotrauma
tempBuffer.WritePadBits();
msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
msg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
}
public virtual void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
@@ -420,30 +421,30 @@ namespace Barotrauma
switch (eventData)
{
case InventoryStateEventData _:
msg.Write(GameMain.Server.EntityEventManager.Events.Last()?.ID ?? (ushort)0);
msg.WriteUInt16(GameMain.Server.EntityEventManager.Events.Last()?.ID ?? (ushort)0);
Inventory.ServerEventWrite(msg, c);
break;
case ControlEventData controlEventData:
Client owner = controlEventData.Owner;
msg.Write(owner == c && owner.Character == this);
msg.Write(owner != null && owner.Character == this && GameMain.Server.ConnectedClients.Contains(owner) ? owner.ID : (byte)0);
msg.WriteBoolean(owner == c && owner.Character == this);
msg.WriteByte(owner != null && owner.Character == this && GameMain.Server.ConnectedClients.Contains(owner) ? owner.SessionId : (byte)0);
break;
case CharacterStatusEventData _:
WriteStatus(msg);
case CharacterStatusEventData statusEventData:
WriteStatus(msg, statusEventData.ForceAfflictionData);
break;
case UpdateSkillsEventData _:
if (Info?.Job == null)
{
msg.Write((byte)0);
msg.WriteByte((byte)0);
}
else
{
var skills = Info.Job.GetSkills();
msg.Write((byte)skills.Count());
msg.WriteByte((byte)skills.Count());
foreach (Skill skill in skills)
{
msg.Write(skill.Identifier);
msg.Write(skill.Level);
msg.WriteIdentifier(skill.Identifier);
msg.WriteSingle(skill.Level);
}
}
break;
@@ -460,33 +461,33 @@ namespace Barotrauma
targetLimbIndex = targetLimbsArray.IndexOf(attackEventData.TargetLimb);
}
}
msg.Write((byte)(attackLimbIndex < 0 ? 255 : attackLimbIndex));
msg.Write((ushort)targetEntityId);
msg.Write((byte)(targetLimbIndex < 0 ? 255 : targetLimbIndex));
msg.Write(attackEventData.TargetSimPos.X);
msg.Write(attackEventData.TargetSimPos.Y);
msg.WriteByte((byte)(attackLimbIndex < 0 ? 255 : attackLimbIndex));
msg.WriteUInt16((ushort)targetEntityId);
msg.WriteByte((byte)(targetLimbIndex < 0 ? 255 : targetLimbIndex));
msg.WriteSingle(attackEventData.TargetSimPos.X);
msg.WriteSingle(attackEventData.TargetSimPos.Y);
}
break;
case AssignCampaignInteractionEventData _:
msg.Write((byte)CampaignInteractionType);
msg.Write(RequireConsciousnessForCustomInteract);
msg.WriteByte((byte)CampaignInteractionType);
msg.WriteBoolean(RequireConsciousnessForCustomInteract);
break;
case ObjectiveManagerStateEventData objectiveManagerStateEventData:
AIObjectiveManager.ObjectiveType type = objectiveManagerStateEventData.ObjectiveType;
msg.WriteRangedInteger((int)type, (int)AIObjectiveManager.ObjectiveType.MinValue, (int)AIObjectiveManager.ObjectiveType.MaxValue);
if (!(AIController is HumanAIController controller))
{
msg.Write(false);
msg.WriteBoolean(false);
break;
}
if (type == AIObjectiveManager.ObjectiveType.Order)
{
var currentOrderInfo = controller.ObjectiveManager.GetCurrentOrderInfo();
bool validOrder = currentOrderInfo != null;
msg.Write(validOrder);
msg.WriteBoolean(validOrder);
if (!validOrder) { break; }
var orderPrefab = currentOrderInfo.Prefab;
msg.Write(orderPrefab.UintIdentifier);
msg.WriteUInt32(orderPrefab.UintIdentifier);
if (!orderPrefab.HasOptions) { break; }
int optionIndex = orderPrefab.AllOptions.IndexOf(currentOrderInfo.Option);
if (optionIndex == -1)
@@ -499,65 +500,62 @@ namespace Barotrauma
{
var objective = controller.ObjectiveManager.CurrentObjective;
bool validObjective = objective?.Identifier is { IsEmpty: false };
msg.Write(validObjective);
msg.WriteBoolean(validObjective);
if (!validObjective) { break; }
msg.Write(objective.Identifier);
msg.Write(objective.Option);
msg.WriteIdentifier(objective.Identifier);
msg.WriteIdentifier(objective.Option);
UInt16 targetEntityId = 0;
if (objective is AIObjectiveOperateItem operateObjective && operateObjective.OperateTarget != null)
{
targetEntityId = operateObjective.OperateTarget.ID;
}
msg.Write(targetEntityId);
msg.WriteUInt16(targetEntityId);
}
break;
case TeamChangeEventData _:
msg.Write((byte)TeamID);
msg.WriteByte((byte)TeamID);
break;
case AddToCrewEventData addToCrewEventData:
msg.Write((byte)addToCrewEventData.TeamType); // team id
ushort[] inventoryItemIDs = addToCrewEventData.InventoryItems.Select(item => item.ID).ToArray();
msg.Write((ushort)inventoryItemIDs.Length);
for (int i = 0; i < inventoryItemIDs.Length; i++)
{
msg.Write(inventoryItemIDs[i]);
}
msg.WriteNetSerializableStruct(addToCrewEventData.ItemTeamChange);
break;
case RemoveFromCrewEventData removeFromCrewEventData:
msg.WriteNetSerializableStruct(removeFromCrewEventData.ItemTeamChange);
break;
case UpdateExperienceEventData _:
msg.Write(Info.ExperiencePoints);
msg.WriteInt32(Info.ExperiencePoints);
break;
case UpdateTalentsEventData _:
msg.Write((ushort)characterTalents.Count);
msg.WriteUInt16((ushort)characterTalents.Count);
foreach (var unlockedTalent in characterTalents)
{
msg.Write(unlockedTalent.AddedThisRound);
msg.Write(unlockedTalent.Prefab.UintIdentifier);
msg.WriteBoolean(unlockedTalent.AddedThisRound);
msg.WriteUInt32(unlockedTalent.Prefab.UintIdentifier);
}
break;
case UpdateMoneyEventData _:
msg.Write(GameMain.GameSession.Campaign.GetWallet(c).Balance);
msg.WriteInt32(GameMain.GameSession.Campaign.GetWallet(c).Balance);
break;
case UpdatePermanentStatsEventData updatePermanentStatsEventData:
StatTypes statType = updatePermanentStatsEventData.StatType;
if (Info == null)
{
msg.Write((byte)0);
msg.Write((byte)0);
msg.WriteByte((byte)0);
msg.WriteByte((byte)0);
}
else if (!Info.SavedStatValues.ContainsKey(statType))
{
msg.Write((byte)0);
msg.Write((byte)statType);
msg.WriteByte((byte)0);
msg.WriteByte((byte)statType);
}
else
{
msg.Write((byte)Info.SavedStatValues[statType].Count);
msg.Write((byte)statType);
msg.WriteByte((byte)Info.SavedStatValues[statType].Count);
msg.WriteByte((byte)statType);
foreach (var savedStatValue in Info.SavedStatValues[statType])
{
msg.Write(savedStatValue.StatIdentifier);
msg.Write(savedStatValue.StatValue);
msg.Write(savedStatValue.RemoveOnDeath);
msg.WriteString(savedStatValue.StatIdentifier);
msg.WriteSingle(savedStatValue.StatValue);
msg.WriteBoolean(savedStatValue.RemoveOnDeath);
}
}
break;
@@ -571,15 +569,15 @@ namespace Barotrauma
/// <param name="forceAfflictionData">Normally full affliction data is not written for dead characters, this can be used to force them to be written</param>
private void WriteStatus(IWriteMessage msg, bool forceAfflictionData = false)
{
msg.Write(IsDead);
msg.WriteBoolean(IsDead);
if (IsDead)
{
msg.WriteRangedInteger((int)CauseOfDeath.Type, 0, Enum.GetValues(typeof(CauseOfDeathType)).Length - 1);
if (CauseOfDeath.Type == CauseOfDeathType.Affliction)
{
msg.Write(CauseOfDeath.Affliction.Identifier);
msg.WriteUInt32(CauseOfDeath.Affliction.UintIdentifier);
}
msg.Write(forceAfflictionData);
msg.WriteBoolean(forceAfflictionData);
if (forceAfflictionData)
{
CharacterHealth.ServerWrite(msg);
@@ -592,7 +590,7 @@ namespace Barotrauma
if (AnimController?.LimbJoints == null)
{
//0 limbs severed
msg.Write((byte)0);
msg.WriteByte((byte)0);
}
else
{
@@ -604,10 +602,10 @@ namespace Barotrauma
severedJointIndices.Add(i);
}
}
msg.Write((byte)severedJointIndices.Count);
msg.WriteByte((byte)severedJointIndices.Count);
foreach (int jointIndex in severedJointIndices)
{
msg.Write((byte)jointIndex);
msg.WriteByte((byte)jointIndex);
}
}
}
@@ -618,23 +616,24 @@ namespace Barotrauma
int initialMsgLength = msg.LengthBytes;
msg.Write(Info == null);
msg.Write(entityId);
msg.Write(SpeciesName);
msg.Write(Seed);
msg.WriteBoolean(Info == null);
msg.WriteUInt16(entityId);
msg.WriteIdentifier(SpeciesName);
msg.WriteString(Seed);
if (Removed)
{
msg.Write(0.0f);
msg.Write(0.0f);
msg.WriteSingle(0.0f);
msg.WriteSingle(0.0f);
}
else
{
msg.Write(WorldPosition.X);
msg.Write(WorldPosition.Y);
msg.WriteSingle(WorldPosition.X);
msg.WriteSingle(WorldPosition.Y);
}
msg.Write(Enabled);
msg.WriteBoolean(Enabled);
msg.WriteBoolean(DisabledByEvent);
//character with no characterinfo (e.g. some monster)
if (Info == null)
@@ -646,54 +645,54 @@ namespace Barotrauma
Client ownerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == this);
if (ownerClient != null)
{
msg.Write(true);
msg.Write(ownerClient.ID);
msg.WriteBoolean(true);
msg.WriteByte(ownerClient.SessionId);
}
else if (GameMain.Server.Character == this)
{
msg.Write(true);
msg.Write((byte)0);
msg.WriteBoolean(true);
msg.WriteByte((byte)0);
}
else
{
msg.Write(false);
msg.WriteBoolean(false);
}
msg.Write(HumanPrefabHealthMultiplier);
msg.Write(Wallet.Balance);
msg.WriteSingle(HumanPrefabHealthMultiplier);
msg.WriteInt32(Wallet.Balance);
msg.WriteRangedInteger(Wallet.RewardDistribution, 0, 100);
msg.Write((byte)TeamID);
msg.Write(this is AICharacter);
msg.Write(info.SpeciesName);
msg.WriteByte((byte)TeamID);
msg.WriteBoolean(this is AICharacter);
msg.WriteIdentifier(info.SpeciesName);
int msgLengthBeforeInfo = msg.LengthBytes;
info.ServerWrite(msg);
int infoLength = msg.LengthBytes - msgLengthBeforeInfo;
msg.Write((byte)CampaignInteractionType);
msg.WriteByte((byte)CampaignInteractionType);
if (CampaignInteractionType == CampaignMode.InteractionType.Store)
{
msg.Write(MerchantIdentifier);
msg.WriteIdentifier(MerchantIdentifier);
}
int msgLengthBeforeOrders = msg.LengthBytes;
// Current orders
msg.Write((byte)info.CurrentOrders.Count(o => o != null));
msg.WriteByte((byte)info.CurrentOrders.Count(o => o != null));
foreach (var orderInfo in info.CurrentOrders)
{
if (orderInfo == null) { continue; }
msg.Write(orderInfo.Prefab.UintIdentifier);
msg.Write(orderInfo.TargetEntity == null ? (UInt16)0 : orderInfo.TargetEntity.ID);
msg.WriteUInt32(orderInfo.Prefab.UintIdentifier);
msg.WriteUInt16(orderInfo.TargetEntity == null ? (UInt16)0 : orderInfo.TargetEntity.ID);
var hasOrderGiver = orderInfo.OrderGiver != null;
msg.Write(hasOrderGiver);
if (hasOrderGiver) { msg.Write(orderInfo.OrderGiver.ID); }
msg.Write((byte)(orderInfo.Option == Identifier.Empty ? 0 : orderInfo.Prefab.Options.IndexOf(orderInfo.Option)));
msg.Write((byte)orderInfo.ManualPriority);
msg.WriteBoolean(hasOrderGiver);
if (hasOrderGiver) { msg.WriteUInt16(orderInfo.OrderGiver.ID); }
msg.WriteByte((byte)(orderInfo.Option == Identifier.Empty ? 0 : orderInfo.Prefab.Options.IndexOf(orderInfo.Option)));
msg.WriteByte((byte)orderInfo.ManualPriority);
var hasTargetPosition = orderInfo.TargetPosition != null;
msg.Write(hasTargetPosition);
msg.WriteBoolean(hasTargetPosition);
if (hasTargetPosition)
{
msg.Write(orderInfo.TargetPosition.Position.X);
msg.Write(orderInfo.TargetPosition.Position.Y);
msg.Write(orderInfo.TargetPosition.Hull == null ? (UInt16)0 : orderInfo.TargetPosition.Hull.ID);
msg.WriteSingle(orderInfo.TargetPosition.Position.X);
msg.WriteSingle(orderInfo.TargetPosition.Position.Y);
msg.WriteUInt16(orderInfo.TargetPosition.Hull == null ? (UInt16)0 : orderInfo.TargetPosition.Hull.ID);
}
}
int ordersLength = msg.LengthBytes - msgLengthBeforeOrders;
@@ -715,7 +714,7 @@ namespace Barotrauma
WriteStatus(tempBuffer, forceAfflictionData: true);
if (msgLengthBeforeStatus + tempBuffer.LengthBytes >= 255 && restrictMessageSize && GameMain.LuaCs.Networking.RestrictMessageSize)
{
msg.Write(false);
msg.WriteBoolean(false);
if (msgLengthBeforeStatus < 255)
{
string errorMsg = $"Error when writing character spawn data for \"{Name}\": status data caused the length of the message to exceed 255 bytes ({msgLengthBeforeStatus} + {tempBuffer.LengthBytes})";
@@ -725,7 +724,7 @@ namespace Barotrauma
}
else
{
msg.Write(true);
msg.WriteBoolean(true);
WriteStatus(msg, forceAfflictionData: true);
}
}
@@ -8,6 +8,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Barotrauma.Steam;
namespace Barotrauma
{
@@ -300,10 +301,16 @@ namespace Barotrauma
Client client = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name, arg));
if (int.TryParse(arg, out int id))
{
client ??= GameMain.Server.ConnectedClients.Find(c => c.ID == id);
client ??= GameMain.Server.ConnectedClients.Find(c => c.SessionId == id);
}
if (Address.Parse(arg).TryUnwrap(out var address))
{
client ??= GameMain.Server.ConnectedClients.Find(c => c.AddressMatches(address));
}
if (AccountId.Parse(arg).TryUnwrap(out var argAccountId))
{
client ??= GameMain.Server.ConnectedClients.Find(c => c.AccountId.ValueEquals(argAccountId));
}
client ??= GameMain.Server.ConnectedClients.Find(c => c.EndpointMatches(arg));
client ??= GameMain.Server.ConnectedClients.Find(c => c.SteamID == Steam.SteamManager.SteamIDStringToUInt64(arg));
return client;
}
@@ -873,7 +880,7 @@ namespace Barotrauma
NewMessage("***************", Color.Cyan);
foreach (Client c in GameMain.Server.ConnectedClients)
{
NewMessage("- " + c.ID.ToString() + ": " + c.Name + (c.Character != null ? " playing " + c.Character.LogName : "") + ", " + c.Karma, Color.Cyan);
NewMessage("- " + c.SessionId.ToString() + ": " + c.Name + (c.Character != null ? " playing " + c.Character.LogName : "") + ", " + c.Karma, Color.Cyan);
}
NewMessage("***************", Color.Cyan);
});
@@ -882,7 +889,7 @@ namespace Barotrauma
GameMain.Server.SendConsoleMessage("***************", client);
foreach (Client c in GameMain.Server.ConnectedClients)
{
GameMain.Server.SendConsoleMessage("- " + c.ID.ToString() + ": " + c.Name + (c.Character != null ? " playing " + c.Character.LogName : "") + ", " + c.Karma, client);
GameMain.Server.SendConsoleMessage("- " + c.SessionId.ToString() + ": " + c.Name + (c.Character != null ? " playing " + c.Character.LogName : "") + ", " + c.Karma, client);
}
GameMain.Server.SendConsoleMessage("***************", client);
});
@@ -905,10 +912,12 @@ namespace Barotrauma
client);
});
AssignOnExecute("banendpoint", (string[] args) =>
AssignOnExecute("banaddress", (string[] args) =>
{
if (GameMain.Server == null || args.Length == 0) return;
if (!(Address.Parse(args[0]).TryUnwrap(out var address))) { return; }
ShowQuestionPrompt("Reason for banning the endpoint \"" + args[0] + "\"? (c to cancel)", (reason) =>
{
if (reason == "c" || reason == "C") { return; }
@@ -926,16 +935,16 @@ namespace Barotrauma
banDuration = parsedBanDuration;
}
var clients = GameMain.Server.ConnectedClients.FindAll(c => c.EndpointMatches(args[0]));
var clients = GameMain.Server.ConnectedClients.Where(c => c.AddressMatches(address)).ToList();
if (clients.Count == 0)
{
GameMain.Server.ServerSettings.BanList.BanPlayer("Unnamed", args[0], reason, banDuration);
GameMain.Server.ServerSettings.BanList.BanPlayer("Unnamed", address, reason, banDuration);
}
else
{
foreach (Client cl in clients)
{
GameMain.Server.BanClient(cl, reason, false, banDuration);
GameMain.Server.BanClient(cl, reason, banDuration);
}
}
});
@@ -1037,7 +1046,8 @@ namespace Barotrauma
NewMessage("***************", Color.Cyan);
foreach (Client c in GameMain.Server.ConnectedClients)
{
NewMessage("- " + c.ID.ToString() + ": " + c.Name + (c.Character != null ? " playing " + c.Character.LogName : "") + ", " + c.Connection.EndPointString + $", ping {c.Ping} ms", Color.Cyan);
NewMessage(
$"- {c.SessionId}: {c.Name}{(c.Character != null ? " playing " + c.Character.LogName : "")}, {c.Connection.Endpoint.StringRepresentation}, {c.Connection.AccountInfo.AccountId}, ping {c.Ping} ms", Color.Cyan);
}
NewMessage("***************", Color.Cyan);
}));
@@ -1046,7 +1056,7 @@ namespace Barotrauma
GameMain.Server.SendConsoleMessage("***************", client, Color.Cyan);
foreach (Client c in GameMain.Server.ConnectedClients)
{
GameMain.Server.SendConsoleMessage("- " + c.ID.ToString() + ": " + c.Name + ", " + c.Connection.EndPointString + $", ping {c.Ping} ms", client, Color.Cyan);
GameMain.Server.SendConsoleMessage("- " + c.SessionId.ToString() + ": " + c.Name + ", " + c.Connection.Endpoint.StringRepresentation + $", ping {c.Ping} ms", client, Color.Cyan);
}
GameMain.Server.SendConsoleMessage("***************", client, Color.Cyan);
});
@@ -1530,11 +1540,12 @@ namespace Barotrauma
);
AssignOnClientRequestExecute(
"banendpoint|banip",
"banaddress|banip",
(Client client, Vector2 cursorPos, string[] args) =>
{
if (args.Length < 1) return;
var clients = GameMain.Server.ConnectedClients.FindAll(c => c.EndpointMatches(args[0]));
if (args.Length < 1) { return; }
if (!(Address.Parse(args[0]).TryUnwrap(out var address))) { return; }
var clients = GameMain.Server.ConnectedClients.Where(c => c.AddressMatches(address)).ToList();
TimeSpan? duration = null;
if (args.Length > 1)
{
@@ -1553,13 +1564,13 @@ namespace Barotrauma
if (clients.Count == 0)
{
GameMain.Server.ServerSettings.BanList.BanPlayer("Unnamed", args[0], reason, duration);
GameMain.Server.ServerSettings.BanList.BanPlayer("Unnamed", address, reason, duration);
}
else
{
foreach (Client cl in clients)
{
GameMain.Server.BanClient(cl, reason, false, duration);
GameMain.Server.BanClient(cl, reason, duration);
}
}
}
@@ -1569,7 +1580,7 @@ namespace Barotrauma
{
if (GameMain.Server == null || args.Length == 0) return;
string clientName = string.Join(" ", args);
GameMain.Server.UnbanPlayer(clientName, "");
GameMain.Server.UnbanPlayer(clientName);
},
() =>
{
@@ -1580,17 +1591,20 @@ namespace Barotrauma
};
}));
commands.Add(new Command("unbanip", "unbanip [ip]: Unban a specific IP.", (string[] args) =>
commands.Add(new Command("unbanaddress", "unbanaddress [endpoint]: Unban a specific endpoint.", (string[] args) =>
{
if (GameMain.Server == null || args.Length == 0) return;
GameMain.Server.UnbanPlayer("", args[0]);
if (Endpoint.Parse(args[0]).TryUnwrap(out var endpoint))
{
GameMain.Server.UnbanPlayer(endpoint);
}
},
() =>
{
if (GameMain.Server == null) return null;
return new string[][]
{
GameMain.Server.ServerSettings.BanList.BannedEndPoints.ToArray()
GameMain.Server.ServerSettings.BanList.BannedAddresses.Select(ep => ep.ToString()).ToArray()
};
}));
@@ -1886,20 +1900,17 @@ namespace Barotrauma
}
foreach (var talentTree in talentTrees)
{
foreach (var subTree in talentTree.TalentSubTrees)
{
foreach (var talentId in talentTree.AllTalentIdentifiers)
{
foreach (var option in subTree.TalentOptionStages)
if (TalentPrefab.TalentPrefabs.TryGet(talentId, out TalentPrefab talentPrefab))
{
foreach (var talent in option.Talents)
{
targetCharacter.GiveTalent(talent);
NewMessage($"Talent \"{talent.DisplayName}\" given to \"{targetCharacter.Name}\" by \"{client.Name}\".");
GameMain.Server.SendConsoleMessage($"Gave talent \"{talent.DisplayName}\" to \"{targetCharacter.Name}\".", client);
NewMessage($"Unlocked talent \"{talent.DisplayName}\".");
}
targetCharacter.GiveTalent(talentPrefab);
NewMessage($"Talent \"{talentPrefab.DisplayName}\" given to \"{targetCharacter.Name}\" by \"{client.Name}\".");
GameMain.Server.SendConsoleMessage($"Gave talent \"{talentPrefab.DisplayName}\" to \"{targetCharacter.Name}\".", client);
NewMessage($"Unlocked talent \"{talentPrefab.DisplayName}\".");
}
}
}
}
}
);
@@ -58,7 +58,7 @@ namespace Barotrauma
Reset();
}
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> targets)
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> targets, float duration)
{
foreach (Entity e in targets)
{
@@ -68,7 +68,7 @@ namespace Barotrauma
{
if (lastActiveAction.ContainsKey(targetClient) &&
lastActiveAction[targetClient].ParentEvent != ParentEvent &&
Timing.TotalTime < lastActiveAction[targetClient].lastActiveTime + BlockOtherConversationsDuration)
Timing.TotalTime < lastActiveAction[targetClient].lastActiveTime + duration)
{
return true;
}
@@ -91,7 +91,7 @@ namespace Barotrauma
{
targetClients.Add(targetClient);
lastActiveAction[targetClient] = this;
ServerWrite(speaker, targetClient);
ServerWrite(speaker, targetClient, interrupt);
}
}
}
@@ -105,46 +105,46 @@ namespace Barotrauma
{
targetClients.Add(c);
lastActiveAction[c] = this;
ServerWrite(speaker, c);
ServerWrite(speaker, c, interrupt);
}
}
}
}
}
private void ServerWrite(Character speaker, Client client)
public void ServerWrite(Character speaker, Client client, bool interrupt)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
outmsg.Write((byte)EventManager.NetworkEventType.CONVERSATION);
outmsg.Write(Identifier);
outmsg.Write(EventSprite);
outmsg.Write((byte)DialogType);
outmsg.Write(ContinueConversation);
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
outmsg.WriteByte((byte)EventManager.NetworkEventType.CONVERSATION);
outmsg.WriteUInt16(Identifier);
outmsg.WriteString(EventSprite);
outmsg.WriteByte((byte)DialogType);
outmsg.WriteBoolean(ContinueConversation);
if (interrupt)
{
outmsg.Write(speaker?.ID ?? Entity.NullEntityID);
outmsg.Write(string.Empty);
outmsg.Write(false);
outmsg.Write((byte)0);
outmsg.Write((byte)0);
outmsg.WriteUInt16(speaker?.ID ?? Entity.NullEntityID);
outmsg.WriteString(string.Empty);
outmsg.WriteBoolean(false);
outmsg.WriteByte((byte)0);
outmsg.WriteByte((byte)0);
}
else
{
outmsg.Write(speaker?.ID ?? Entity.NullEntityID);
outmsg.Write(Text ?? string.Empty);
outmsg.Write(FadeToBlack);
outmsg.Write((byte)Options.Count);
outmsg.WriteUInt16(speaker?.ID ?? Entity.NullEntityID);
outmsg.WriteString(Text ?? string.Empty);
outmsg.WriteBoolean(FadeToBlack);
outmsg.WriteByte((byte)Options.Count);
for (int i = 0; i < Options.Count; i++)
{
outmsg.Write(Options[i].Text);
outmsg.WriteString(Options[i].Text);
}
int[] endings = GetEndingOptions();
outmsg.Write((byte)endings.Length);
outmsg.WriteByte((byte)endings.Length);
foreach (var end in endings)
{
outmsg.Write((byte)end);
outmsg.WriteByte((byte)end);
}
}
GameMain.Server?.ServerPeer?.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
@@ -10,14 +10,14 @@ namespace Barotrauma
private void ServerWrite(IEnumerable<Entity> targets)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
outmsg.Write((byte)EventManager.NetworkEventType.STATUSEFFECT);
outmsg.Write(ParentEvent.Prefab.Identifier);
outmsg.Write((UInt16)actionIndex);
outmsg.Write((UInt16)targets.Count());
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
outmsg.WriteByte((byte)EventManager.NetworkEventType.STATUSEFFECT);
outmsg.WriteIdentifier(ParentEvent.Prefab.Identifier);
outmsg.WriteUInt16((UInt16)actionIndex);
outmsg.WriteUInt16((UInt16)targets.Count());
foreach (Entity target in targets)
{
outmsg.Write(target.ID);
outmsg.WriteUInt16(target.ID);
}
foreach (Client c in GameMain.Server.ConnectedClients)
{
@@ -27,16 +27,24 @@ namespace Barotrauma
#endif
continue;
}
if (selectedOption == byte.MaxValue)
if (convAction.SelectedOption > -1)
{
convAction.IgnoreClient(sender, 3f);
//someone else already chose an option for this conversation: interrupt for this client
convAction.ServerWrite(convAction.speaker, sender, interrupt: true);
}
else
{
convAction.SelectedOption = selectedOption;
if (selectedOption == byte.MaxValue)
{
convAction.IgnoreClient(sender, 3f);
}
else
{
convAction.SelectedOption = selectedOption;
}
}
return;
return;
}
}
}
@@ -12,19 +12,19 @@ namespace Barotrauma
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
msg.Write((ushort)spawnedItems.Count);
msg.WriteUInt16((ushort)spawnedItems.Count);
foreach (Item item in spawnedItems)
{
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0, -1);
}
msg.Write((byte)characters.Count);
msg.WriteByte((byte)characters.Count);
foreach (Character character in characters)
{
character.WriteSpawnData(msg, character.ID, restrictMessageSize: false);
msg.Write(requireKill.Contains(character));
msg.Write(requireRescue.Contains(character));
msg.Write((ushort)characterItems[character].Count());
msg.WriteBoolean(requireKill.Contains(character));
msg.WriteBoolean(requireRescue.Contains(character));
msg.WriteUInt16((ushort)characterItems[character].Count());
foreach (Item item in characterItems[character])
{
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0, item.ParentInventory?.FindIndex(item) ?? -1);
@@ -7,12 +7,12 @@ namespace Barotrauma
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
msg.Write((ushort)existingTargets.Count);
msg.WriteUInt16((ushort)existingTargets.Count);
foreach (var t in existingTargets)
{
msg.Write(t != null ? t.ID : Entity.NullEntityID);
msg.WriteUInt16(t != null ? t.ID : Entity.NullEntityID);
}
msg.Write((ushort)spawnedTargets.Count);
msg.WriteUInt16((ushort)spawnedTargets.Count);
foreach (var t in spawnedTargets)
{
t.WriteSpawnData(msg, t.ID, false);
@@ -7,7 +7,7 @@ namespace Barotrauma
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
msg.Write((ushort)items.Count);
msg.WriteUInt16((ushort)items.Count);
foreach (Item item in items)
{
item.WriteSpawnData(msg,
@@ -5,12 +5,16 @@ namespace Barotrauma
{
partial class CombatMission
{
const float RoundEndDuration = 5.0f;
private readonly bool[] teamDead = new bool[2];
private List<Character>[] crews;
private bool initialized = false;
private float roundEndTimer;
public override LocalizedString Description
{
get
@@ -53,6 +57,7 @@ namespace Barotrauma
{
teamDead[0] = crews[0].All(c => c.IsDead || c.IsIncapacitated);
teamDead[1] = crews[1].All(c => c.IsDead || c.IsIncapacitated);
if (teamDead[0] && teamDead[1]) { state = 1; }
}
if (state == 0)
@@ -66,13 +71,17 @@ namespace Barotrauma
GameMain.GameSession.WinningTeam = i == 0 ? CharacterTeamType.Team1 : CharacterTeamType.Team2;
state = 1;
//state 1 = team 1 won, 2 = team 2 won
State = i + 1;
break;
}
}
}
else
{
roundEndTimer -= deltaTime;
if (roundEndTimer > 0.0f) { return; }
if (teamDead[0] && teamDead[1])
{
GameMain.GameSession.WinningTeam = CharacterTeamType.None;
@@ -81,7 +90,7 @@ namespace Barotrauma
else if (GameMain.GameSession.WinningTeam != CharacterTeamType.None)
{
GameMain.Server.EndGame();
}
}
}
}
}
@@ -16,12 +16,12 @@ namespace Barotrauma
throw new InvalidOperationException("Server attempted to write escort mission data when no characters had been spawned.");
}
msg.Write((byte)characters.Count);
msg.WriteByte((byte)characters.Count);
foreach (Character character in characters)
{
character.WriteSpawnData(msg, character.ID, restrictMessageSize: false);
msg.Write(terroristCharacters.Contains(character));
msg.Write((ushort)characterItems[character].Count());
msg.WriteBoolean(terroristCharacters.Contains(character));
msg.WriteUInt16((ushort)characterItems[character].Count());
foreach (Item item in characterItems[character])
{
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0, item.ParentInventory?.FindIndex(item) ?? -1);
@@ -7,17 +7,17 @@ namespace Barotrauma
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
msg.Write((byte)caves.Count);
msg.WriteByte((byte)caves.Count);
foreach (var cave in caves)
{
msg.Write((byte)(Level.Loaded == null || !Level.Loaded.Caves.Contains(cave) ? 255 : Level.Loaded.Caves.IndexOf(cave)));
msg.WriteByte((byte)(Level.Loaded == null || !Level.Loaded.Caves.Contains(cave) ? 255 : Level.Loaded.Caves.IndexOf(cave)));
}
foreach (var kvp in spawnedResources)
{
msg.Write((byte)kvp.Value.Count);
msg.WriteByte((byte)kvp.Value.Count);
var rotation = resourceClusters[kvp.Key].Rotation;
msg.Write(rotation);
msg.WriteSingle(rotation);
foreach (var r in kvp.Value)
{
r.WriteSpawnData(msg, r.ID, Entity.NullEntityID, 0, -1);
@@ -26,11 +26,11 @@ namespace Barotrauma
foreach (var kvp in relevantLevelResources)
{
msg.Write(kvp.Key);
msg.Write((byte)kvp.Value.Length);
msg.WriteIdentifier(kvp.Key);
msg.WriteByte((byte)kvp.Value.Length);
foreach (var i in kvp.Value)
{
msg.Write(i.ID);
msg.WriteUInt16(i.ID);
}
}
}
@@ -12,18 +12,22 @@ namespace Barotrauma
LocalizedString header = messageIndex < Headers.Length ? Headers[messageIndex] : "";
LocalizedString message = messageIndex < Messages.Length ? Messages[messageIndex] : "";
if (!message.IsNullOrEmpty())
{
message = ModifyMessage(message, color: false);
}
GameServer.Log($"{TextManager.Get("MissionInfo")}: {header} - {message}", ServerLog.MessageType.ServerMessage);
}
public virtual void ServerWriteInitial(IWriteMessage msg, Client c)
{
msg.Write((ushort)State);
msg.WriteUInt16((ushort)State);
}
public virtual void ServerWrite(IWriteMessage msg)
{
msg.Write((ushort)State);
msg.WriteUInt16((ushort)State);
}
}
}
@@ -14,7 +14,7 @@ namespace Barotrauma
throw new InvalidOperationException("Server attempted to write monster mission data when no monsters had been spawned.");
}
msg.Write((byte)monsters.Count);
msg.WriteByte((byte)monsters.Count);
foreach (Character monster in monsters)
{
monster.WriteSpawnData(msg, monster.ID, restrictMessageSize: false);
@@ -9,10 +9,10 @@ namespace Barotrauma
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
msg.Write((byte)(selectedCave == null || Level.Loaded == null || !Level.Loaded.Caves.Contains(selectedCave) ? 255 : Level.Loaded.Caves.IndexOf(selectedCave)));
msg.Write(nestPosition.X);
msg.Write(nestPosition.Y);
msg.Write((ushort)items.Count);
msg.WriteByte((byte)(selectedCave == null || Level.Loaded == null || !Level.Loaded.Caves.Contains(selectedCave) ? 255 : Level.Loaded.Caves.IndexOf(selectedCave)));
msg.WriteSingle(nestPosition.X);
msg.WriteSingle(nestPosition.Y);
msg.WriteUInt16((ushort)items.Count);
foreach (Item item in items)
{
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0, -1);
@@ -17,11 +17,11 @@ namespace Barotrauma
throw new InvalidOperationException("Server attempted to write escort mission data when no characters had been spawned.");
}
msg.Write((byte)characters.Count);
msg.WriteByte((byte)characters.Count);
foreach (Character character in characters)
{
character.WriteSpawnData(msg, character.ID, restrictMessageSize: false);
msg.Write((ushort)characterItems[character].Count());
msg.WriteUInt16((ushort)characterItems[character].Count());
foreach (Item item in characterItems[character])
{
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0, item.ParentInventory?.FindIndex(item) ?? -1);
@@ -18,21 +18,21 @@ namespace Barotrauma
{
base.ServerWriteInitial(msg, c);
msg.Write(usedExistingItem);
msg.WriteBoolean(usedExistingItem);
if (usedExistingItem)
{
msg.Write(item.ID);
msg.WriteUInt16(item.ID);
}
else
{
item.WriteSpawnData(msg, item.ID, originalInventoryID, originalItemContainerIndex, originalSlotIndex);
}
msg.Write((byte)executedEffectIndices.Count);
msg.WriteByte((byte)executedEffectIndices.Count);
foreach (Pair<int, int> effectIndex in executedEffectIndices)
{
msg.Write((byte)effectIndex.First);
msg.Write((byte)effectIndex.Second);
msg.WriteByte((byte)effectIndex.First);
msg.WriteByte((byte)effectIndex.Second);
}
}
}
@@ -7,7 +7,7 @@ namespace Barotrauma
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
msg.Write((ushort)startingItems.Count);
msg.WriteUInt16((ushort)startingItems.Count);
foreach (var item in startingItems)
{
item.WriteSpawnData(msg,
@@ -27,11 +27,11 @@ namespace Barotrauma
private void ServerWriteScanTargetStatus(IWriteMessage msg)
{
msg.Write((byte)scanTargets.Count);
msg.WriteByte((byte)scanTargets.Count);
foreach (var kvp in scanTargets)
{
msg.Write(kvp.Key != null ? kvp.Key.ID : Entity.NullEntityID);
msg.Write(kvp.Value);
msg.WriteUInt16(kvp.Key != null ? kvp.Key.ID : Entity.NullEntityID);
msg.WriteBoolean(kvp.Value);
}
}
}
@@ -157,9 +157,9 @@ namespace Barotrauma
string password = "";
bool enableUpnp = false;
int maxPlayers = 10;
int? ownerKey = null;
UInt64 steamId = 0;
int maxPlayers = 10;
Option<int> ownerKey = Option<int>.None();
Option<SteamId> steamId = Option<SteamId>.None();
IPAddress listenIp = IPAddress.Any;
XDocument doc = XMLExtensions.TryLoadXml(ServerSettings.SettingsFile);
@@ -176,7 +176,7 @@ namespace Barotrauma
password = doc.Root.GetAttributeString("password", "");
enableUpnp = doc.Root.GetAttributeBool("enableupnp", false);
maxPlayers = doc.Root.GetAttributeInt("maxplayers", 10);
ownerKey = null;
ownerKey = Option<int>.None();
}
#if DEBUG
@@ -231,12 +231,12 @@ namespace Barotrauma
case "-ownerkey":
if (int.TryParse(CommandLineArgs[i + 1], out int key))
{
ownerKey = key;
ownerKey = Option<int>.Some(key);
}
i++;
break;
case "-steamid":
UInt64.TryParse(CommandLineArgs[i + 1], out steamId);
steamId = SteamId.Parse(CommandLineArgs[i + 1]);
i++;
break;
case "-pipes":
@@ -257,6 +257,7 @@ namespace Barotrauma
maxPlayers,
ownerKey,
steamId);
Server.StartServer();
for (int i = 0; i < CommandLineArgs.Length; i++)
{
@@ -288,7 +289,7 @@ namespace Barotrauma
public void CloseServer()
{
Server?.Disconnect();
Server?.Quit();
ShouldRun = false;
Server = null;
}
@@ -345,7 +346,7 @@ namespace Barotrauma
if (Server == null) { break; }
SteamManager.Update((float)Timing.Step);
TaskPool.Update();
CoroutineManager.Update((float)Timing.Step, (float)Timing.Step);
CoroutineManager.Update(paused: false, (float)Timing.Step);
GameMain.LuaCs.Update();
GameMain.LuaCs.Hook.Call("think", new object[] { });
@@ -47,7 +47,7 @@ namespace Barotrauma
public void ServerWriteActiveOrders(IWriteMessage msg)
{
ushort count = (ushort)ActiveOrders.Count(o => o.Order != null && !o.FadeOutTime.HasValue);
msg.Write(count);
msg.WriteUInt16(count);
if (count > 0)
{
foreach (var activeOrder in ActiveOrders)
@@ -55,10 +55,10 @@ namespace Barotrauma
if (!(activeOrder?.Order is Order order) || activeOrder.FadeOutTime.HasValue) { continue; }
OrderChatMessage.WriteOrder(msg, order, null, isNewOrder: true);
bool hasOrderGiver = order.OrderGiver != null;
msg.Write(hasOrderGiver);
msg.WriteBoolean(hasOrderGiver);
if (hasOrderGiver)
{
msg.Write(order.OrderGiver.ID);
msg.WriteUInt16(order.OrderGiver.ID);
}
}
}
@@ -6,6 +6,8 @@ namespace Barotrauma
{
private readonly Queue<WalletChangedData> transactions = new Queue<WalletChangedData>();
public bool ShouldForceUpdate;
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged)
{
transactions.Enqueue(new WalletChangedData
@@ -15,6 +17,15 @@ namespace Barotrauma
});
}
/// <summary>
/// Forces the server to sync the state of the wallet regardless if the balance/reward has changed
/// </summary>
public void ForceUpdate()
{
SettingsChanged(balanceChanged: Option<int>.Some(0), rewardChanged: Option<int>.None());
ShouldForceUpdate = true;
}
public bool HasTransactions() => transactions.Count > 0;
public NetWalletTransaction DequeueAndMergeTransactions(ushort id)
@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using System;
using System.Xml.Linq;
namespace Barotrauma
@@ -15,24 +16,27 @@ namespace Barotrauma
public CharacterCampaignData(Client client)
{
Name = client.Name;
ClientEndPoint = client.Connection.EndPointString;
SteamID = client.SteamID;
ClientAddress = client.Connection.Endpoint.Address;
AccountId = client.AccountId;
CharacterInfo = client.CharacterInfo;
healthData = new XElement("health");
client.Character?.CharacterHealth?.Save(healthData);
if (client.Character?.Inventory != null)
//the character may not be controlled by the client atm, but still exist
Character character = client.Character ?? CharacterInfo?.Character;
character?.CharacterHealth?.Save(healthData);
if (character?.Inventory != null)
{
itemData = new XElement("inventory");
Character.SaveInventory(client.Character.Inventory, itemData);
Character.SaveInventory(character.Inventory, itemData);
}
OrderData = new XElement("orders");
if (client.CharacterInfo != null)
if (CharacterInfo != null)
{
CharacterInfo.SaveOrderData(client.CharacterInfo, OrderData);
CharacterInfo.SaveOrderData(CharacterInfo, OrderData);
}
if (client.Character?.Wallet.Save() is { } walletSave)
if (character?.Wallet.Save() is { } walletSave)
{
WalletData = walletSave;
}
@@ -55,13 +59,13 @@ namespace Barotrauma
public CharacterCampaignData(XElement element)
{
Name = element.GetAttributeString("name", "Unnamed");
ClientEndPoint = element.GetAttributeString("endpoint", null) ?? element.GetAttributeString("ip", "");
string steamID = element.GetAttributeString("steamid", "");
if (!string.IsNullOrEmpty(steamID))
{
ulong.TryParse(steamID, out ulong parsedID);
SteamID = parsedID;
}
string clientEndPointStr = element.GetAttributeString("address", null)
?? element.GetAttributeString("endpoint", null)
?? element.GetAttributeString("ip", "");
ClientAddress = Address.Parse(clientEndPointStr).Fallback(new UnknownAddress());
string accountIdStr = element.GetAttributeString("accountid", null)
?? element.GetAttributeString("steamid", "");
AccountId = Networking.AccountId.Parse(accountIdStr);
foreach (XElement subElement in element.Elements())
{
@@ -89,19 +93,20 @@ namespace Barotrauma
public bool MatchesClient(Client client)
{
if (SteamID > 0)
if (AccountId.TryUnwrap(out var accountId)
&& client.AccountId.TryUnwrap(out var clientId))
{
return SteamID == client.SteamID;
return accountId == clientId;
}
else
{
return ClientEndPoint == client.Connection.EndPointString;
return ClientAddress == client.Connection.Endpoint.Address;
}
}
public bool IsDuplicate(CharacterCampaignData other)
{
return other.SteamID == SteamID && other.ClientEndPoint == ClientEndPoint;
return AccountId == other.AccountId && other.ClientAddress == ClientAddress;
}
public void SpawnInventoryItems(Character character, Inventory inventory)
@@ -117,9 +122,9 @@ namespace Barotrauma
character.SpawnInventoryItems(inventory, itemData.FromPackage(null));
}
public void ApplyHealthData(Character character)
public void ApplyHealthData(Character character, Func<AfflictionPrefab, bool> afflictionPredicate = null)
{
CharacterInfo.ApplyHealthData(character, healthData);
CharacterInfo.ApplyHealthData(character, healthData, afflictionPredicate);
}
public void ApplyOrderData(Character character)
@@ -136,8 +141,8 @@ namespace Barotrauma
{
XElement element = new XElement("CharacterCampaignData",
new XAttribute("name", Name),
new XAttribute("endpoint", ClientEndPoint),
new XAttribute("steamid", SteamID));
new XAttribute("address", ClientAddress),
new XAttribute("accountid", AccountId.TryUnwrap(out var accountId) ? accountId.StringRepresentation : ""));
CharacterInfo?.Save(element);
if (itemData != null) { element.Add(itemData); }
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Steam;
namespace Barotrauma
{
@@ -45,21 +46,26 @@ namespace Barotrauma
class SavedExperiencePoints
{
public readonly ulong SteamID;
public readonly string EndPoint;
public readonly Option<AccountId> AccountId;
public readonly Address Address;
public readonly int ExperiencePoints;
public SavedExperiencePoints(Client client)
{
SteamID = client.SteamID;
EndPoint = client.Connection.EndPointString;
AccountId = client.AccountId;
Address = client.Connection.Endpoint.Address;
ExperiencePoints = client.Character?.Info?.ExperiencePoints ?? 0;
}
public SavedExperiencePoints(XElement element)
{
SteamID = element.GetAttributeUInt64("steamid", 0);
EndPoint = element.GetAttributeString("endpoint", string.Empty);
AccountId = Networking.AccountId.Parse(
element.GetAttributeString("accountid", null)
?? element.GetAttributeString("steamid", ""));
Address = Address.Parse(
element.GetAttributeString("address", null)
?? element.GetAttributeString("endpoint", ""))
.Fallback(new UnknownAddress());
ExperiencePoints = element.GetAttributeInt("points", 0);
}
}
@@ -202,11 +208,11 @@ namespace Barotrauma
}
public int GetSavedExperiencePoints(Client client)
{
return savedExperiencePoints.Find(s => s.SteamID != 0 && client.SteamID == s.SteamID || client.EndpointMatches(s.EndPoint))?.ExperiencePoints ?? 0;
return savedExperiencePoints.Find(s => client.AccountId == s.AccountId || client.Connection.Endpoint.Address == s.Address)?.ExperiencePoints ?? 0;
}
public void ClearSavedExperiencePoints(Client client)
{
savedExperiencePoints.RemoveAll(s => s.SteamID != 0 && client.SteamID == s.SteamID || client.EndpointMatches(s.EndPoint));
savedExperiencePoints.RemoveAll(s => client.AccountId == s.AccountId || client.Connection.Endpoint.Address == s.Address);
}
public void SavePlayers()
@@ -354,6 +360,7 @@ namespace Barotrauma
LeaveUnconnectedSubs(leavingSub);
NextLevel = newLevel;
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
GameMain.GameSession.EventManager.RegisterEventHistory();
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
}
else
@@ -530,8 +537,9 @@ namespace Barotrauma
if (wallet.HasTransactions())
{
NetWalletTransaction transaction = wallet.DequeueAndMergeTransactions(id);
if (transaction.ChangedData.BalanceChanged.IsNone() && transaction.ChangedData.RewardDistributionChanged.IsNone()) { continue; }
if (!wallet.ShouldForceUpdate && transaction.ChangedData.BalanceChanged.IsNone() && transaction.ChangedData.RewardDistributionChanged.IsNone()) { continue; }
transactions.Add(transaction);
wallet.ShouldForceUpdate = false;
}
}
@@ -567,57 +575,57 @@ namespace Barotrauma
NetFlags requiredFlags = lastUpdateID.Keys.Where(k => IsFlagRequired(c, k)).Aggregate((NetFlags)0, (f1, f2) => f1 | f2);
msg.Write((UInt16)requiredFlags);
msg.WriteUInt16((UInt16)requiredFlags);
msg.Write(IsFirstRound);
msg.Write(CampaignID);
msg.Write(lastSaveID);
msg.Write(map.Seed);
msg.WriteBoolean(IsFirstRound);
msg.WriteByte(CampaignID);
msg.WriteUInt16(lastSaveID);
msg.WriteString(map.Seed);
if (requiredFlags.HasFlag(NetFlags.Misc))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.Misc));
msg.Write(PurchasedHullRepairs);
msg.Write(PurchasedItemRepairs);
msg.Write(PurchasedLostShuttles);
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.Misc));
msg.WriteBoolean(PurchasedHullRepairs);
msg.WriteBoolean(PurchasedItemRepairs);
msg.WriteBoolean(PurchasedLostShuttles);
}
if (requiredFlags.HasFlag(NetFlags.MapAndMissions))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.MapAndMissions));
msg.Write(ForceMapUI);
msg.Write(map.AllowDebugTeleport);
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.MapAndMissions));
msg.WriteBoolean(ForceMapUI);
msg.WriteBoolean(map.AllowDebugTeleport);
msg.WriteUInt16(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.WriteUInt16(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
if (map.CurrentLocation != null)
{
msg.Write((byte)map.CurrentLocation.AvailableMissions.Count());
msg.WriteByte((byte)map.CurrentLocation.AvailableMissions.Count());
foreach (Mission mission in map.CurrentLocation.AvailableMissions)
{
msg.Write(mission.Prefab.Identifier);
msg.WriteIdentifier(mission.Prefab.Identifier);
if (mission.Locations[0] == mission.Locations[1])
{
msg.Write((byte)255);
msg.WriteByte((byte)255);
}
else
{
Location missionDestination = mission.Locations[0] == map.CurrentLocation ? mission.Locations[1] : mission.Locations[0];
LocationConnection connection = map.CurrentLocation.Connections.Find(c => c.OtherLocation(map.CurrentLocation) == missionDestination);
msg.Write((byte)map.CurrentLocation.Connections.IndexOf(connection));
msg.WriteByte((byte)map.CurrentLocation.Connections.IndexOf(connection));
}
}
}
else
{
msg.Write((byte)0);
msg.WriteByte((byte)0);
}
var selectedMissionIndices = map.GetSelectedMissionIndices();
msg.Write((byte)selectedMissionIndices.Count());
msg.WriteByte((byte)selectedMissionIndices.Count());
foreach (int selectedMissionIndex in selectedMissionIndices)
{
msg.Write((byte)selectedMissionIndex);
msg.WriteByte((byte)selectedMissionIndex);
}
WriteStores(msg);
@@ -625,7 +633,7 @@ namespace Barotrauma
if (requiredFlags.HasFlag(NetFlags.SubList))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.SubList));
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.SubList));
var subList = GameMain.NetLobbyScreen.GetSubList();
List<int> ownedSubmarineIndices = new List<int>();
for (int i = 0; i < subList.Count; i++)
@@ -635,83 +643,83 @@ namespace Barotrauma
ownedSubmarineIndices.Add(i);
}
}
msg.Write((ushort)ownedSubmarineIndices.Count);
msg.WriteUInt16((ushort)ownedSubmarineIndices.Count);
foreach (int index in ownedSubmarineIndices)
{
msg.Write((ushort)index);
msg.WriteUInt16((ushort)index);
}
}
if (requiredFlags.HasFlag(NetFlags.UpgradeManager))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.UpgradeManager));
msg.Write((ushort)UpgradeManager.PendingUpgrades.Count);
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.UpgradeManager));
msg.WriteUInt16((ushort)UpgradeManager.PendingUpgrades.Count);
foreach (var (prefab, category, level) in UpgradeManager.PendingUpgrades)
{
msg.Write(prefab.Identifier);
msg.Write(category.Identifier);
msg.Write((byte)level);
msg.WriteIdentifier(prefab.Identifier);
msg.WriteIdentifier(category.Identifier);
msg.WriteByte((byte)level);
}
msg.Write((ushort)UpgradeManager.PurchasedItemSwaps.Count);
msg.WriteUInt16((ushort)UpgradeManager.PurchasedItemSwaps.Count);
foreach (var itemSwap in UpgradeManager.PurchasedItemSwaps)
{
msg.Write(itemSwap.ItemToRemove.ID);
msg.Write(itemSwap.ItemToInstall?.Identifier ?? Identifier.Empty);
msg.WriteUInt16(itemSwap.ItemToRemove.ID);
msg.WriteIdentifier(itemSwap.ItemToInstall?.Identifier ?? Identifier.Empty);
}
}
if (requiredFlags.HasFlag(NetFlags.ItemsInBuyCrate))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.ItemsInBuyCrate));
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.ItemsInBuyCrate));
WriteItems(msg, CargoManager.ItemsInBuyCrate);
WriteStores(msg);
}
if (requiredFlags.HasFlag(NetFlags.ItemsInSellFromSubCrate))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.ItemsInSellFromSubCrate));
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.ItemsInSellFromSubCrate));
WriteItems(msg, CargoManager.ItemsInSellFromSubCrate);
WriteStores(msg);
}
if (requiredFlags.HasFlag(NetFlags.PurchasedItems))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.PurchasedItems));
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.PurchasedItems));
WriteItems(msg, CargoManager.PurchasedItems);
WriteStores(msg);
}
if (requiredFlags.HasFlag(NetFlags.SoldItems))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.SoldItems));
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.SoldItems));
WriteItems(msg, CargoManager.SoldItems);
WriteStores(msg);
}
if (requiredFlags.HasFlag(NetFlags.Reputation))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.Reputation));
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.Reputation));
Reputation reputation = Map?.CurrentLocation?.Reputation;
msg.Write(reputation != null);
if (reputation != null) { msg.Write(reputation.Value); }
msg.WriteBoolean(reputation != null);
if (reputation != null) { msg.WriteSingle(reputation.Value); }
// hopefully we'll never have more than 128 factions
msg.Write((byte)Factions.Count);
msg.WriteByte((byte)Factions.Count);
foreach (Faction faction in Factions)
{
msg.Write(faction.Prefab.Identifier);
msg.Write(faction.Reputation.Value);
msg.WriteIdentifier(faction.Prefab.Identifier);
msg.WriteSingle(faction.Reputation.Value);
}
}
if (requiredFlags.HasFlag(NetFlags.CharacterInfo))
{
msg.Write(GetLastUpdateIdForFlag(NetFlags.CharacterInfo));
msg.WriteUInt16(GetLastUpdateIdForFlag(NetFlags.CharacterInfo));
var characterData = GetClientCharacterData(c);
if (characterData?.CharacterInfo == null)
{
msg.Write(false);
msg.WriteBoolean(false);
}
else
{
msg.Write(true);
msg.WriteBoolean(true);
characterData.CharacterInfo.ServerWrite(msg);
}
}
@@ -722,22 +730,22 @@ namespace Barotrauma
{
// Store balance
bool hasStores = map.CurrentLocation.Stores != null && map.CurrentLocation.Stores.Any();
msg.Write(hasStores);
msg.WriteBoolean(hasStores);
if (hasStores)
{
msg.Write((byte)map.CurrentLocation.Stores.Count);
msg.WriteByte((byte)map.CurrentLocation.Stores.Count);
foreach (var store in map.CurrentLocation.Stores.Values)
{
msg.Write(store.Identifier);
msg.Write((UInt16)store.Balance);
msg.WriteIdentifier(store.Identifier);
msg.WriteUInt16((UInt16)store.Balance);
}
}
}
else
{
msg.Write((byte)0);
msg.WriteByte((byte)0);
// Store balance
msg.Write(false);
msg.WriteBoolean(false);
}
}
}
@@ -790,11 +798,23 @@ namespace Barotrauma
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
int hullRepairCost = GetHullRepairCost();
int itemRepairCost = GetItemRepairCost();
int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost;
Location location = Map.CurrentLocation;
int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost;
int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost;
int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost;
if (location != null)
{
hullRepairCost = location.GetAdjustedMechanicalCost(hullRepairCost);
itemRepairCost = location.GetAdjustedMechanicalCost(itemRepairCost);
shuttleRetrieveCost = location.GetAdjustedMechanicalCost(shuttleRetrieveCost);
}
Wallet personalWallet = GetWallet(sender);
personalWallet?.ForceUpdate();
if (AllowedToManageWallets(sender))
{
Bank.ForceUpdate();
}
if (purchasedHullRepairs != PurchasedHullRepairs)
{
@@ -875,6 +895,12 @@ namespace Barotrauma
{
foreach (var item in store.Value.ToList())
{
if (map?.CurrentLocation?.Stores == null || !map.CurrentLocation.Stores.ContainsKey(store.Key)) { continue; }
int availableQuantity = map.CurrentLocation.Stores[store.Key].Stock.Find(s => s.ItemPrefab == item.ItemPrefab)?.Quantity ?? 0;
int alreadyPurchasedQuantity =
CargoManager.GetBuyCrateItem(store.Key, item.ItemPrefab)?.Quantity ?? 0 +
CargoManager.GetPurchasedItem(store.Key, item.ItemPrefab)?.Quantity ?? 0;
item.Quantity = MathHelper.Clamp(item.Quantity, 0, availableQuantity - alreadyPurchasedQuantity);
CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, item.Quantity, sender);
}
}
@@ -884,13 +910,21 @@ namespace Barotrauma
{
prevPurchasedItems.Add(kvp.Key, new List<PurchasedItem>(kvp.Value));
}
foreach (var store in prevPurchasedItems)
foreach (var kvp in prevPurchasedItems)
{
CargoManager.SellBackPurchasedItems(store.Key, store.Value, sender);
CargoManager.SellBackPurchasedItems(kvp.Key, kvp.Value, sender);
}
foreach (var store in purchasedItems)
foreach (var kvp in purchasedItems)
{
CargoManager.PurchaseItems(store.Key, store.Value, false, sender);
var storeId = kvp.Key;
var purchasedItemList = kvp.Value;
foreach (var purchasedItem in purchasedItemList)
{
int availableQuantity = map.CurrentLocation.Stores[storeId].Stock.Find(s => s.ItemPrefab == purchasedItem.ItemPrefab)?.Quantity ?? 0;
purchasedItem.Quantity = Math.Min(purchasedItem.Quantity, availableQuantity);
}
CargoManager.PurchaseItems(storeId, purchasedItemList, false, sender);
}
foreach (var (storeIdentifier, items) in CargoManager.PurchasedItems)
@@ -1239,41 +1273,41 @@ namespace Barotrauma
foreach (Client client in GameMain.Server.ConnectedClients)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.CREW);
msg.WriteByte((byte)ServerPacketHeader.CREW);
msg.Write((ushort)availableHires.Count);
msg.WriteUInt16((ushort)availableHires.Count);
foreach (CharacterInfo hire in availableHires)
{
hire.ServerWrite(msg);
msg.Write(hire.Salary);
msg.WriteInt32(hire.Salary);
}
msg.Write((ushort)pendingHires.Count);
msg.WriteUInt16((ushort)pendingHires.Count);
foreach (CharacterInfo pendingHire in pendingHires)
{
msg.Write(pendingHire.GetIdentifierUsingOriginalName());
msg.WriteInt32(pendingHire.GetIdentifierUsingOriginalName());
}
msg.Write((ushort)(hiredCharacters?.Count ?? 0));
msg.WriteUInt16((ushort)(hiredCharacters?.Count ?? 0));
if(hiredCharacters != null)
{
foreach (CharacterInfo info in hiredCharacters)
{
info.ServerWrite(msg);
msg.Write(info.Salary);
msg.WriteInt32(info.Salary);
}
}
bool validRenaming = renamedCrewMember.id > -1 && !string.IsNullOrEmpty(renamedCrewMember.newName);
msg.Write(validRenaming);
msg.WriteBoolean(validRenaming);
if (validRenaming)
{
msg.Write(renamedCrewMember.id);
msg.Write(renamedCrewMember.newName);
msg.WriteInt32(renamedCrewMember.id);
msg.WriteString(renamedCrewMember.newName);
}
msg.Write(firedCharacter != null);
if (firedCharacter != null) { msg.Write(firedCharacter.GetIdentifier()); }
msg.WriteBoolean(firedCharacter != null);
if (firedCharacter != null) { msg.WriteInt32(firedCharacter.GetIdentifier()); }
GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
@@ -1351,8 +1385,8 @@ namespace Barotrauma
foreach (var savedExperiencePoint in savedExperiencePoints)
{
savedExperiencePointsElement.Add(new XElement("Point",
new XAttribute("steamid", savedExperiencePoint.SteamID),
new XAttribute("endpoint", savedExperiencePoint?.EndPoint ?? string.Empty),
new XAttribute("accountid", savedExperiencePoint.AccountId.TryUnwrap(out var accountId) ? accountId.StringRepresentation : ""),
new XAttribute("address", savedExperiencePoint.Address.StringRepresentation),
new XAttribute("points", savedExperiencePoint.ExperiencePoints)));
}
@@ -154,7 +154,7 @@ namespace Barotrauma
private IWriteMessage StartSending()
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.MEDICAL);
msg.WriteByte((byte)ServerPacketHeader.MEDICAL);
return msg;
}
@@ -181,8 +181,8 @@ namespace Barotrauma
}
IWriteMessage msg = StartSending();
msg.Write((byte)header);
msg.Write((byte)flag);
msg.WriteByte((byte)header);
msg.WriteByte((byte)flag);
netStruct?.Write(msg);
GameMain.Server.ServerPeer.Send(msg, c.Connection, deliveryMethod);
}
@@ -17,26 +17,26 @@ namespace Barotrauma
if (client != null && !client.Spectating)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ServerPacketHeader.READY_CHECK);
msg.Write((byte) ReadyCheckState.Start);
msg.Write(new DateTimeOffset(startTime).ToUnixTimeSeconds());
msg.Write(new DateTimeOffset(endTime).ToUnixTimeSeconds());
msg.Write(author);
msg.WriteByte((byte)ServerPacketHeader.READY_CHECK);
msg.WriteByte((byte)ReadyCheckState.Start);
msg.WriteInt64(new DateTimeOffset(startTime).ToUnixTimeSeconds());
msg.WriteInt64(new DateTimeOffset(endTime).ToUnixTimeSeconds());
msg.WriteString(author);
if (sender != null)
{
msg.Write(true);
msg.Write(sender.ID);
msg.WriteBoolean(true);
msg.WriteByte(sender.SessionId);
}
else
{
msg.Write(false);
msg.WriteBoolean(false);
}
msg.Write((ushort) ActivePlayers.Count);
msg.WriteUInt16((ushort)ActivePlayers.Count);
foreach (byte clientId in Clients.Keys)
{
msg.Write(clientId);
msg.WriteByte(clientId);
}
GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
@@ -55,10 +55,10 @@ namespace Barotrauma
foreach (Client client in ActivePlayers)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.READY_CHECK);
msg.Write((byte)ReadyCheckState.Update);
msg.Write((byte)state);
msg.Write(otherClient);
msg.WriteByte((byte)ServerPacketHeader.READY_CHECK);
msg.WriteByte((byte)ReadyCheckState.Update);
msg.WriteByte((byte)state);
msg.WriteByte(otherClient);
GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
}
@@ -72,13 +72,13 @@ namespace Barotrauma
if (client != null && !client.Spectating)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ServerPacketHeader.READY_CHECK);
msg.Write((byte) ReadyCheckState.End);
msg.Write((ushort) Clients.Count);
msg.WriteByte((byte)ServerPacketHeader.READY_CHECK);
msg.WriteByte((byte)ReadyCheckState.End);
msg.WriteUInt16((ushort)Clients.Count);
foreach (var (id, state) in Clients)
{
msg.Write(id);
msg.Write((byte) state);
msg.WriteByte(id);
msg.WriteByte((byte)state);
}
GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
@@ -88,7 +88,7 @@ namespace Barotrauma
public static void ServerRead(IReadMessage inc, Client client)
{
ReadyCheckState state = (ReadyCheckState) inc.ReadByte();
ReadyCheckState state = (ReadyCheckState)inc.ReadByte();
ReadyCheck? readyCheck = GameMain.GameSession?.CrewManager?.ActiveReadyCheck;
switch (state)
@@ -98,11 +98,11 @@ namespace Barotrauma
break;
case ReadyCheckState.Update when readyCheck != null:
ReadyStatus status = (ReadyStatus) inc.ReadByte();
if (!readyCheck.Clients.ContainsKey(client.ID)) { return; }
ReadyStatus status = (ReadyStatus)inc.ReadByte();
if (!readyCheck.Clients.ContainsKey(client.SessionId)) { return; }
readyCheck.Clients[client.ID] = status;
readyCheck.UpdateReadyCheck(client.ID, status);
readyCheck.Clients[client.SessionId] = status;
readyCheck.UpdateReadyCheck(client.SessionId, status);
break;
}
}
@@ -111,8 +111,8 @@ namespace Barotrauma
{
if (GameMain.GameSession?.CrewManager == null || GameMain.GameSession.CrewManager.ActiveReadyCheck != null) { return; }
List<Client> connectedClients = GameMain.Server.ConnectedClients;
ReadyCheck newReadyCheck = new ReadyCheck(connectedClients.Where(c => !c.Spectating).Select(c => c.ID).ToList(), 30);
var connectedClients = GameMain.Server.ConnectedClients;
ReadyCheck newReadyCheck = new ReadyCheck(connectedClients.Where(c => !c.Spectating).Select(c => c.SessionId).ToList(), 30);
GameMain.GameSession.CrewManager.ActiveReadyCheck = newReadyCheck;
newReadyCheck.InitializeReadyCheck(author, sender);
}
@@ -6,11 +6,11 @@ namespace Barotrauma.Items.Components
{
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(docked);
msg.WriteBoolean(docked);
if (docked)
{
msg.Write(DockingTarget.item.ID);
msg.Write(IsLocked);
msg.WriteUInt16(DockingTarget.item.ID);
msg.WriteBoolean(IsLocked);
}
}
public void ServerEventRead(IReadMessage msg, Client c)
@@ -38,13 +38,13 @@ namespace Barotrauma.Items.Components
bool forcedOpen = TryExtractEventData<EventData>(extraData, out var eventData) && eventData.ForcedOpen;
base.ServerEventWrite(msg, c, extraData);
msg.Write(isOpen);
msg.Write(isBroken);
msg.Write(forcedOpen); //forced open
msg.Write(isStuck);
msg.Write(isJammed);
msg.WriteBoolean(isOpen);
msg.WriteBoolean(isBroken);
msg.WriteBoolean(forcedOpen); //forced open
msg.WriteBoolean(isStuck);
msg.WriteBoolean(isJammed);
msg.WriteRangedSingle(stuck, 0.0f, 100.0f, 8);
msg.Write(lastUser == null ? (UInt16)0 : lastUser.ID);
msg.WriteUInt16(lastUser == null ? (UInt16)0 : lastUser.ID);
}
}
}
@@ -6,14 +6,14 @@ namespace Barotrauma.Items.Components
{
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(tainted);
msg.WriteBoolean(tainted);
if (tainted)
{
msg.Write(selectedTaintedEffect?.UintIdentifier ?? 0);
msg.WriteUInt32(selectedTaintedEffect?.UintIdentifier ?? 0);
}
else
{
msg.Write(selectedEffect?.UintIdentifier ?? 0);
msg.WriteUInt32(selectedEffect?.UintIdentifier ?? 0);
}
}
}
@@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.WriteRangedSingle(Health, 0f, (float) MaxHealth, 8);
msg.WriteRangedSingle(Health, 0f, (float)MaxHealth, 8);
if (TryExtractEventData(extraData, out EventData eventData))
{
int offset = eventData.Offset;
@@ -48,11 +48,11 @@ namespace Barotrauma.Items.Components
{
VineTile vine = Vines[i];
var (x, y) = vine.Position;
msg.WriteRangedInteger((byte) vine.Type, 0b0000, 0b1111);
msg.WriteRangedInteger((byte)vine.Type, 0b0000, 0b1111);
msg.WriteRangedInteger(vine.FlowerConfig.Serialize(), 0, 0xFFF);
msg.WriteRangedInteger(vine.LeafConfig.Serialize(), 0, 0xFFF);
msg.Write((byte) (x / VineTile.Size));
msg.Write((byte) (y / VineTile.Size));
msg.WriteByte((byte)(x / VineTile.Size));
msg.WriteByte((byte)(y / VineTile.Size));
}
}
else
@@ -10,13 +10,13 @@ namespace Barotrauma.Items.Components
base.ServerEventWrite(msg, c, extraData);
bool writeAttachData = attachable && body != null;
msg.Write(writeAttachData);
msg.WriteBoolean(writeAttachData);
if (!writeAttachData) { return; }
msg.Write(Attached);
msg.Write(body.SimPosition.X);
msg.Write(body.SimPosition.Y);
msg.Write(item.Submarine?.ID ?? Entity.NullEntityID);
msg.WriteBoolean(Attached);
msg.WriteSingle(body.SimPosition.X);
msg.WriteSingle(body.SimPosition.Y);
msg.WriteUInt16(item.Submarine?.ID ?? Entity.NullEntityID);
}
public void ServerEventRead(IReadMessage msg, Client c)
@@ -8,7 +8,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(deattachTimer);
msg.WriteSingle(deattachTimer);
}
}
}
@@ -78,7 +78,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(Text);
msg.WriteString(Text);
lastSentText = Text;
}
}
@@ -38,7 +38,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(IsActive);
msg.WriteBoolean(IsActive);
lastSentState = IsActive;
}
}
@@ -6,8 +6,8 @@ namespace Barotrauma.Items.Components
{
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(State);
msg.Write(user == null ? (ushort)0 : user.ID);
msg.WriteBoolean(State);
msg.WriteUInt16(user == null ? (ushort)0 : user.ID);
}
}
}
@@ -18,9 +18,9 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(user?.ID ?? 0);
msg.Write(IsActive);
msg.Write(progressTimer);
msg.WriteUInt16(user?.ID ?? 0);
msg.WriteBoolean(IsActive);
msg.WriteSingle(progressTimer);
}
}
}
@@ -9,7 +9,7 @@ namespace Barotrauma.Items.Components
{
//force can only be adjusted at 10% intervals -> no need for more accuracy than this
msg.WriteRangedInteger((int)(targetForce / 10.0f), -10, 10);
msg.Write(User == null ? Entity.NullEntityID : User.ID);
msg.WriteUInt16(User == null ? Entity.NullEntityID : User.ID);
}
public void ServerEventRead(IReadMessage msg, Client c)
@@ -55,18 +55,18 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
var componentData = ExtractEventData<EventData>(extraData);
msg.Write((byte)componentData.State);
msg.Write(timeUntilReady);
msg.WriteByte((byte)componentData.State);
msg.WriteSingle(timeUntilReady);
uint recipeHash = fabricatedItem?.RecipeHash ?? 0;
msg.Write(recipeHash);
UInt16 userID = fabricatedItem is null || user is null ? (UInt16)0 : user.ID;
msg.Write(userID);
msg.WriteUInt32(recipeHash);
UInt16 userId = fabricatedItem is null || user is null ? (UInt16)0 : user.ID;
msg.WriteUInt16(userId);
var reachedLimits = fabricationLimits.Where(kvp => kvp.Value <= 0);
msg.Write((ushort)reachedLimits.Count());
msg.WriteUInt16((ushort)reachedLimits.Count());
foreach (var kvp in reachedLimits)
{
msg.Write(kvp.Key);
msg.WriteUInt32(kvp.Key);
}
}
}
@@ -49,16 +49,16 @@ namespace Barotrauma.Items.Components
{
//flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this
msg.WriteRangedInteger((int)(flowPercentage / 10.0f), -10, 10);
msg.Write(IsActive);
msg.Write(Hijacked);
msg.WriteBoolean(IsActive);
msg.WriteBoolean(Hijacked);
if (TargetLevel != null)
{
msg.Write(true);
msg.Write(TargetLevel.Value);
msg.WriteBoolean(true);
msg.WriteSingle(TargetLevel.Value);
}
else
{
msg.Write(false);
msg.WriteBoolean(false);
}
}
}
@@ -45,8 +45,8 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(autoTemp);
msg.Write(_powerOn);
msg.WriteBoolean(autoTemp);
msg.WriteBoolean(_powerOn);
msg.WriteRangedSingle(temperature, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);
@@ -100,30 +100,30 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Barotrauma.Networking.Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(autoPilot);
msg.Write(TryExtractEventData<EventData>(extraData, out var eventData) && eventData.DockingButtonClicked);
msg.Write(user?.ID ?? Entity.NullEntityID);
msg.WriteBoolean(autoPilot);
msg.WriteBoolean(TryExtractEventData<EventData>(extraData, out var eventData) && eventData.DockingButtonClicked);
msg.WriteUInt16(user?.ID ?? Entity.NullEntityID);
if (!autoPilot)
{
//no need to write steering info if autopilot is controlling
msg.Write(steeringInput.X);
msg.Write(steeringInput.Y);
msg.Write(targetVelocity.X);
msg.Write(targetVelocity.Y);
msg.Write(steeringAdjustSpeed);
msg.WriteSingle(steeringInput.X);
msg.WriteSingle(steeringInput.Y);
msg.WriteSingle(targetVelocity.X);
msg.WriteSingle(targetVelocity.Y);
msg.WriteSingle(steeringAdjustSpeed);
}
else
{
msg.Write(posToMaintain != null);
msg.WriteBoolean(posToMaintain != null);
if (posToMaintain != null)
{
msg.Write(((Vector2)posToMaintain).X);
msg.Write(((Vector2)posToMaintain).Y);
msg.WriteSingle(((Vector2)posToMaintain).X);
msg.WriteSingle(((Vector2)posToMaintain).Y);
}
else
{
msg.Write(LevelStartSelected);
msg.WriteBoolean(LevelStartSelected);
}
}
}
@@ -25,39 +25,39 @@ namespace Barotrauma.Items.Components
var eventData = ExtractEventData<EventData>(extraData);
bool launch = eventData.Launch;
msg.Write(launch);
msg.WriteBoolean(launch);
if (launch)
{
msg.Write(User.ID);
msg.Write(launchPos.X);
msg.Write(launchPos.Y);
msg.Write(launchRot);
msg.WriteUInt16(User.ID);
msg.WriteSingle(launchPos.X);
msg.WriteSingle(launchPos.Y);
msg.WriteSingle(launchRot);
}
bool stuck = StickTarget != null && !item.Removed && !StickTargetRemoved();
msg.Write(stuck);
msg.WriteBoolean(stuck);
if (stuck)
{
msg.Write(item.Submarine?.ID ?? Entity.NullEntityID);
msg.Write(item.CurrentHull?.ID ?? Entity.NullEntityID);
msg.Write(item.SimPosition.X);
msg.Write(item.SimPosition.Y);
msg.Write(jointAxis.X);
msg.Write(jointAxis.Y);
msg.WriteUInt16(item.Submarine?.ID ?? Entity.NullEntityID);
msg.WriteUInt16(item.CurrentHull?.ID ?? Entity.NullEntityID);
msg.WriteSingle(item.SimPosition.X);
msg.WriteSingle(item.SimPosition.Y);
msg.WriteSingle(jointAxis.X);
msg.WriteSingle(jointAxis.Y);
if (StickTarget.UserData is Structure structure)
{
msg.Write(structure.ID);
msg.WriteUInt16(structure.ID);
int bodyIndex = structure.Bodies.IndexOf(StickTarget);
msg.Write((byte)(bodyIndex == -1 ? 0 : bodyIndex));
msg.WriteByte((byte)(bodyIndex == -1 ? 0 : bodyIndex));
}
else if (StickTarget.UserData is Entity entity)
{
msg.Write(entity.ID);
msg.WriteUInt16(entity.ID);
}
else if (StickTarget.UserData is Limb limb)
{
msg.Write(limb.character.ID);
msg.Write((byte)Array.IndexOf(limb.character.AnimController.Limbs, limb));
msg.WriteUInt16(limb.character.ID);
msg.WriteByte((byte)Array.IndexOf(limb.character.AnimController.Limbs, limb));
}
else
{
@@ -44,13 +44,13 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(deteriorationTimer);
msg.Write(deteriorateAlwaysResetTimer);
msg.Write(DeteriorateAlways);
msg.Write(tinkeringDuration);
msg.Write(tinkeringStrength);
msg.Write(tinkeringPowersDevices);
msg.Write(CurrentFixer == null ? (ushort)0 : CurrentFixer.ID);
msg.WriteSingle(deteriorationTimer);
msg.WriteSingle(deteriorateAlwaysResetTimer);
msg.WriteBoolean(DeteriorateAlways);
msg.WriteSingle(tinkeringDuration);
msg.WriteSingle(tinkeringStrength);
msg.WriteBoolean(tinkeringPowersDevices);
msg.WriteUInt16(CurrentFixer == null ? (ushort)0 : CurrentFixer.ID);
msg.WriteRangedInteger((int)currentFixerAction, 0, 2);
}
}
@@ -6,25 +6,25 @@ namespace Barotrauma.Items.Components
{
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(Snapped);
msg.WriteBoolean(Snapped);
if (!Snapped)
{
msg.Write(target?.ID ?? Entity.NullEntityID);
msg.WriteUInt16(target?.ID ?? Entity.NullEntityID);
if (source is Entity entity && !entity.Removed)
{
msg.Write(entity?.ID ?? Entity.NullEntityID);
msg.Write((byte)0);
msg.WriteUInt16(entity?.ID ?? Entity.NullEntityID);
msg.WriteByte((byte)0);
}
else if (source is Limb limb && limb.character != null && !limb.character.Removed)
{
msg.Write(limb.character?.ID ?? Entity.NullEntityID);
msg.Write((byte)limb.character.AnimController.Limbs.IndexOf(limb));
msg.WriteUInt16(limb.character?.ID ?? Entity.NullEntityID);
msg.WriteByte((byte)limb.character.AnimController.Limbs.IndexOf(limb));
}
else
{
msg.Write(Entity.NullEntityID);
msg.Write((byte)0);
msg.WriteUInt16(Entity.NullEntityID);
msg.WriteByte((byte)0);
}
}
}
@@ -8,7 +8,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(scanTimer);
msg.WriteSingle(scanTimer);
}
}
}
@@ -208,7 +208,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(user == null ? (ushort)0 : user.ID);
msg.WriteUInt16(user == null ? (ushort)0 : user.ID);
ClientEventWrite(msg, extraData);
}
}
@@ -72,15 +72,15 @@ namespace Barotrauma.Items.Components
var element = customInterfaceElementList[i];
if (element.HasPropertyName)
{
msg.Write(element.Signal);
msg.WriteString(element.Signal);
}
else if(element.ContinuousSignal)
{
msg.Write(element.State);
msg.WriteBoolean(element.State);
}
else
{
msg.Write(extraData is Item.ComponentStateEventData { ComponentData: EventData eventData } && eventData.BtnElement == customInterfaceElementList[i]);
msg.WriteBoolean(extraData is Item.ComponentStateEventData { ComponentData: EventData eventData } && eventData.BtnElement == customInterfaceElementList[i]);
}
}
}
@@ -38,7 +38,7 @@ namespace Barotrauma.Items.Components
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(Value);
msg.WriteString(Value);
lastSentValue = Value;
}
}
@@ -101,11 +101,11 @@ namespace Barotrauma.Items.Components
{
if (TryExtractEventData(extraData, out ServerEventData eventData))
{
msg.Write(eventData.MsgToSend);
msg.WriteString(eventData.MsgToSend);
}
else
{
msg.Write(OutputValue);
msg.WriteString(OutputValue);
}
}
}
@@ -41,8 +41,8 @@ namespace Barotrauma.Items.Components
msg.WriteRangedInteger(nodeCount, 0, MaxNodesPerNetworkEvent);
for (int i = nodeStartIndex; i < nodeStartIndex + nodeCount; i++)
{
msg.Write(nodes[i].X);
msg.Write(nodes[i].Y);
msg.WriteSingle(nodes[i].X);
msg.WriteSingle(nodes[i].Y);
}
}
@@ -62,14 +62,14 @@ namespace Barotrauma
throw error("component \"" + components[containerIndex] + "\" is not server serializable");
}
msg.WriteRangedInteger(containerIndex, 0, components.Count - 1);
msg.Write(GameMain.Server.EntityEventManager.Events.Last()?.ID ?? (ushort)0);
msg.WriteUInt16(GameMain.Server.EntityEventManager.Events.Last()?.ID ?? (ushort)0);
itemContainer.Inventory.ServerEventWrite(msg, c);
break;
case ItemStatusEventData _:
msg.Write(condition);
msg.WriteSingle(condition);
break;
case AssignCampaignInteractionEventData _:
msg.Write((byte)CampaignInteractionType);
msg.WriteByte((byte)CampaignInteractionType);
break;
case ApplyStatusEffectEventData applyStatusEffectEventData:
{
@@ -83,15 +83,15 @@ namespace Barotrauma
byte targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255;
msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1);
msg.Write((byte)(targetComponent == null ? 255 : components.IndexOf(targetComponent)));
msg.Write(applyStatusEffectEventData.TargetCharacter?.ID ?? (ushort)0);
msg.Write(targetLimbIndex);
msg.Write(applyStatusEffectEventData.UseTarget?.ID ?? (ushort)0);
msg.Write(worldPosition.HasValue);
msg.WriteByte((byte)(targetComponent == null ? 255 : components.IndexOf(targetComponent)));
msg.WriteUInt16(applyStatusEffectEventData.TargetCharacter?.ID ?? (ushort)0);
msg.WriteByte(targetLimbIndex);
msg.WriteUInt16(applyStatusEffectEventData.UseTarget?.ID ?? (ushort)0);
msg.WriteBoolean(worldPosition.HasValue);
if (worldPosition.HasValue)
{
msg.Write(worldPosition.Value.X);
msg.Write(worldPosition.Value.Y);
msg.WriteSingle(worldPosition.Value.X);
msg.WriteSingle(worldPosition.Value.Y);
}
}
break;
@@ -109,16 +109,16 @@ namespace Barotrauma
case UpgradeEventData upgradeEventData:
var upgrade = upgradeEventData.Upgrade;
var upgradeTargets = upgrade.TargetComponents;
msg.Write(upgrade.Identifier);
msg.Write((byte)upgrade.Level);
msg.Write((byte)upgradeTargets.Count);
msg.WriteIdentifier(upgrade.Identifier);
msg.WriteByte((byte)upgrade.Level);
msg.WriteByte((byte)upgradeTargets.Count);
foreach (var (_, value) in upgrade.TargetComponents)
{
msg.Write((byte)value.Length);
msg.WriteByte((byte)value.Length);
foreach (var propertyReference in value)
{
object originalValue = propertyReference.OriginalValue;
msg.Write((float)(originalValue ?? -1));
msg.WriteSingle((float)(originalValue ?? -1));
}
}
break;
@@ -189,35 +189,35 @@ namespace Barotrauma
{
if (GameMain.Server == null) { return; }
msg.Write(Prefab.OriginalName);
msg.Write(Prefab.Identifier);
msg.Write(Description != base.Prefab.Description);
msg.WriteString(Prefab.OriginalName);
msg.WriteIdentifier(Prefab.Identifier);
msg.WriteBoolean(Description != base.Prefab.Description);
if (Description != base.Prefab.Description)
{
msg.Write(Description);
msg.WriteString(Description);
}
msg.Write(entityID);
msg.WriteUInt16(entityID);
if (ParentInventory == null || ParentInventory.Owner == null || originalInventoryID == 0)
{
msg.Write((ushort)0);
msg.WriteUInt16((ushort)0);
msg.Write(Position.X);
msg.Write(Position.Y);
msg.WriteSingle(Position.X);
msg.WriteSingle(Position.Y);
msg.WriteRangedSingle(body == null ? 0.0f : MathUtils.WrapAngleTwoPi(body.Rotation), 0.0f, MathHelper.TwoPi, 8);
msg.Write(Submarine != null ? Submarine.ID : (ushort)0);
msg.WriteUInt16(Submarine != null ? Submarine.ID : (ushort)0);
}
else
{
msg.Write(originalInventoryID);
msg.Write(originalItemContainerIndex);
msg.Write(originalSlotIndex < 0 ? (byte)255 : (byte)originalSlotIndex);
msg.WriteUInt16(originalInventoryID);
msg.WriteByte(originalItemContainerIndex);
msg.WriteByte(originalSlotIndex < 0 ? (byte)255 : (byte)originalSlotIndex);
}
msg.Write(body == null ? (byte)0 : (byte)body.BodyType);
msg.Write(SpawnedInCurrentOutpost);
msg.Write(AllowStealing);
msg.WriteByte(body == null ? (byte)0 : (byte)body.BodyType);
msg.WriteBoolean(SpawnedInCurrentOutpost);
msg.WriteBoolean(AllowStealing);
msg.WriteRangedInteger(Quality, 0, Items.Components.Quality.MaxQuality);
byte teamID = 0;
@@ -237,39 +237,39 @@ namespace Barotrauma
}
}
msg.Write(teamID);
msg.WriteByte(teamID);
bool hasIdCard = idCardComponent != null;
msg.Write(hasIdCard);
msg.WriteBoolean(hasIdCard);
if (hasIdCard)
{
msg.Write(idCardComponent.OwnerName);
msg.Write(idCardComponent.OwnerTags);
msg.Write((byte)Math.Max(0, idCardComponent.OwnerBeardIndex+1));
msg.Write((byte)Math.Max(0, idCardComponent.OwnerHairIndex+1));
msg.Write((byte)Math.Max(0, idCardComponent.OwnerMoustacheIndex+1));
msg.Write((byte)Math.Max(0, idCardComponent.OwnerFaceAttachmentIndex+1));
msg.WriteString(idCardComponent.OwnerName);
msg.WriteString(idCardComponent.OwnerTags);
msg.WriteByte((byte)Math.Max(0, idCardComponent.OwnerBeardIndex+1));
msg.WriteByte((byte)Math.Max(0, idCardComponent.OwnerHairIndex+1));
msg.WriteByte((byte)Math.Max(0, idCardComponent.OwnerMoustacheIndex+1));
msg.WriteByte((byte)Math.Max(0, idCardComponent.OwnerFaceAttachmentIndex+1));
msg.WriteColorR8G8B8(idCardComponent.OwnerHairColor);
msg.WriteColorR8G8B8(idCardComponent.OwnerFacialHairColor);
msg.WriteColorR8G8B8(idCardComponent.OwnerSkinColor);
msg.Write(idCardComponent.OwnerJobId);
msg.Write((byte)idCardComponent.OwnerSheetIndex.X);
msg.Write((byte)idCardComponent.OwnerSheetIndex.Y);
msg.WriteIdentifier(idCardComponent.OwnerJobId);
msg.WriteByte((byte)idCardComponent.OwnerSheetIndex.X);
msg.WriteByte((byte)idCardComponent.OwnerSheetIndex.Y);
}
bool tagsChanged = tags.Count != base.Prefab.Tags.Count || !tags.All(t => base.Prefab.Tags.Contains(t));
msg.Write(tagsChanged);
msg.WriteBoolean(tagsChanged);
if (tagsChanged)
{
IEnumerable<Identifier> splitTags = Tags.Split(',').ToIdentifiers();
msg.Write(string.Join(',', splitTags.Where(t => !base.Prefab.Tags.Contains(t))));
msg.Write(string.Join(',', base.Prefab.Tags.Where(t => !splitTags.Contains(t))));
msg.WriteString(string.Join(',', splitTags.Where(t => !base.Prefab.Tags.Contains(t))));
msg.WriteString(string.Join(',', base.Prefab.Tags.Where(t => !splitTags.Contains(t))));
}
var nameTag = GetComponent<NameTag>();
msg.Write(nameTag != null);
msg.WriteBoolean(nameTag != null);
if (nameTag != null)
{
msg.Write(nameTag.WrittenName ?? "");
msg.WriteString(nameTag.WrittenName ?? "");
}
}
@@ -342,12 +342,12 @@ namespace Barotrauma
public void ServerWritePosition(IWriteMessage msg, Client c)
{
msg.Write(ID);
msg.WriteUInt16(ID);
IWriteMessage tempBuffer = new WriteOnlyMessage();
body.ServerWrite(tempBuffer);
msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
msg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
msg.WritePadBits();
}
@@ -32,21 +32,21 @@ namespace Barotrauma
{
if (!(extraData is IEventData eventData)) { throw new Exception($"Malformed level event: expected {nameof(Level)}.{nameof(IEventData)}"); }
msg.Write((byte)eventData.EventType);
msg.WriteByte((byte)eventData.EventType);
switch (eventData)
{
case SingleLevelWallEventData { Wall: var destructibleWall }:
int index = ExtraWalls.IndexOf(destructibleWall);
msg.Write((ushort)(index == -1 ? ushort.MaxValue : index));
msg.WriteUInt16((ushort)(index == -1 ? ushort.MaxValue : index));
//write health using one byte
msg.Write((byte)MathHelper.Clamp((int)(MathUtils.InverseLerp(0.0f, destructibleWall.MaxHealth, destructibleWall.Damage) * 255.0f), 0, 255));
msg.WriteByte((byte)MathHelper.Clamp((int)(MathUtils.InverseLerp(0.0f, destructibleWall.MaxHealth, destructibleWall.Damage) * 255.0f), 0, 255));
break;
case GlobalLevelWallEventData _:
foreach (LevelWall levelWall in ExtraWalls)
{
if (levelWall.Body.BodyType == BodyType.Static) { continue; }
msg.Write(levelWall.Body.Position.X);
msg.Write(levelWall.Body.Position.Y);
msg.WriteSingle(levelWall.Body.Position.X);
msg.WriteSingle(levelWall.Body.Position.Y);
msg.WriteRangedSingle(levelWall.MoveState, 0.0f, MathHelper.TwoPi, 16);
}
break;
@@ -56,7 +56,7 @@ namespace Barotrauma.MapCreatures.Behavior
public void ServerWrite(IWriteMessage msg, IEventData eventData)
{
msg.Write((byte)eventData.NetworkHeader);
msg.WriteByte((byte)eventData.NetworkHeader);
switch (eventData)
{
@@ -80,51 +80,51 @@ namespace Barotrauma.MapCreatures.Behavior
break;
}
msg.Write(PowerConsumptionTimer);
msg.WriteSingle(PowerConsumptionTimer);
}
private void ServerWriteSpawn(IWriteMessage msg)
{
msg.Write(Prefab.Identifier);
msg.Write(Offset.X);
msg.Write(Offset.Y);
msg.WriteIdentifier(Prefab.Identifier);
msg.WriteSingle(Offset.X);
msg.WriteSingle(Offset.Y);
}
private void ServerWriteBranchGrowth(IWriteMessage msg, BallastFloraBranch branch, int parentId = -1)
{
var (x, y) = branch.Position;
msg.Write(parentId);
msg.Write((int)branch.ID);
msg.Write(branch.IsRootGrowth);
msg.WriteInt32(parentId);
msg.WriteInt32((int)branch.ID);
msg.WriteBoolean(branch.IsRootGrowth);
msg.WriteRangedInteger((byte)branch.Type, 0b0000, 0b1111);
msg.WriteRangedInteger((byte)branch.Sides, 0b0000, 0b1111);
msg.WriteRangedInteger(branch.FlowerConfig.Serialize(), 0, 0xFFF);
msg.WriteRangedInteger(branch.LeafConfig.Serialize(), 0, 0xFFF);
msg.Write((ushort)branch.MaxHealth);
msg.Write((int)(x / VineTile.Size));
msg.Write((int)(y / VineTile.Size));
msg.Write(branch.ParentBranch == null ? -1 : Branches.IndexOf(branch.ParentBranch));
msg.WriteUInt16((ushort)branch.MaxHealth);
msg.WriteInt32((int)(x / VineTile.Size));
msg.WriteInt32((int)(y / VineTile.Size));
msg.WriteInt32(branch.ParentBranch == null ? -1 : Branches.IndexOf(branch.ParentBranch));
}
private void ServerWriteBranchDamage(IWriteMessage msg, BallastFloraBranch branch)
{
msg.Write((int)branch.ID);
msg.Write(branch.Health);
msg.WriteInt32((int)branch.ID);
msg.WriteSingle(branch.Health);
}
private void ServerWriteInfect(IWriteMessage msg, UInt16 itemID, InfectEventData.InfectState infect, BallastFloraBranch infector = null)
{
msg.Write(itemID);
msg.Write(infect == InfectEventData.InfectState.Yes);
msg.WriteUInt16(itemID);
msg.WriteBoolean(infect == InfectEventData.InfectState.Yes);
if (infect == InfectEventData.InfectState.Yes)
{
msg.Write(infector?.ID ?? -1);
msg.WriteInt32(infector?.ID ?? -1);
}
}
private void ServerWriteBranchRemove(IWriteMessage msg, BallastFloraBranch branch)
{
msg.Write(branch.ID);
msg.WriteInt32(branch.ID);
}
public void CreateNetworkMessage(IEventData extraData)
@@ -94,8 +94,8 @@ namespace Barotrauma
msg.WriteRangedInteger(decals.Count, 0, MaxDecalsPerHull);
foreach (Decal decal in decals)
{
msg.Write(decal.Prefab.UintIdentifier);
msg.Write((byte)decal.SpriteIndex);
msg.WriteUInt32(decal.Prefab.UintIdentifier);
msg.WriteByte((byte)decal.SpriteIndex);
float normalizedXPos = MathHelper.Clamp(MathUtils.InverseLerp(0.0f, rect.Width, decal.CenterPosition.X), 0.0f, 1.0f);
float normalizedYPos = MathHelper.Clamp(MathUtils.InverseLerp(-rect.Height, 0.0f, decal.CenterPosition.Y), 0.0f, 1.0f);
msg.WriteRangedSingle(normalizedXPos, 0.0f, 1.0f, 8);
@@ -11,7 +11,7 @@ namespace Barotrauma
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write((byte)Sections.Length);
msg.WriteByte((byte)Sections.Length);
for (int i = 0; i < Sections.Length; i++)
{
msg.WriteRangedSingle(Sections[i].damage / MaxHealth, 0.0f, 1.0f, 8);
@@ -7,11 +7,11 @@ namespace Barotrauma
{
public void ServerWritePosition(IWriteMessage msg, Client c)
{
msg.Write(ID);
msg.WriteUInt16(ID);
IWriteMessage tempBuffer = new WriteOnlyMessage();
subBody.Body.ServerWrite(tempBuffer);
msg.Write((byte)tempBuffer.LengthBytes);
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
msg.WriteByte((byte)tempBuffer.LengthBytes);
msg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
msg.WritePadBits();
}
@@ -1,93 +1,68 @@
using System;
using System.Collections.Generic;
#nullable enable
using System;
using Barotrauma.IO;
using System.Linq;
using System.Net;
using Barotrauma.Steam;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma.Networking
{
partial class BannedPlayer
{
private static UInt16 LastIdentifier = 0;
private static UInt32 LastIdentifier = 0;
public BannedPlayer(string name, string endPoint, string reason, DateTime? expirationTime)
public bool Expired => ExpirationTime is { } expirationTime && DateTime.Now > expirationTime;
public BannedPlayer(
string name, Either<Address, AccountId> addressOrAccountId, string reason, DateTime? expirationTime)
{
this.Name = name;
this.EndPoint = endPoint;
ParseEndPointAsSteamId();
this.AddressOrAccountId = addressOrAccountId;
this.Reason = reason;
this.ExpirationTime = expirationTime;
this.UniqueIdentifier = LastIdentifier; LastIdentifier++;
this.IsRangeBan = EndPoint.IndexOf(".x") > -1;
}
public BannedPlayer(string name, ulong steamID, string reason, DateTime? expirationTime)
{
this.Name = name;
this.SteamID = steamID;
this.Reason = reason;
this.ExpirationTime = expirationTime;
this.UniqueIdentifier = LastIdentifier; LastIdentifier++;
this.IsRangeBan = false;
this.EndPoint = "";
}
public bool CompareTo(string endpointCompare)
{
if (string.IsNullOrEmpty(EndPoint) || string.IsNullOrEmpty(endpointCompare)) { return false; }
if (!IsRangeBan)
{
return endpointCompare == EndPoint;
}
else
{
int rangeBanIndex = EndPoint.IndexOf(".x");
if (endpointCompare.Length < rangeBanIndex) return false;
return endpointCompare.Substring(0, rangeBanIndex) == EndPoint.Substring(0, rangeBanIndex);
}
}
public bool CompareTo(IPAddress ipCompare)
{
if (string.IsNullOrEmpty(EndPoint) || ipCompare == null) { return false; }
if (ipCompare.IsIPv4MappedToIPv6 && CompareTo(ipCompare.MapToIPv4NoThrow().ToString()))
{
return true;
}
return CompareTo(ipCompare.ToString());
}
}
partial class BanList
{
const string SavePath = "Data/bannedplayers.txt";
private const string SavePath = "Data/bannedplayers.xml";
private const string LegacySavePath = "Data/bannedplayers.txt";
partial void InitProjectSpecific()
{
if (!File.Exists(SavePath)) { return; }
if (!File.Exists(SavePath))
{
LoadLegacyBanList();
}
else
{
LoadBanList();
}
}
private void LoadLegacyBanList()
{
if (!File.Exists(LegacySavePath)) { return; }
string[] lines;
try
{
lines = File.ReadAllLines(SavePath);
lines = File.ReadAllLines(LegacySavePath);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to open the list of banned players in " + SavePath, e);
DebugConsole.ThrowError($"Failed to open the list of banned players in {LegacySavePath}", e);
return;
}
foreach (string line in lines)
{
string[] separatedLine = line.Split(',');
if (separatedLine.Length < 2) continue;
if (separatedLine.Length < 2) { continue; }
string name = separatedLine[0];
string identifier = separatedLine[1];
string endpointStr = separatedLine[1];
DateTime? expirationTime = null;
if (separatedLine.Length > 2 && !string.IsNullOrEmpty(separatedLine[2]))
@@ -96,99 +71,105 @@ namespace Barotrauma.Networking
{
expirationTime = parsedTime;
}
else
{
string error = $"Failed to parse the ban duration of \"{name}\" ({separatedLine[2]}) from the legacy ban list file (text file which has now been changed to XML). Considering the ban permanent.";
DebugConsole.ThrowError(error);
GameServer.AddPendingMessageToOwner(error, ChatMessageType.Error);
}
}
string reason = separatedLine.Length > 3 ? string.Join(",", separatedLine.Skip(3)) : "";
if (expirationTime.HasValue && DateTime.Now > expirationTime.Value) continue;
if (expirationTime.HasValue && DateTime.Now > expirationTime.Value) { continue; }
if (identifier.Contains(".") || identifier.Contains(":"))
if (AccountId.Parse(endpointStr).TryUnwrap(out var accountId))
{
//identifier is an ip
bannedPlayers.Add(new BannedPlayer(name, identifier, reason, expirationTime));
bannedPlayers.Add(new BannedPlayer(name, accountId, reason, expirationTime));
}
else
else if (Address.Parse(endpointStr).TryUnwrap(out var address))
{
//identifier should be a steam id
if (ulong.TryParse(identifier, out ulong steamID))
{
bannedPlayers.Add(new BannedPlayer(name, steamID, reason, expirationTime));
}
else
{
DebugConsole.ThrowError("Error in banlist: \"" + identifier + "\" is not a valid IP or a Steam ID");
}
bannedPlayers.Add(new BannedPlayer(name, address, reason, expirationTime));
}
}
Save();
File.Delete(LegacySavePath);
}
public bool IsBanned(IPAddress IP, ulong steamID, ulong ownerSteamID, out string reason)
private void LoadBanList()
{
reason = string.Empty;
if (IPAddress.IsLoopback(IP)) { return false; }
var bannedPlayer = bannedPlayers.Find(bp =>
bp.CompareTo(IP) ||
(steamID > 0 && (bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID)) ||
(ownerSteamID > 0 && (bp.SteamID == ownerSteamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == ownerSteamID)));
reason = bannedPlayer?.Reason;
return bannedPlayer != null;
}
XDocument? doc = XMLExtensions.TryLoadXml(SavePath);
if (doc?.Root is null) { return; }
public bool IsBanned(IPAddress IP, out string reason)
{
reason = string.Empty;
if (IPAddress.IsLoopback(IP)) { return false; }
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
var bannedPlayer = bannedPlayers.Find(bp => bp.CompareTo(IP));
reason = bannedPlayer?.Reason;
return bannedPlayer != null;
}
public bool IsBanned(ulong steamID, out string reason)
{
reason = string.Empty;
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
var bannedPlayer = bannedPlayers.Find(bp =>
steamID > 0 &&
(bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID));
reason = bannedPlayer?.Reason;
return bannedPlayer != null;
}
public void BanPlayer(string name, IPAddress ip, string reason, TimeSpan? duration)
{
string ipStr = ip.IsIPv4MappedToIPv6 ? ip.MapToIPv4NoThrow().ToString() : ip.ToString();
BanPlayer(name, ipStr, 0, reason, duration);
}
public void BanPlayer(string name, string endPoint, string reason, TimeSpan? duration)
{
BanPlayer(name, endPoint, 0, reason, duration);
}
public void BanPlayer(string name, ulong steamID, string reason, TimeSpan? duration)
{
if (steamID == 0) { return; }
BanPlayer(name, "", steamID, reason, duration);
}
private void BanPlayer(string name, string endPoint, ulong steamID, string reason, TimeSpan? duration)
{
var existingBan = bannedPlayers.Find(bp => bp.EndPoint == endPoint && bp.SteamID == steamID);
if (existingBan != null)
static Option<BannedPlayer> loadFromElement(XElement element)
{
if (!duration.HasValue) return;
var accountId = AccountId.Parse(element.GetAttributeString("accountid", ""));
var address = Address.Parse(element.GetAttributeString("address", ""));
DebugConsole.Log("Set \"" + name + "\"'s ban duration to " + duration.Value);
existingBan.ExpirationTime = DateTime.Now + duration.Value;
Save();
return;
var name = element.GetAttributeString("name", "")!;
var reason = element.GetAttributeString("reason", "")!;
DateTime? expirationTime = DateTime.FromBinary(unchecked((long)element.GetAttributeUInt64("expirationtime", 0)));
if (expirationTime < DateTime.Now) { expirationTime = null; }
if (accountId.IsNone() && address.IsNone()) { return Option<BannedPlayer>.None(); }
Either<Address, AccountId> addressOrAccountId = accountId.TryUnwrap(out var accId)
? (Either<Address, AccountId>)accId
: address.TryUnwrap(out var addr)
? addr
: throw new InvalidCastException();
return Option<BannedPlayer>.Some(new BannedPlayer(name, addressOrAccountId, reason, expirationTime));
}
bannedPlayers.AddRange(doc.Root.Elements().Select(loadFromElement)
.OfType<Some<BannedPlayer>>().Select(o => o.Value));
}
private void RemoveExpired()
{
bannedPlayers.RemoveAll(bp => bp.Expired);
}
public bool IsBanned(Endpoint endpoint, out string reason)
=> IsBanned(endpoint.Address, out reason);
public bool IsBanned(Address address, out string reason)
{
RemoveExpired();
if (address.IsLocalHost)
{
reason = string.Empty;
return false;
}
var bannedPlayer = bannedPlayers.Find(bp => bp.AddressOrAccountId.TryGet(out Address adr) && address.Equals(adr));
reason = bannedPlayer?.Reason ?? string.Empty;
return bannedPlayer != null;
}
System.Diagnostics.Debug.Assert(!name.Contains(','));
public bool IsBanned(AccountId accountId, out string reason)
{
RemoveExpired();
var bannedPlayer = bannedPlayers.Find(bp => bp.AddressOrAccountId.TryGet(out AccountId id) && accountId.Equals(id));
reason = bannedPlayer?.Reason ?? string.Empty;
return bannedPlayer != null;
}
public void BanPlayer(string name, Endpoint endpoint, string reason, TimeSpan? duration)
=> BanPlayer(name, endpoint.Address, reason, duration);
public void BanPlayer(string name, Either<Address, AccountId> addressOrAccountId, string reason, TimeSpan? duration)
{
if (addressOrAccountId.TryGet(out Address address) && address.IsLocalHost) { return; }
var existingBan = bannedPlayers.Find(bp => bp.AddressOrAccountId == addressOrAccountId);
if (existingBan != null) { bannedPlayers.Remove(existingBan); }
string logMsg = "Banned " + name;
if (!string.IsNullOrEmpty(reason)) logMsg += ", reason: " + reason;
if (duration.HasValue) logMsg += ", duration: " + duration.Value.ToString();
if (!string.IsNullOrEmpty(reason)) { logMsg += ", reason: " + reason; }
if (duration.HasValue) { logMsg += ", duration: " + duration.Value.ToString(); }
DebugConsole.Log(logMsg);
@@ -198,46 +179,19 @@ namespace Barotrauma.Networking
expirationTime = DateTime.Now + duration.Value;
}
if (!string.IsNullOrEmpty(endPoint))
{
bannedPlayers.Add(new BannedPlayer(name, endPoint, reason, expirationTime));
}
else if (steamID > 0)
{
bannedPlayers.Add(new BannedPlayer(name, steamID, reason, expirationTime));
}
else
{
DebugConsole.ThrowError("Failed to ban a client (no valid IP or Steam ID given)");
return;
}
bannedPlayers.Add(new BannedPlayer(name, addressOrAccountId, reason, expirationTime));
Save();
}
public void UnbanPlayer(string name)
public void UnbanPlayer(Endpoint endpoint)
=> UnbanPlayer(endpoint.Address);
public void UnbanPlayer(Either<Address, AccountId> addressOrAccountId)
{
name = name.ToLower();
var player = bannedPlayers.Find(bp => bp.Name.ToLower() == name);
var player = bannedPlayers.Find(bp => bp.AddressOrAccountId == addressOrAccountId);
if (player == null)
{
DebugConsole.Log("Could not unban player \"" + name + "\". Matching player not found.");
}
else
{
RemoveBan(player);
}
}
public void UnbanEndPoint(string endPoint)
{
ulong steamId = SteamManager.SteamIDStringToUInt64(endPoint);
var player = bannedPlayers.Find(bp =>
bp.EndPoint == endPoint ||
(steamId != 0 && steamId == SteamManager.SteamIDStringToUInt64(bp.EndPoint)));
if (player == null)
{
DebugConsole.Log("Could not unban endpoint \"" + endPoint + "\". Matching player not found.");
DebugConsole.Log("Could not unban endpoint \"" + addressOrAccountId + "\". Matching player not found.");
}
else
{
@@ -255,49 +209,38 @@ namespace Barotrauma.Networking
Save();
}
private void RangeBan(BannedPlayer banned)
{
banned.EndPoint = ToRange(banned.EndPoint);
BannedPlayer bp;
while ((bp = bannedPlayers.Find(x => banned.CompareTo(x.EndPoint))) != null)
{
//remove all specific bans that are now covered by the rangeban
bannedPlayers.Remove(bp);
}
bannedPlayers.Add(banned);
Save();
}
public void Save()
{
GameServer.Log("Saving banlist", ServerLog.MessageType.ServerMessage);
GameMain.Server?.ServerSettings?.UpdateFlag(ServerSettings.NetFlags.Properties);
bannedPlayers.RemoveAll(bp => bp.ExpirationTime.HasValue && DateTime.Now > bp.ExpirationTime.Value);
RemoveExpired();
List<string> lines = new List<string>();
foreach (BannedPlayer banned in bannedPlayers)
static XElement saveToElement(BannedPlayer bannedPlayer)
{
string line = banned.Name;
line += "," + ((banned.SteamID > 0) ? SteamManager.SteamIDUInt64ToString(banned.SteamID) : banned.EndPoint);
line += "," + (banned.ExpirationTime.HasValue ? banned.ExpirationTime.Value.ToString() : "");
if (!string.IsNullOrWhiteSpace(banned.Reason)) line += "," + banned.Reason;
XElement retVal = new XElement("ban");
retVal.SetAttributeValue("name", bannedPlayer.Name);
retVal.SetAttributeValue("reason", bannedPlayer.Reason);
if (bannedPlayer.AddressOrAccountId.TryGet(out AccountId accountId))
{
retVal.SetAttributeValue("accountid", accountId.StringRepresentation);
}
else if (bannedPlayer.AddressOrAccountId.TryGet(out Address address))
{
retVal.SetAttributeValue("address", address.StringRepresentation);
}
if (bannedPlayer.ExpirationTime is { } expirationTime)
{
retVal.SetAttributeValue("expirationtime", unchecked((ulong)expirationTime.ToBinary()));
}
lines.Add(line);
return retVal;
}
try
{
File.WriteAllLines(SavePath, lines);
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving the list of banned players to " + SavePath + " failed", e);
}
XDocument doc = new XDocument(new XElement("bannedplayers"));
bannedPlayers.Select(saveToElement).ForEach(doc.Root!.Add);
doc.SaveSafe(SavePath);
}
public void ServerAdminWrite(IWriteMessage outMsg, Client c)
@@ -309,12 +252,12 @@ namespace Barotrauma.Networking
if (!c.HasPermission(ClientPermissions.Ban))
{
outMsg.Write(false); outMsg.WritePadBits();
outMsg.WriteBoolean(false); outMsg.WritePadBits();
return;
}
outMsg.Write(true);
outMsg.Write(c.Connection == GameMain.Server.OwnerConnection);
outMsg.WriteBoolean(true);
outMsg.WriteBoolean(c.Connection == GameMain.Server.OwnerConnection);
outMsg.WritePadBits();
outMsg.WriteVariableUInt32((UInt32)bannedPlayers.Count);
@@ -322,27 +265,33 @@ namespace Barotrauma.Networking
{
BannedPlayer bannedPlayer = bannedPlayers[i];
outMsg.Write(bannedPlayer.Name);
outMsg.Write(bannedPlayer.UniqueIdentifier);
outMsg.Write(bannedPlayer.IsRangeBan);
outMsg.Write(bannedPlayer.ExpirationTime != null);
outMsg.WriteString(bannedPlayer.Name);
outMsg.WriteUInt32(bannedPlayer.UniqueIdentifier);
outMsg.WriteBoolean(bannedPlayer.ExpirationTime != null);
outMsg.WritePadBits();
if (bannedPlayer.ExpirationTime != null)
{
double hoursFromNow = (bannedPlayer.ExpirationTime.Value - DateTime.Now).TotalHours;
outMsg.Write(hoursFromNow);
outMsg.WriteDouble(hoursFromNow);
}
outMsg.Write(bannedPlayer.Reason ?? "");
outMsg.WriteString(bannedPlayer.Reason ?? "");
if (c.Connection == GameMain.Server.OwnerConnection)
{
outMsg.Write(bannedPlayer.EndPoint);
outMsg.Write(bannedPlayer.SteamID);
if (bannedPlayer.AddressOrAccountId.TryGet(out Address endpoint))
{
outMsg.WriteBoolean(true); outMsg.WritePadBits();
outMsg.WriteString(endpoint.StringRepresentation);
}
else
{
outMsg.WriteBoolean(false); outMsg.WritePadBits();
outMsg.WriteString(((SteamId)bannedPlayer.AddressOrAccountId).StringRepresentation);
}
}
}
}
catch (Exception e)
{
string errorMsg = "Error while writing banlist. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
@@ -355,38 +304,25 @@ namespace Barotrauma.Networking
{
if (!c.HasPermission(ClientPermissions.Ban))
{
UInt16 removeCount = incMsg.ReadUInt16();
incMsg.BitPosition += removeCount * 4 * 8;
UInt16 rangeBanCount = incMsg.ReadUInt16();
incMsg.BitPosition += rangeBanCount * 4 * 8;
UInt32 removeCount = incMsg.ReadVariableUInt32();
incMsg.BitPosition += (int)removeCount * 4 * 8;
return false;
}
else
{
UInt16 removeCount = incMsg.ReadUInt16();
UInt32 removeCount = incMsg.ReadVariableUInt32();
for (int i = 0; i < removeCount; i++)
{
UInt16 id = incMsg.ReadUInt16();
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
UInt32 id = incMsg.ReadUInt32();
BannedPlayer? bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
if (bannedPlayer != null)
{
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.EndPoint + ")", ServerLog.MessageType.ConsoleUsage);
GameServer.Log(GameServer.ClientLogName(c) + " unbanned " + bannedPlayer.Name + " (" + bannedPlayer.AddressOrAccountId + ")", ServerLog.MessageType.ConsoleUsage);
RemoveBan(bannedPlayer);
}
}
Int16 rangeBanCount = incMsg.ReadInt16();
for (int i = 0; i < rangeBanCount; i++)
{
UInt16 id = incMsg.ReadUInt16();
BannedPlayer bannedPlayer = bannedPlayers.Find(p => p.UniqueIdentifier == id);
if (bannedPlayer != null)
{
GameServer.Log(GameServer.ClientLogName(c) + " rangebanned " + bannedPlayer.Name + " (" + bannedPlayer.EndPoint + ")", ServerLog.MessageType.ConsoleUsage);
RangeBan(bannedPlayer);
}
}
return removeCount > 0 || rangeBanCount > 0;
return removeCount > 0;
}
}
}
@@ -212,24 +212,24 @@ namespace Barotrauma.Networking
public virtual void ServerWrite(IWriteMessage msg, Client c)
{
msg.Write((byte)ServerNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);
msg.WriteByte((byte)ServerNetObject.CHAT_MESSAGE);
msg.WriteUInt16(NetStateID);
msg.WriteRangedInteger((int)Type, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
msg.Write((byte)ChangeType);
msg.Write(Text);
msg.WriteByte((byte)ChangeType);
msg.WriteString(Text);
msg.Write(SenderName);
msg.Write(SenderClient != null);
msg.WriteString(SenderName);
msg.WriteBoolean(SenderClient != null);
if (SenderClient != null)
{
msg.Write((SenderClient.SteamID != 0) ? SenderClient.SteamID : SenderClient.ID);
msg.WriteString(SenderClient.AccountId.TryUnwrap(out var accountId) ? accountId.StringRepresentation : SenderClient.SessionId.ToString());
}
msg.Write(Sender != null && c.InGame);
msg.WriteBoolean(Sender != null && c.InGame);
if (Sender != null && c.InGame)
{
msg.Write(Sender.ID);
msg.WriteUInt16(Sender.ID);
}
msg.Write(customTextColor != null);
msg.WriteBoolean(customTextColor != null);
if (customTextColor != null)
{
msg.WriteColorR8G8B8A8(customTextColor.Value);
@@ -237,7 +237,7 @@ namespace Barotrauma.Networking
msg.WritePadBits();
if (Type == ChatMessageType.ServerMessageBoxInGame)
{
msg.Write(IconStyle);
msg.WriteString(IconStyle);
}
}
}
@@ -8,12 +8,16 @@ namespace Barotrauma.Networking
{
public bool VoiceEnabled = true;
public UInt16 LastRecvClientListUpdate = 0;
public UInt16 LastRecvClientListUpdate
= NetIdUtils.GetIdOlderThan(GameMain.Server.LastClientListUpdateID);
public UInt16 LastSentServerSettingsUpdate = 0;
public UInt16 LastRecvServerSettingsUpdate = 0;
public UInt16 LastSentServerSettingsUpdate
= NetIdUtils.GetIdOlderThan(GameMain.Server.ServerSettings.LastUpdateIdForFlag[ServerSettings.NetFlags.Properties]);
public UInt16 LastRecvServerSettingsUpdate
= NetIdUtils.GetIdOlderThan(GameMain.Server.ServerSettings.LastUpdateIdForFlag[ServerSettings.NetFlags.Properties]);
public UInt16 LastRecvLobbyUpdate = 0;
public UInt16 LastRecvLobbyUpdate
= NetIdUtils.GetIdOlderThan(GameMain.NetLobbyScreen.LastUpdateID);
public UInt16 LastSentChatMsgID = 0; //last msg this client said
public UInt16 LastRecvChatMsgID = 0; //last msg this client knows about
@@ -21,7 +25,8 @@ namespace Barotrauma.Networking
public UInt16 LastSentEntityEventID = 0;
public UInt16 LastRecvEntityEventID = 0;
public readonly Dictionary<MultiPlayerCampaign.NetFlags, UInt16> LastRecvCampaignUpdate = new Dictionary<MultiPlayerCampaign.NetFlags, ushort>();
public readonly Dictionary<MultiPlayerCampaign.NetFlags, UInt16> LastRecvCampaignUpdate
= new Dictionary<MultiPlayerCampaign.NetFlags, UInt16>();
public UInt16 LastRecvCampaignSave = 0;
public (UInt16 saveId, float time) LastCampaignSaveSendTime;
@@ -57,11 +62,13 @@ namespace Barotrauma.Networking
public bool ReadyToStart;
public List<JobVariant> JobPreferences;
public List<JobVariant> JobPreferences { get; set; }
public JobVariant AssignedJob;
public float DeleteDisconnectedTimer;
public DateTime JoinTime;
private CharacterInfo characterInfo;
public CharacterInfo CharacterInfo
{
@@ -105,15 +112,26 @@ namespace Barotrauma.Networking
}
}
private List<Client> kickVoters;
public int KickVoteCount
{
get { return kickVoters.Count; }
}
partial void InitProjSpecific()
{
kickVoters = new List<Client>();
JobPreferences = new List<JobVariant>();
VoipQueue = new VoipQueue(ID, true, true);
VoipQueue = new VoipQueue(SessionId, true, true);
GameMain.Server.VoipServer.RegisterQueue(VoipQueue);
//initialize to infinity, gets set to a proper value when initializing midround syncing
MidRoundSyncTimeOut = double.PositiveInfinity;
JoinTime = DateTime.Now;
}
partial void DisposeProjSpecific()
@@ -147,7 +165,22 @@ namespace Barotrauma.Networking
{
if (string.IsNullOrWhiteSpace(name)) { return false; }
char[] disallowedChars = new char[] { ';', ',', '<', '>', '/', '\\', '[', ']', '"', '?' };
char[] disallowedChars =
{
//',', //previously disallowed because of the ban list format
';',
'<',
'>',
'/', //disallowed because of server messages using forward slash as a delimiter (TODO: implement escaping)
'\\',
'[',
']',
'"',
'?'
};
if (name.Any(c => disallowedChars.Contains(c))) { return false; }
foreach (char character in name)
@@ -158,34 +191,88 @@ namespace Barotrauma.Networking
return true;
}
public bool EndpointMatches(string endPoint)
public bool AddressMatches(Address address)
{
return Connection.EndpointMatches(endPoint);
return Connection.Endpoint.Address.Equals(address);
}
public void AddKickVote(Client voter)
{
if (voter != null && !kickVoters.Contains(voter)) { kickVoters.Add(voter); }
}
public void RemoveKickVote(Client voter)
{
kickVoters.Remove(voter);
}
public bool HasKickVoteFrom(Client voter)
{
return kickVoters.Contains(voter);
}
public bool HasKickVoteFromSessionId(int id)
{
return kickVoters.Any(k => k.SessionId == id);
}
public static void UpdateKickVotes(IReadOnlyList<Client> connectedClients)
{
foreach (Client client in connectedClients)
{
client.kickVoters.RemoveAll(voter => !connectedClients.Contains(voter));
}
}
/// <summary>
/// Reset what this client has voted for and the kick votes given to this client
/// </summary>
public void ResetVotes(bool resetKickVotes)
{
for (int i = 0; i < votes.Length; i++)
{
votes[i] = null;
}
if (resetKickVotes)
{
kickVoters.Clear();
}
}
public void SetPermissions(ClientPermissions permissions, IEnumerable<DebugConsole.Command> permittedConsoleCommands)
{
this.Permissions = permissions;
this.PermittedConsoleCommands.Clear();
foreach (var command in permittedConsoleCommands)
Permissions = permissions;
PermittedConsoleCommands.Clear();
PermittedConsoleCommands.UnionWith(permittedConsoleCommands);
if (Permissions.HasFlag(ClientPermissions.ManageSettings))
{
this.PermittedConsoleCommands.Add(command);
//ensure the client has the up-to-date server settings
GameMain.Server?.ServerSettings?.ForcePropertyUpdate();
}
}
public void GivePermission(ClientPermissions permission)
{
if (!this.Permissions.HasFlag(permission)) this.Permissions |= permission;
if (!Permissions.HasFlag(permission))
{
Permissions |= permission;
if (permission.HasFlag(ClientPermissions.ManageSettings))
{
//ensure the client has the up-to-date server settings
GameMain.Server?.ServerSettings?.ForcePropertyUpdate();
}
}
}
public void RemovePermission(ClientPermissions permission)
{
this.Permissions &= ~permission;
Permissions &= ~permission;
}
public bool HasPermission(ClientPermissions permission)
{
return this.Permissions.HasFlag(permission);
return Permissions.HasFlag(permission);
}
}
}
@@ -32,23 +32,23 @@ namespace Barotrauma
if (GameMain.Server is null) { return; }
if (!(extraData is SpawnOrRemove entities)) { throw new Exception($"Malformed {nameof(EntitySpawner)} event: expected {nameof(SpawnOrRemove)}"); }
message.Write(entities is RemoveEntity);
message.WriteBoolean(entities is RemoveEntity);
if (entities is RemoveEntity)
{
message.Write(entities.ID);
message.WriteUInt16(entities.ID);
}
else
{
switch (entities.Entity)
{
case Item item:
message.Write((byte)SpawnableType.Item);
message.WriteByte((byte)SpawnableType.Item);
DebugConsole.Log(
$"Writing item spawn data {item} (ID: {entities.ID})");
item.WriteSpawnData(message, entities.ID, entities.InventoryID, entities.ItemContainerIndex, entities.SlotIndex);
break;
case Character character:
message.Write((byte)SpawnableType.Character);
message.WriteByte((byte)SpawnableType.Character);
DebugConsole.Log(
$"Writing character spawn data: {character} (ID: {entities.ID})");
character.WriteSpawnData(message, entities.ID, restrictMessageSize: true);
@@ -52,7 +52,7 @@ namespace Barotrauma.Networking
}
}
public static int MaxPacketsPerUpdate = 4;
public static int MaxPacketsPerUpdate = 10;
public float PacketsPerUpdate { get; set; } = 1.0f;
public byte[] Data { get; }
@@ -219,27 +219,27 @@ namespace Barotrauma.Networking
if (!transfer.Acknowledged)
{
message = new WriteOnlyMessage();
message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
message.WriteByte((byte)ServerPacketHeader.FILE_TRANSFER);
//if the recipient is the owner of the server (= a client running the server from the main exe)
//we don't need to send anything, the client can just read the file directly
if (transfer.Connection == GameMain.Server.OwnerConnection)
{
message.Write((byte)FileTransferMessageType.TransferOnSameMachine);
message.Write((byte)transfer.ID);
message.Write((byte)transfer.FileType);
message.Write(transfer.FilePath);
message.WriteByte((byte)FileTransferMessageType.TransferOnSameMachine);
message.WriteByte((byte)transfer.ID);
message.WriteByte((byte)transfer.FileType);
message.WriteString(transfer.FilePath);
peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
transfer.Status = FileTransferStatus.Finished;
}
else
{
message.Write((byte)FileTransferMessageType.Initiate);
message.Write((byte)transfer.ID);
message.Write((byte)transfer.FileType);
message.WriteByte((byte)FileTransferMessageType.Initiate);
message.WriteByte((byte)transfer.ID);
message.WriteByte((byte)transfer.FileType);
//message.Write((ushort)chunkLen);
message.Write(transfer.Data.Length);
message.Write(transfer.FileName);
message.WriteInt32(transfer.Data.Length);
message.WriteString(transfer.FileName);
peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
transfer.Status = FileTransferStatus.Sending;
@@ -262,13 +262,13 @@ namespace Barotrauma.Networking
int sendByteCount = (remaining > chunkLen ? chunkLen : (int)remaining);
message = new WriteOnlyMessage();
message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
message.Write((byte)FileTransferMessageType.Data);
message.WriteByte((byte)ServerPacketHeader.FILE_TRANSFER);
message.WriteByte((byte)FileTransferMessageType.Data);
message.Write((byte)transfer.ID);
message.Write(transfer.SentOffset);
message.WriteByte((byte)transfer.ID);
message.WriteInt32(transfer.SentOffset);
message.Write((ushort)sendByteCount);
message.WriteUInt16((ushort)sendByteCount);
int chunkDestPos = message.BytePosition;
message.BitPosition += sendByteCount * 8;
message.LengthBits = Math.Max(message.LengthBits, message.BitPosition);
@@ -39,7 +39,7 @@ namespace Barotrauma.Networking
string resultFileName
= dir.StartsWith(ContentPackage.LocalModsDir)
? $"Local_{mod.Name}"
: $"Workshop_{mod.Name}_{mod.SteamWorkshopId}";
: $"Workshop_{mod.Name}_{(mod.UgcId.TryUnwrap(out var ugcId) ? ugcId.ToString() : "NULL")}";
resultFileName = ToolBox.RemoveInvalidFileNameChars(resultFileName.Replace('\\', '_').Replace('/', '_'));
resultFileName = $"{resultFileName}{Extension}";
return Path.Combine(UploadFolder, resultFileName);
File diff suppressed because it is too large Load Diff
@@ -188,9 +188,9 @@ namespace Barotrauma
}
}
if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedConstruction != null)
if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedItem != null)
{
if (client.Character.SelectedConstruction.GetComponent<Steering>() != null)
if (client.Character.SelectedItem.GetComponent<Steering>() != null)
{
AdjustKarma(client.Character, SteerSubKarmaIncrease * deltaTime, "Steering the sub");
}
@@ -246,7 +246,7 @@ namespace Barotrauma.Networking
" (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago, " +
(lastSentToAnyoneTime - firstEventToResend.CreateTime).ToString("0.##") + " s older than last event sent to anyone)" +
" Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncOldEvent + "/ServerMessage.ExcessiveDesyncOldEvent");
server.DisconnectClient(c, PeerDisconnectPacket.WithReason(DisconnectReason.ExcessiveDesyncOldEvent));
}
);
}
@@ -260,7 +260,7 @@ namespace Barotrauma.Networking
{
DebugConsole.NewMessage(c.Name + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
GameServer.Log(GameServer.ClientLogName(c) + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncRemovedEvent + "/ServerMessage.ExcessiveDesyncRemovedEvent");
server.DisconnectClient(c, PeerDisconnectPacket.WithReason(DisconnectReason.ExcessiveDesyncRemovedEvent));
});
}
}
@@ -269,7 +269,7 @@ namespace Barotrauma.Networking
foreach (Client timedOutClient in timedOutClients)
{
GameServer.Log("Disconnecting client " + GameServer.ClientLogName(timedOutClient) + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
GameMain.Server.DisconnectClient(timedOutClient, "", DisconnectReason.SyncTimeout + "/ServerMessage.SyncTimeout");
GameMain.Server.DisconnectClient(timedOutClient, PeerDisconnectPacket.WithReason(DisconnectReason.SyncTimeout));
}
bufferedEvents.RemoveAll(b => b.IsProcessed);
@@ -344,15 +344,15 @@ namespace Barotrauma.Networking
if (client.NeedsMidRoundSync)
{
msg.Write((byte)ServerNetObject.ENTITY_EVENT_INITIAL);
msg.Write(client.UnreceivedEntityEventCount);
msg.Write(client.FirstNewEventID);
msg.WriteByte((byte)ServerNetObject.ENTITY_EVENT_INITIAL);
msg.WriteUInt16(client.UnreceivedEntityEventCount);
msg.WriteUInt16(client.FirstNewEventID);
Write(msg, eventsToSync, out sentEvents, client);
}
else
{
msg.Write((byte)ServerNetObject.ENTITY_EVENT);
msg.WriteByte((byte)ServerNetObject.ENTITY_EVENT);
Write(msg, eventsToSync, out sentEvents, client);
}
@@ -499,7 +499,7 @@ namespace Barotrauma.Networking
ReadWriteMessage buffer = new ReadWriteMessage();
byte[] temp = msg.ReadBytes(msgLength - 2);
buffer.Write(temp, 0, msgLength - 2);
buffer.WriteBytes(temp, 0, msgLength - 2);
buffer.BitPosition = 0;
BufferEvent(new BufferedEvent(sender, sender.Character, characterStateID, entity, buffer));
@@ -1,4 +1,5 @@
using System;
using Barotrauma.Steam;
using System;
namespace Barotrauma.Networking
{
@@ -6,21 +7,21 @@ namespace Barotrauma.Networking
{
public override void ServerWrite(IWriteMessage msg, Client c)
{
msg.Write((byte)ServerNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);
msg.WriteByte((byte)ServerNetObject.CHAT_MESSAGE);
msg.WriteUInt16(NetStateID);
msg.WriteRangedInteger((int)ChatMessageType.Order, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
msg.Write(SenderName);
msg.Write(SenderClient != null);
msg.WriteString(SenderName);
msg.WriteBoolean(SenderClient != null);
if (SenderClient != null)
{
msg.Write((SenderClient.SteamID != 0) ? SenderClient.SteamID : SenderClient.ID);
msg.WriteString(SenderClient.AccountId.TryUnwrap(out var accountId) ? accountId.StringRepresentation : SenderClient.SessionId.ToString());
}
msg.Write(Sender != null && c.InGame);
msg.WriteBoolean(Sender != null && c.InGame);
if (Sender != null && c.InGame)
{
msg.Write(Sender.ID);
}
msg.Write(false); //text color (no custom text colors for order messages)
msg.WriteUInt16(Sender.ID);
}
msg.WriteBoolean(false); //text color (no custom text colors for order messages)
msg.WritePadBits();
WriteOrder(msg);
}
@@ -1,24 +1,45 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Net;
using System.Linq;
using Barotrauma.Steam;
using Lidgren.Network;
namespace Barotrauma.Networking
{
class LidgrenServerPeer : ServerPeer
internal sealed class LidgrenServerPeer : ServerPeer
{
private NetPeerConfiguration netPeerConfiguration;
private NetServer netServer;
private readonly NetPeerConfiguration netPeerConfiguration;
private NetServer? netServer;
private readonly List<NetIncomingMessage> incomingLidgrenMessages;
public LidgrenServerPeer(int? ownKey, ServerSettings settings)
public LidgrenServerPeer(Option<int> ownKey, ServerSettings settings, Callbacks callbacks) : base(callbacks)
{
serverSettings = settings;
netServer = null;
netPeerConfiguration = new NetPeerConfiguration("barotrauma")
{
AcceptIncomingConnections = true,
AutoExpandMTU = false,
MaximumConnections = NetConfig.MaxPlayers * 2,
EnableUPnP = serverSettings.EnableUPnP,
Port = serverSettings.Port
};
netPeerConfiguration.DisableMessageType(
NetIncomingMessageType.DebugMessage
| NetIncomingMessageType.WarningMessage
| NetIncomingMessageType.Receipt
| NetIncomingMessageType.ErrorMessage
| NetIncomingMessageType.Error
| NetIncomingMessageType.UnconnectedData);
netPeerConfiguration.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
connectedClients = new List<NetworkConnection>();
pendingClients = new List<PendingClient>();
@@ -31,25 +52,7 @@ namespace Barotrauma.Networking
{
if (netServer != null) { return; }
var address = serverSettings.ListenIPAddress;
if (address == IPAddress.Any) address = IPAddress.IPv6Any;
netPeerConfiguration = new NetPeerConfiguration("barotrauma")
{
AcceptIncomingConnections = true,
AutoExpandMTU = false,
MaximumConnections = NetConfig.MaxPlayers * 2,
EnableUPnP = serverSettings.EnableUPnP,
Port = serverSettings.Port,
LocalAddress = address
};
netPeerConfiguration.DisableMessageType(NetIncomingMessageType.DebugMessage |
NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt |
NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error |
NetIncomingMessageType.UnconnectedData);
netPeerConfiguration.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
incomingLidgrenMessages.Clear();
netServer = new NetServer(netPeerConfiguration);
@@ -65,21 +68,21 @@ namespace Barotrauma.Networking
}
}
public override void Close(string msg = null)
public override void Close()
{
if (netServer == null) { return; }
for (int i = pendingClients.Count - 1; i >= 0; i--)
{
RemovePendingClient(pendingClients[i], DisconnectReason.ServerShutdown, msg);
RemovePendingClient(pendingClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
Disconnect(connectedClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
Disconnect(connectedClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
netServer.Shutdown(msg ?? DisconnectReason.ServerShutdown.ToString());
netServer.Shutdown(PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown).ToLidgrenStringRepresentation());
pendingClients.Clear();
connectedClients.Clear();
@@ -88,21 +91,17 @@ namespace Barotrauma.Networking
Steamworks.SteamServer.OnValidateAuthTicketResponse -= OnAuthChange;
OnShutdown?.Invoke();
callbacks.OnShutdown.Invoke();
}
public override void Update(float deltaTime)
{
if (netServer == null) { return; }
if (netServer is null) { return; }
if (OnOwnerDetermined != null && OwnerConnection != null)
{
OnOwnerDetermined?.Invoke(OwnerConnection);
OnOwnerDetermined = null;
}
ToolBox.ThrowIfNull(incomingLidgrenMessages);
netServer.ReadMessages(incomingLidgrenMessages);
//process incoming connections first
foreach (NetIncomingMessage inc in incomingLidgrenMessages.Where(m => m.MessageType == NetIncomingMessageType.ConnectionApproval))
{
@@ -129,7 +128,7 @@ namespace Barotrauma.Networking
catch (Exception e)
{
string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("LidgrenServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
GameAnalyticsManager.AddErrorEventOnce($"LidgrenServerPeer.Update:ClientReadException{e.TargetSite}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#else
@@ -141,7 +140,8 @@ namespace Barotrauma.Networking
{
PendingClient pendingClient = pendingClients[i];
var connection = pendingClient.Connection as LidgrenConnection;
LidgrenConnection connection = (LidgrenConnection)pendingClient.Connection;
if (connection.NetConnection.Status == NetConnectionStatus.InitiatedConnect ||
connection.NetConnection.Status == NetConnectionStatus.ReceivedInitiation ||
connection.NetConnection.Status == NetConnectionStatus.RespondedAwaitingApproval ||
@@ -149,6 +149,7 @@ namespace Barotrauma.Networking
{
continue;
}
UpdatePendingClient(pendingClient);
if (i >= pendingClients.Count || pendingClients[i] != pendingClient) { i--; }
}
@@ -158,7 +159,9 @@ namespace Barotrauma.Networking
private void InitUPnP()
{
if (netServer == null) { return; }
if (netServer is null) { return; }
ToolBox.ThrowIfNull(netPeerConfiguration);
netServer.UPnP.ForwardPort(netPeerConfiguration.Port, "barotrauma");
#if USE_STEAM
@@ -193,71 +196,74 @@ namespace Barotrauma.Networking
if (!skipDeny && connectedClients.Count >= serverSettings.MaxPlayers)
{
inc.SenderConnection.Deny(DisconnectReason.ServerFull.ToString());
inc.SenderConnection.Deny(PeerDisconnectPacket.WithReason(DisconnectReason.ServerFull).ToLidgrenStringRepresentation());
return;
}
if (serverSettings.BanList.IsBanned(inc.SenderConnection.RemoteEndPoint.Address, 0, 0, out string banReason))
if (serverSettings.BanList.IsBanned(new LidgrenEndpoint(inc.SenderConnection.RemoteEndPoint), out string banReason))
{
//IP banned: deny immediately
inc.SenderConnection.Deny(DisconnectReason.Banned.ToString() + "/ " + banReason);
inc.SenderConnection.Deny(PeerDisconnectPacket.Banned(banReason).ToLidgrenStringRepresentation());
return;
}
PendingClient pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
if (pendingClient == null)
if (pendingClient is null)
{
pendingClient = new PendingClient(new LidgrenConnection("PENDING", inc.SenderConnection, 0));
pendingClient = new PendingClient(new LidgrenConnection(inc.SenderConnection));
pendingClients.Add(pendingClient);
}
inc.SenderConnection.Approve();
}
private void HandleDataMessage(NetIncomingMessage inc)
private void HandleDataMessage(NetIncomingMessage lidgrenMsg)
{
if (netServer == null) { return; }
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == lidgrenMsg.SenderConnection);
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
IReadMessage inc = lidgrenMsg.ToReadMessage();
if (packetHeader.IsConnectionInitializationStep() && pendingClient != null)
var (_, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
if (packetHeader.IsConnectionInitializationStep() && pendingClient != null && initialization.HasValue)
{
ReadConnectionInitializationStep(pendingClient, new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
ReadConnectionInitializationStep(pendingClient, inc, initialization.Value);
}
else if (!packetHeader.IsConnectionInitializationStep())
{
LidgrenConnection conn = connectedClients.Find(c => (c is LidgrenConnection l) && l.NetConnection == inc.SenderConnection) as LidgrenConnection;
if (conn == null)
if (connectedClients.Find(c => c is LidgrenConnection l && l.NetConnection == lidgrenMsg.SenderConnection) is not LidgrenConnection conn)
{
if (pendingClient != null)
{
RemovePendingClient(pendingClient, DisconnectReason.AuthenticationRequired, "Received data message from unauthenticated client");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationRequired));
}
else if (inc.SenderConnection.Status != NetConnectionStatus.Disconnected &&
inc.SenderConnection.Status != NetConnectionStatus.Disconnecting)
else if (lidgrenMsg.SenderConnection.Status != NetConnectionStatus.Disconnected &&
lidgrenMsg.SenderConnection.Status != NetConnectionStatus.Disconnecting)
{
inc.SenderConnection.Disconnect(DisconnectReason.AuthenticationRequired.ToString() + "/ Received data message from unauthenticated client");
lidgrenMsg.SenderConnection.Disconnect(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationRequired).ToLidgrenStringRepresentation());
}
return;
}
if (pendingClient != null) { pendingClients.Remove(pendingClient); }
if (serverSettings.BanList.IsBanned(conn.IPEndPoint.Address, conn.SteamID, conn.OwnerSteamID, out string banReason))
if (serverSettings.BanList.IsBanned(conn.Endpoint, out string banReason)
|| (conn.AccountInfo.AccountId.TryUnwrap(out var accountId) && serverSettings.BanList.IsBanned(accountId, out banReason))
|| conn.AccountInfo.OtherMatchingIds.Any(id => serverSettings.BanList.IsBanned(id, out banReason)))
{
Disconnect(conn, DisconnectReason.Banned.ToString() + "/ " + banReason);
Disconnect(conn, PeerDisconnectPacket.Banned(banReason));
return;
}
UInt16 length = inc.ReadUInt16();
//DebugConsole.NewMessage(isCompressed + " " + isConnectionInitializationStep + " " + (int)incByte + " " + length);
IReadMessage msg = new ReadOnlyMessage(inc.Data, packetHeader.IsCompressed(), inc.PositionInBytes, length, conn);
OnMessageReceived?.Invoke(conn, msg);
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
callbacks.OnMessageReceived.Invoke(conn, packet.GetReadMessage(packetHeader.IsCompressed(), conn));
}
}
private void HandleStatusChanged(NetIncomingMessage inc)
{
if (netServer == null) { return; }
@@ -265,30 +271,29 @@ namespace Barotrauma.Networking
switch (inc.SenderConnection.Status)
{
case NetConnectionStatus.Disconnected:
string disconnectMsg;
LidgrenConnection conn = connectedClients.Select(c => c as LidgrenConnection).FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
LidgrenConnection? conn = connectedClients.Cast<LidgrenConnection>().FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
if (conn != null)
{
if (conn == OwnerConnection)
{
DebugConsole.NewMessage("Owner disconnected: closing the server...");
GameServer.Log("Owner disconnected: closing the server...", ServerLog.MessageType.ServerMessage);
Close(DisconnectReason.ServerShutdown.ToString() + "/ Owner disconnected");
Close();
}
else
{
disconnectMsg = $"ServerMessage.HasDisconnected~[client]={conn.Name}";
Disconnect(conn, disconnectMsg);
Disconnect(conn, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
}
else
{
PendingClient pendingClient = pendingClients.Find(c => (c.Connection is LidgrenConnection l) && l.NetConnection == inc.SenderConnection);
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
if (pendingClient != null)
{
RemovePendingClient(pendingClient, DisconnectReason.Unknown, $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
}
break;
}
}
@@ -298,44 +303,45 @@ namespace Barotrauma.Networking
Steamworks.SteamServer.OnValidateAuthTicketResponse += OnAuthChange;
}
private void OnAuthChange(Steamworks.SteamId steamID, Steamworks.SteamId ownerID, Steamworks.AuthResponse status)
private void OnAuthChange(Steamworks.SteamId steamId, Steamworks.SteamId ownerId, Steamworks.AuthResponse status)
{
if (netServer == null) { return; }
PendingClient pendingClient = pendingClients.Find(c => c.SteamID == steamID);
DebugConsole.Log(steamID + " validation: " + status+", "+(pendingClient!=null));
if (pendingClient == null)
PendingClient? pendingClient = pendingClients.Find(c => c.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId);
DebugConsole.Log($"{steamId} validation: {status}, {(pendingClient != null)}");
if (pendingClient is null)
{
if (status != Steamworks.AuthResponse.OK)
if (status == Steamworks.AuthResponse.OK) { return; }
if (connectedClients.Find(c
=> c.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId)
is LidgrenConnection connection)
{
LidgrenConnection connection = connectedClients.Find(c => c.SteamID == steamID) as LidgrenConnection;
if (connection != null)
{
Disconnect(connection, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam authentication status changed: " + status.ToString());
}
Disconnect(connection, PeerDisconnectPacket.SteamAuthError(status));
}
return;
}
LidgrenConnection pendingConnection = pendingClient.Connection as LidgrenConnection;
string banReason;
if (serverSettings.BanList.IsBanned(pendingConnection.NetConnection.RemoteEndPoint.Address, steamID, ownerID, out banReason))
LidgrenConnection pendingConnection = (LidgrenConnection)pendingClient.Connection;
if (serverSettings.BanList.IsBanned(pendingConnection.Endpoint, out string banReason)
|| serverSettings.BanList.IsBanned(new SteamId(steamId), out banReason)
|| serverSettings.BanList.IsBanned(new SteamId(ownerId), out banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
return;
}
if (status == Steamworks.AuthResponse.OK)
{
pendingClient.OwnerSteamID = ownerID;
pendingClient.Connection.SetAccountInfo(new AccountInfo(new SteamId(steamId), new SteamId(ownerId)));
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
pendingClient.UpdateTime = Timing.TotalTime;
}
else
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam authentication failed: " + status.ToString());
return;
RemovePendingClient(pendingClient, PeerDisconnectPacket.SteamAuthError(status));
}
}
@@ -343,151 +349,144 @@ namespace Barotrauma.Networking
{
if (netServer == null) { return; }
if (!(conn is LidgrenConnection lidgrenConn)) return;
if (!connectedClients.Contains(lidgrenConn))
if (!connectedClients.Contains(conn))
{
DebugConsole.ThrowError("Tried to send message to unauthenticated connection: " + lidgrenConn.IPString);
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {conn.Endpoint.StringRepresentation}");
return;
}
NetDeliveryMethod lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
switch (deliveryMethod)
{
case DeliveryMethod.Unreliable:
lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
break;
case DeliveryMethod.Reliable:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableUnordered;
break;
case DeliveryMethod.ReliableOrdered:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableOrdered;
break;
}
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
#if DEBUG
ToolBox.ThrowIfNull(netPeerConfiguration);
netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Server.SimulatedDuplicatesChance;
netPeerConfiguration.SimulatedMinimumLatency = GameMain.Server.SimulatedMinimumLatency;
netPeerConfiguration.SimulatedRandomLatency = GameMain.Server.SimulatedRandomLatency;
netPeerConfiguration.SimulatedLoss = GameMain.Server.SimulatedLoss;
#endif
NetOutgoingMessage lidgrenMsg = netServer.CreateMessage();
byte[] msgData = new byte[msg.LengthBytes];
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
lidgrenMsg.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
lidgrenMsg.Write((UInt16)length);
lidgrenMsg.Write(msgData, 0, length);
NetSendResult result = netServer.SendMessage(lidgrenMsg, lidgrenConn.NetConnection, lidgrenDeliveryMethod);
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
var headers = new PeerPacketHeaders
{
DebugConsole.NewMessage("Failed to send message to "+conn.Name+": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
}
DeliveryMethod = deliveryMethod,
PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None,
Initialization = null
};
var body = new PeerPacketMessage
{
Buffer = bufAux
};
SendMsgInternal(conn, headers, body);
}
public override void Disconnect(NetworkConnection conn,string msg=null)
public override void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket)
{
if (netServer == null) { return; }
if (!(conn is LidgrenConnection lidgrenConn)) { return; }
if (conn is not LidgrenConnection lidgrenConn) { return; }
if (connectedClients.Contains(lidgrenConn))
{
lidgrenConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(lidgrenConn);
OnDisconnect?.Invoke(conn, msg);
Steam.SteamManager.StopAuthSession(conn.SteamID);
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
if (conn.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId }) { SteamManager.StopAuthSession(steamId); }
}
lidgrenConn.NetConnection.Disconnect(msg ?? "Disconnected");
lidgrenConn.NetConnection.Disconnect(peerDisconnectPacket.ToLidgrenStringRepresentation());
}
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
{
LidgrenConnection lidgrenConn = conn as LidgrenConnection;
NetDeliveryMethod lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
switch (deliveryMethod)
{
case DeliveryMethod.Unreliable:
lidgrenDeliveryMethod = NetDeliveryMethod.Unreliable;
break;
case DeliveryMethod.Reliable:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableUnordered;
break;
case DeliveryMethod.ReliableOrdered:
lidgrenDeliveryMethod = NetDeliveryMethod.ReliableOrdered;
break;
}
IWriteMessage msgToSend = new WriteOnlyMessage();
msgToSend.WriteNetSerializableStruct(headers);
body?.Write(msgToSend);
NetOutgoingMessage lidgrenMsg = netServer.CreateMessage();
lidgrenMsg.Write(msg.Buffer, 0, msg.LengthBytes);
NetSendResult result = netServer.SendMessage(lidgrenMsg, lidgrenConn.NetConnection, lidgrenDeliveryMethod);
NetSendResult result = ForwardToLidgren(msgToSend, conn, headers.DeliveryMethod);
if (result != NetSendResult.Sent && result != NetSendResult.Queued)
{
DebugConsole.NewMessage("Failed to send message to " + conn.Name + ": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow);
DebugConsole.NewMessage($"Failed to send message to {conn.Endpoint}: {result}", Microsoft.Xna.Framework.Color.Yellow);
}
}
protected override void CheckOwnership(PendingClient pendingClient)
{
LidgrenConnection l = pendingClient.Connection as LidgrenConnection;
if (OwnerConnection == null &&
IPAddress.IsLoopback(l.NetConnection.RemoteEndPoint.Address.MapToIPv4NoThrow()) &&
ownerKey != null && pendingClient.OwnerKey != 0 && pendingClient.OwnerKey == ownerKey)
if (OwnerConnection == null
&& pendingClient.Connection is LidgrenConnection l
&& IPAddress.IsLoopback(l.NetConnection.RemoteEndPoint.Address)
&& ownerKey.IsSome() && pendingClient.OwnerKey == ownerKey)
{
ownerKey = null;
ownerKey = Option<int>.None();
OwnerConnection = pendingClient.Connection;
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
}
}
protected override void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket)
protected override void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient)
{
if (pendingClient.SteamID == null)
if (pendingClient.AccountInfo.AccountId.IsNone())
{
bool requireSteamAuth = GameSettings.CurrentConfig.RequireSteamAuthentication;
#if DEBUG
requireSteamAuth = false;
#endif
bool hasSteamAuth = packet.SteamAuthTicket.TryUnwrap(out var ticket);
//steam auth cannot be done (SteamManager not initialized or no ticket given),
//but it's not required either -> let the client join without auth
if ((!Steam.SteamManager.IsInitialized || (ticket?.Length ?? 0) == 0) &&
!requireSteamAuth)
if ((!SteamManager.IsInitialized || !hasSteamAuth) && !requireSteamAuth)
{
pendingClient.Connection.Name = name;
pendingClient.Name = name;
pendingClient.OwnerKey = ownKey;
pendingClient.Name = packet.Name;
pendingClient.OwnerKey = packet.OwnerKey;
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
else
{
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
if (!packet.SteamId.TryUnwrap(out var id) || id is not SteamId steamId)
{
if (requireSteamAuth)
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: " + authSessionStartState.ToString());
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.SteamAuthenticationFailed));
return;
}
else
}
else
{
Steamworks.BeginAuthResult authSessionStartState = SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{
steamId = 0;
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
if (requireSteamAuth)
{
RemovePendingClient(pendingClient, PeerDisconnectPacket.SteamAuthError(authSessionStartState));
}
else
{
packet.SteamId = Option<AccountId>.None();
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
}
}
pendingClient.SteamID = steamId;
pendingClient.Connection.Name = name;
pendingClient.Name = name;
pendingClient.OwnerKey = ownKey;
pendingClient.Connection.SetAccountInfo(new AccountInfo(packet.SteamId.Select(uid => (AccountId)uid)));
pendingClient.Name = packet.Name;
pendingClient.OwnerKey = packet.OwnerKey;
pendingClient.AuthSessionStarted = true;
}
}
else
{
if (pendingClient.SteamID != steamId)
if (pendingClient.AccountInfo.AccountId != packet.SteamId.Select(uid => (AccountId)uid))
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
return;
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.SteamAuthenticationFailed));
}
}
}
private NetSendResult ForwardToLidgren(IWriteMessage msg, NetworkConnection connection, DeliveryMethod deliveryMethod)
{
ToolBox.ThrowIfNull(netServer);
LidgrenConnection conn = (LidgrenConnection)connection;
return netServer.SendMessage(msg.ToLidgren(netServer), conn.NetConnection, deliveryMethod.ToLidgren());
}
}
}
}
@@ -1,75 +1,61 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Barotrauma.Extensions;
namespace Barotrauma.Networking
{
abstract class ServerPeer
internal abstract class ServerPeer
{
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
public delegate void DisconnectCallback(NetworkConnection connection, string reason);
public delegate void InitializationCompleteCallback(NetworkConnection connection);
public delegate void ShutdownCallback();
public delegate void OwnerDeterminedCallback(NetworkConnection connection);
public readonly record struct Callbacks(
Callbacks.MessageCallback OnMessageReceived,
Callbacks.DisconnectCallback OnDisconnect,
Callbacks.InitializationCompleteCallback OnInitializationComplete,
Callbacks.ShutdownCallback OnShutdown,
Callbacks.OwnerDeterminedCallback OnOwnerDetermined)
{
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
public delegate void DisconnectCallback(NetworkConnection connection, PeerDisconnectPacket peerDisconnectPacket);
public delegate void InitializationCompleteCallback(NetworkConnection connection, string? clientName);
public delegate void ShutdownCallback();
public delegate void OwnerDeterminedCallback(NetworkConnection connection);
}
public MessageCallback OnMessageReceived;
public DisconnectCallback OnDisconnect;
public InitializationCompleteCallback OnInitializationComplete;
public ShutdownCallback OnShutdown;
public OwnerDeterminedCallback OnOwnerDetermined;
protected int? ownerKey;
public NetworkConnection OwnerConnection { get; protected set; }
protected readonly Callbacks callbacks;
protected ServerPeer(Callbacks callbacks)
{
this.callbacks = callbacks;
}
public abstract void InitializeSteamServerCallbacks();
public abstract void Start();
public abstract void Close(string msg = null);
public abstract void Close();
public abstract void Update(float deltaTime);
public class PendingClient
protected sealed class PendingClient
{
public string Name;
public int OwnerKey;
public NetworkConnection Connection;
public string? Name;
public Option<int> OwnerKey;
public readonly NetworkConnection Connection;
public ConnectionInitialization InitializationStep;
public double UpdateTime;
public double TimeOut;
public int Retries;
private UInt64? steamId;
public UInt64? SteamID
{
get { return steamId; }
set
{
steamId = value;
Connection.SetSteamIDIfUnknown(value ?? 0);
}
}
private UInt64? ownerSteamId;
public UInt64? OwnerSteamID
{
get { return ownerSteamId; }
set
{
ownerSteamId = value;
Connection.SetOwnerSteamIDIfUnknown(value ?? 0);
}
}
public Int32? PasswordSalt;
public bool AuthSessionStarted;
public AccountInfo AccountInfo => Connection.AccountInfo;
public PendingClient(NetworkConnection conn)
{
OwnerKey = 0;
OwnerKey = Option<int>.None();
Connection = conn;
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
Retries = 0;
SteamID = null;
OwnerSteamID = null;
PasswordSalt = null;
UpdateTime = Timing.TotalTime + Timing.Step * 3.0;
TimeOut = NetworkConnection.TimeoutThreshold;
@@ -81,73 +67,70 @@ namespace Barotrauma.Networking
TimeOut = NetworkConnection.TimeoutThreshold;
}
}
protected List<NetworkConnection> connectedClients;
protected List<PendingClient> pendingClients;
protected ServerSettings serverSettings;
protected List<NetworkConnection> connectedClients = null!;
protected List<PendingClient> pendingClients = null!;
protected ServerSettings serverSettings = null!;
protected Option<int> ownerKey = null!;
protected NetworkConnection? OwnerConnection;
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc, ConnectionInitialization initializationStep)
{
pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
if (pendingClient.InitializationStep != initializationStep) return;
if (pendingClient.InitializationStep != initializationStep) { return; }
pendingClient.UpdateTime = Timing.TotalTime + Timing.Step;
switch (initializationStep)
{
case ConnectionInitialization.SteamTicketAndVersion:
string name = Client.SanitizeName(inc.ReadString());
int ownerKey = inc.ReadInt32();
UInt64 steamId = inc.ReadUInt64();
UInt16 ticketLength = inc.ReadUInt16();
byte[] ticketBytes = inc.ReadBytes(ticketLength);
var authPacket = INetSerializableStruct.Read<ClientSteamTicketAndVersionPacket>(inc);
if (!Client.IsValidName(name, serverSettings))
if (!Client.IsValidName(authPacket.Name, serverSettings))
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.InvalidName));
return;
}
string version = inc.ReadString();
bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
bool isCompatibleVersion =
Version.TryParse(authPacket.GameVersion, out var remoteVersion)
&& NetworkMember.IsCompatible(remoteVersion, GameMain.Version);
if (!isCompatibleVersion)
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");
RemovePendingClient(pendingClient, PeerDisconnectPacket.InvalidVersion());
GameServer.Log($"{name} ({steamId}) couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage($"{name} ({steamId}) couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
GameServer.Log($"{authPacket.Name} ({authPacket.SteamId}) couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage($"{authPacket.Name} ({authPacket.SteamId}) couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
return;
}
LanguageIdentifier language = inc.ReadIdentifier().ToLanguageIdentifier();
pendingClient.Connection.Language = language;
pendingClient.Connection.Language = authPacket.Language.ToLanguageIdentifier();
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), authPacket.Name.ToLower()));
if (nameTaken != null)
{
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
GameServer.Log($"{name} ({steamId}) couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.NameTaken));
GameServer.Log($"{authPacket.Name} ({authPacket.SteamId}) couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
return;
}
if (!pendingClient.AuthSessionStarted)
{
ProcessAuthTicket(name, ownerKey, steamId, pendingClient, ticketBytes);
ProcessAuthTicket(authPacket, pendingClient);
}
break;
case ConnectionInitialization.Password:
int pwLength = inc.ReadByte();
byte[] incPassword = inc.ReadBytes(pwLength);
if (pendingClient.PasswordSalt == null)
var passwordPacket = INetSerializableStruct.Read<ClientPeerPasswordPacket>(inc);
if (pendingClient.PasswordSalt is null)
{
DebugConsole.ThrowError("Received password message from client without salt");
return;
}
if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
if (serverSettings.IsPasswordCorrect(passwordPacket.Password, pendingClient.PasswordSalt.Value))
{
pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
}
@@ -156,13 +139,13 @@ namespace Barotrauma.Networking
pendingClient.Retries++;
if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
{
string banMsg = "Failed to enter correct password too many times";
const string banMsg = "Failed to enter correct password too many times";
BanPendingClient(pendingClient, banMsg, null);
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banMsg));
return;
}
}
pendingClient.UpdateTime = Timing.TotalTime;
break;
case ConnectionInitialization.ContentPackageOrder:
@@ -172,37 +155,49 @@ namespace Barotrauma.Networking
}
}
protected abstract void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket);
protected abstract void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient);
protected void BanPendingClient(PendingClient pendingClient, string banReason, TimeSpan? duration)
{
if (pendingClient.Connection is LidgrenConnection l)
void banAccountId(AccountId accountId)
{
serverSettings.BanList.BanPlayer(pendingClient.Name, l.NetConnection.RemoteEndPoint.Address, banReason, duration);
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", accountId, banReason, duration);
}
else if (pendingClient.Connection is SteamP2PConnection s)
if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var id)) { banAccountId(id); }
pendingClient.AccountInfo.OtherMatchingIds.ForEach(banAccountId);
if (pendingClient.AccountInfo.AccountId.TryUnwrap(out var accountId))
{
serverSettings.BanList.BanPlayer(pendingClient.Name, s.SteamID, banReason, duration);
serverSettings.BanList.BanPlayer(pendingClient.Name, s.OwnerSteamID, banReason, duration);
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", accountId, banReason, duration);
}
else
{
serverSettings.BanList.BanPlayer(pendingClient.Name ?? "Player", pendingClient.Connection.Endpoint, banReason, duration);
}
}
protected bool IsPendingClientBanned(PendingClient pendingClient, out string banReason)
protected bool IsPendingClientBanned(PendingClient pendingClient, out string? banReason)
{
if (pendingClient.Connection is LidgrenConnection l)
bool isAccountIdBanned(AccountId accountId, out string? banReason)
{
return serverSettings.BanList.IsBanned(l.NetConnection.RemoteEndPoint.Address, out banReason);
return serverSettings.BanList.IsBanned(accountId, out banReason);
}
else if (pendingClient.Connection is SteamP2PConnection s)
banReason = default;
bool isBanned = pendingClient.AccountInfo.AccountId.TryUnwrap(out var id)
&& isAccountIdBanned(id, out banReason);
foreach (var otherId in pendingClient.AccountInfo.OtherMatchingIds)
{
return serverSettings.BanList.IsBanned(s.SteamID, out banReason) ||
serverSettings.BanList.IsBanned(s.OwnerSteamID, out banReason);
if (isBanned) { break; }
isBanned |= isAccountIdBanned(otherId, out banReason);
}
banReason = null;
return false;
return isBanned;
}
protected abstract void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg);
protected abstract void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body);
protected void UpdatePendingClient(PendingClient pendingClient)
{
@@ -213,12 +208,12 @@ namespace Barotrauma.Networking
if (!skipRemove && connectedClients.Count >= serverSettings.MaxPlayers)
{
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.ServerFull));
}
if (IsPendingClientBanned(pendingClient, out string banReason))
if (IsPendingClientBanned(pendingClient, out string? banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
return;
}
@@ -228,80 +223,86 @@ namespace Barotrauma.Networking
connectedClients.Add(newConnection);
pendingClients.Remove(pendingClient);
CheckOwnership(pendingClient);
callbacks.OnInitializationComplete.Invoke(newConnection, pendingClient.Name);
OnInitializationComplete?.Invoke(newConnection);
CheckOwnership(pendingClient);
}
pendingClient.TimeOut -= Timing.Step;
if (pendingClient.TimeOut < 0.0)
{
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Timeout));
}
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
pendingClient.UpdateTime = Timing.TotalTime + 1.0;
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep |
PacketHeader.IsServerMessage));
outMsg.Write((byte)pendingClient.InitializationStep);
PeerPacketHeaders headers = new PeerPacketHeaders
{
DeliveryMethod = DeliveryMethod.Reliable,
PacketHeader = PacketHeader.IsConnectionInitializationStep | PacketHeader.IsServerMessage,
Initialization = pendingClient.InitializationStep
};
INetSerializableStruct? structToSend = null;
switch (pendingClient.InitializationStep)
{
case ConnectionInitialization.ContentPackageOrder:
outMsg.Write(GameMain.Server.ServerName);
var mpContentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).ToList();
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count);
for (int i = 0; i < mpContentPackages.Count; i++)
DateTime timeNow = DateTime.UtcNow;
structToSend = new ServerPeerContentPackageOrderPacket
{
outMsg.Write(mpContentPackages[i].Name);
byte[] hashBytes = mpContentPackages[i].Hash.ByteRepresentation;
outMsg.WriteVariableUInt32((UInt32)hashBytes.Length);
outMsg.Write(hashBytes, 0, hashBytes.Length);
outMsg.Write(mpContentPackages[i].SteamWorkshopId);
UInt32 installTimeDiffSeconds = (UInt32)((mpContentPackages[i].InstallTime ?? DateTime.UtcNow) - DateTime.UtcNow).TotalSeconds;
outMsg.Write(installTimeDiffSeconds);
}
ServerName = GameMain.Server.ServerName,
ContentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent || cp.Files.All(f => f is SubmarineFile))
.Select(contentPackage => new ServerContentPackage(contentPackage, timeNow))
.ToImmutableArray()
};
break;
case ConnectionInitialization.Password:
outMsg.Write(pendingClient.PasswordSalt == null); outMsg.WritePadBits();
if (pendingClient.PasswordSalt == null)
structToSend = new ServerPeerPasswordPacket
{
pendingClient.PasswordSalt = Lidgren.Network.CryptoRandom.Instance.Next();
outMsg.Write(pendingClient.PasswordSalt.Value);
}
else
Salt = GetSalt(pendingClient),
RetriesLeft = Option<int>.Some(pendingClient.Retries)
};
static Option<int> GetSalt(PendingClient client)
{
outMsg.Write(pendingClient.Retries);
if (client.PasswordSalt is { } salt) { return Option<int>.Some(salt); }
salt = Lidgren.Network.CryptoRandom.Instance.Next();
client.PasswordSalt = salt;
return Option<int>.Some(salt);
}
break;
}
SendMsgInternal(pendingClient.Connection, DeliveryMethod.Reliable, outMsg);
SendMsgInternal(pendingClient.Connection, headers, structToSend);
}
protected virtual void CheckOwnership(PendingClient pendingClient) { }
public void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string msg)
protected void RemovePendingClient(PendingClient pendingClient, PeerDisconnectPacket peerDisconnectPacket)
{
if (pendingClients.Contains(pendingClient))
{
Disconnect(pendingClient.Connection, reason + "/" + msg);
Disconnect(pendingClient.Connection, peerDisconnectPacket);
pendingClients.Remove(pendingClient);
if (pendingClient.AuthSessionStarted)
if (pendingClient.AuthSessionStarted && pendingClient.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId })
{
Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value);
pendingClient.SteamID = null;
pendingClient.OwnerSteamID = null;
Steam.SteamManager.StopAuthSession(steamId);
pendingClient.Connection.SetAccountInfo(AccountInfo.None);
pendingClient.AuthSessionStarted = false;
}
}
}
public abstract void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true);
public abstract void Disconnect(NetworkConnection conn, string msg = null);
public abstract void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket);
}
}
}
@@ -1,70 +1,61 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Net;
using System.Linq;
using System.Threading;
namespace Barotrauma.Networking
{
class SteamP2PServerPeer : ServerPeer
internal sealed class SteamP2PServerPeer : ServerPeer
{
private bool started;
public UInt64 OwnerSteamID
{
get;
private set;
}
private readonly SteamId ownerSteamId;
private UInt64 ownerKey64 => unchecked((UInt64)ownerKey.Value);
private UInt64 ReadSteamId(IReadMessage inc)
=> inc.ReadUInt64() ^ ownerKey64;
private void WriteSteamId(IWriteMessage msg, UInt64 val)
=> msg.Write(val ^ ownerKey64);
private UInt64 ownerKey64 => unchecked((UInt64)ownerKey.Fallback(0));
public SteamP2PServerPeer(UInt64 steamId, int ownerKey, ServerSettings settings)
private SteamId ReadSteamId(IReadMessage inc) => new SteamId(inc.ReadUInt64() ^ ownerKey64);
private void WriteSteamId(IWriteMessage msg, SteamId val) => msg.WriteUInt64(val.Value ^ ownerKey64);
public SteamP2PServerPeer(SteamId steamId, int ownerKey, ServerSettings settings, Callbacks callbacks) : base(callbacks)
{
serverSettings = settings;
connectedClients = new List<NetworkConnection>();
pendingClients = new List<PendingClient>();
this.ownerKey = ownerKey;
this.ownerKey = Option<int>.Some(ownerKey);
OwnerSteamID = steamId;
ownerSteamId = steamId;
started = false;
}
public override void Start()
{
IWriteMessage outMsg = new WriteOnlyMessage();
WriteSteamId(outMsg, OwnerSteamID);
outMsg.Write((byte)DeliveryMethod.Reliable);
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep | PacketHeader.IsServerMessage));
byte[] msgToSend = (byte[])outMsg.Buffer.Clone();
Array.Resize(ref msgToSend, outMsg.LengthBytes);
ChildServerRelay.Write(msgToSend);
var headers = new PeerPacketHeaders
{
DeliveryMethod = DeliveryMethod.Reliable,
PacketHeader = PacketHeader.IsConnectionInitializationStep | PacketHeader.IsServerMessage,
Initialization = null
};
SendMsgInternal(ownerSteamId, headers, null);
started = true;
}
public override void Close(string msg = null)
public override void Close()
{
if (!started) { return; }
if (OwnerConnection != null) OwnerConnection.Status = NetworkConnectionStatus.Disconnected;
if (OwnerConnection != null) { OwnerConnection.Status = NetworkConnectionStatus.Disconnected; }
for (int i = pendingClients.Count - 1; i >= 0; i--)
{
RemovePendingClient(pendingClients[i], DisconnectReason.ServerShutdown, msg);
RemovePendingClient(pendingClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
Disconnect(connectedClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
Disconnect(connectedClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
pendingClients.Clear();
@@ -72,27 +63,21 @@ namespace Barotrauma.Networking
ChildServerRelay.ShutDown();
OnShutdown?.Invoke();
callbacks.OnShutdown.Invoke();
}
public override void Update(float deltaTime)
{
if (!started) { return; }
if (OnOwnerDetermined != null && OwnerConnection != null)
{
OnOwnerDetermined?.Invoke(OwnerConnection);
OnOwnerDetermined = null;
}
//backwards for loop so we can remove elements while iterating
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
SteamP2PConnection conn = connectedClients[i] as SteamP2PConnection;
SteamP2PConnection conn = (SteamP2PConnection)connectedClients[i];
conn.Decay(deltaTime);
if (conn.Timeout < 0.0)
{
Disconnect(conn, "Timed out");
Disconnect(conn, PeerDisconnectPacket.WithReason(DisconnectReason.Timeout));
}
}
@@ -109,7 +94,7 @@ namespace Barotrauma.Networking
catch (Exception e)
{
string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("SteamP2PServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
GameAnalyticsManager.AddErrorEventOnce($"SteamP2PServerPeer.Update:ClientReadException{e.TargetSite}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#else
@@ -124,130 +109,132 @@ namespace Barotrauma.Networking
if (i >= pendingClients.Count || pendingClients[i] != pendingClient) { i--; }
}
}
private void HandleDataMessage(IReadMessage inc)
{
if (!started) { return; }
UInt64 senderSteamId = ReadSteamId(inc);
UInt64 ownerSteamId = ReadSteamId(inc);
SteamId senderSteamId = ReadSteamId(inc);
SteamId sentOwnerSteamId = ReadSteamId(inc);
var (deliveryMethod, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
if (packetHeader.IsServerMessage())
{
DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString());
DebugConsole.ThrowError($"Got server message from {senderSteamId}");
return;
}
if (senderSteamId != OwnerSteamID) //sender is remote, handle disconnects and heartbeats
if (senderSteamId != ownerSteamId) //sender is remote, handle disconnects and heartbeats
{
PendingClient pendingClient = pendingClients.Find(c => c.SteamID == senderSteamId);
SteamP2PConnection connectedClient = connectedClients.Find(c => c.SteamID == senderSteamId) as SteamP2PConnection;
bool connectionMatches(NetworkConnection conn) =>
conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var steamId } }
&& steamId == senderSteamId;
PendingClient? pendingClient = pendingClients.Find(c => connectionMatches(c.Connection));
SteamP2PConnection? connectedClient = connectedClients.Find(connectionMatches) as SteamP2PConnection;
pendingClient?.Heartbeat();
connectedClient?.Heartbeat();
string banReason;
if (serverSettings.BanList.IsBanned(senderSteamId, out banReason) ||
serverSettings.BanList.IsBanned(ownerSteamId, out banReason))
if (packetHeader.IsConnectionInitializationStep())
{
if (!initialization.HasValue) { return; }
ConnectionInitialization initializationStep = initialization.Value;
if (pendingClient != null)
{
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
ReadConnectionInitializationStep(
pendingClient,
new ReadWriteMessage(inc.Buffer, inc.BitPosition, inc.LengthBits, false),
initializationStep);
}
else if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClient = new PendingClient(new SteamP2PConnection(senderSteamId));
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
pendingClients.Add(pendingClient);
}
}
else if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason) ||
serverSettings.BanList.IsBanned(sentOwnerSteamId, out banReason))
{
if (pendingClient != null)
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
}
else if (connectedClient != null)
{
Disconnect(connectedClient, DisconnectReason.Banned.ToString() + "/ "+ banReason);
Disconnect(connectedClient, PeerDisconnectPacket.Banned(banReason));
}
return;
}
else if (packetHeader.IsDisconnectMessage())
{
if (pendingClient != null)
{
string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}";
RemovePendingClient(pendingClient, DisconnectReason.Unknown, disconnectMsg);
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
else if (connectedClient != null)
{
string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={connectedClient.Name}";
Disconnect(connectedClient, disconnectMsg, false);
Disconnect(connectedClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
return;
}
else if (packetHeader.IsHeartbeatMessage())
{
//message exists solely as a heartbeat, ignore its contents
return;
}
else if (packetHeader.IsConnectionInitializationStep())
{
if (pendingClient != null)
{
if (ownerSteamId != 0)
{
pendingClient.Connection.SetOwnerSteamIDIfUnknown(ownerSteamId);
}
ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null));
}
else
{
ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClients.Add(new PendingClient(new SteamP2PConnection("PENDING", senderSteamId)) { SteamID = senderSteamId });
}
}
}
else if (connectedClient != null)
{
UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, connectedClient);
OnMessageReceived?.Invoke(connectedClient, msg);
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, connectedClient);
callbacks.OnMessageReceived.Invoke(connectedClient, msg);
}
}
else //sender is owner
{
if (OwnerConnection != null) { (OwnerConnection as SteamP2PConnection).Heartbeat(); }
(OwnerConnection as SteamP2PConnection)?.Heartbeat();
if (packetHeader.IsDisconnectMessage())
{
DebugConsole.ThrowError("Received disconnect message from owner");
return;
}
if (packetHeader.IsServerMessage())
{
DebugConsole.ThrowError("Received server message from owner");
return;
}
if (packetHeader.IsConnectionInitializationStep())
{
if (OwnerConnection == null)
if (OwnerConnection is null)
{
string ownerName = inc.ReadString();
OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID)
var packet = INetSerializableStruct.Read<SteamP2PInitializationOwnerPacket>(inc);
OwnerConnection = new SteamP2PConnection(ownerSteamId)
{
Language = GameSettings.CurrentConfig.Language
};
OwnerConnection.SetOwnerSteamIDIfUnknown(OwnerSteamID);
OwnerConnection.SetAccountInfo(new AccountInfo(ownerSteamId, ownerSteamId));
OnInitializationComplete?.Invoke(OwnerConnection);
callbacks.OnInitializationComplete.Invoke(OwnerConnection, packet.OwnerName);
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
}
return;
}
if (packetHeader.IsHeartbeatMessage())
{
return;
}
else
{
UInt16 length = inc.ReadUInt16();
IReadMessage msg = new ReadOnlyMessage(inc.Buffer, packetHeader.IsCompressed(), inc.BytePosition, length, OwnerConnection);
OnMessageReceived?.Invoke(OwnerConnection, msg);
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, OwnerConnection);
callbacks.OnMessageReceived.Invoke(OwnerConnection!, msg);
}
}
}
@@ -256,90 +243,104 @@ namespace Barotrauma.Networking
{
throw new InvalidOperationException("Called InitializeSteamServerCallbacks on SteamP2PServerPeer!");
}
public override void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true)
{
if (!started) { return; }
if (!(conn is SteamP2PConnection steamp2pConn)) return;
if (!connectedClients.Contains(steamp2pConn) && conn != OwnerConnection)
if (conn is not SteamP2PConnection steamP2PConn) { return; }
if (!connectedClients.Contains(steamP2PConn) && conn != OwnerConnection)
{
DebugConsole.ThrowError("Tried to send message to unauthenticated connection: " + steamp2pConn.SteamID.ToString());
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {steamP2PConn.AccountInfo.AccountId}");
return;
}
IWriteMessage msgToSend = new WriteOnlyMessage();
byte[] msgData = new byte[16];
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
WriteSteamId(msgToSend, conn.SteamID);
msgToSend.Write((byte)deliveryMethod);
msgToSend.Write((byte)((isCompressed ? PacketHeader.IsCompressed : PacketHeader.None) | PacketHeader.IsServerMessage));
msgToSend.Write((UInt16)length);
msgToSend.Write(msgData, 0, length);
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || connAccountId is not SteamId) { return; }
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
ChildServerRelay.Write(bufToSend);
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
var headers = new PeerPacketHeaders
{
DeliveryMethod = deliveryMethod,
PacketHeader = (isCompressed ? PacketHeader.IsCompressed : PacketHeader.None)
| PacketHeader.IsServerMessage,
Initialization = null
};
var body = new PeerPacketMessage
{
Buffer = bufAux
};
SendMsgInternal(steamP2PConn, headers, body);
}
private void SendDisconnectMessage(UInt64 steamId, string msg)
{
if (!started) { return; }
if (string.IsNullOrWhiteSpace(msg)) { return; }
IWriteMessage msgToSend = new WriteOnlyMessage();
WriteSteamId(msgToSend, steamId);
msgToSend.Write((byte)DeliveryMethod.Reliable);
msgToSend.Write((byte)(PacketHeader.IsDisconnectMessage | PacketHeader.IsServerMessage));
msgToSend.Write(msg);
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
ChildServerRelay.Write(bufToSend);
}
private void Disconnect(NetworkConnection conn, string msg, bool sendDisconnectMessage)
private void SendDisconnectMessage(SteamId steamId, PeerDisconnectPacket peerDisconnectPacket)
{
if (!started) { return; }
if (!(conn is SteamP2PConnection steamp2pConn)) { return; }
if (sendDisconnectMessage) { SendDisconnectMessage(steamp2pConn.SteamID, msg); }
var headers = new PeerPacketHeaders
{
DeliveryMethod = DeliveryMethod.Reliable,
PacketHeader = PacketHeader.IsDisconnectMessage | PacketHeader.IsServerMessage,
Initialization = null
};
SendMsgInternal(steamId, headers, peerDisconnectPacket);
}
public override void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket)
{
if (!started) { return; }
if (conn is not SteamP2PConnection steamp2pConn) { return; }
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || connAccountId is not SteamId connSteamId) { return; }
SendDisconnectMessage(connSteamId, peerDisconnectPacket);
if (connectedClients.Contains(steamp2pConn))
{
steamp2pConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(steamp2pConn);
OnDisconnect?.Invoke(conn, msg);
Steam.SteamManager.StopAuthSession(conn.SteamID);
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
Steam.SteamManager.StopAuthSession(connSteamId);
}
else if (steamp2pConn == OwnerConnection)
{
//TODO: fix?
throw new InvalidOperationException("Cannot disconnect owner peer");
}
}
public override void Disconnect(NetworkConnection conn, string msg = null)
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
{
Disconnect(conn, msg, true);
}
var connSteamId = conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var id } } ? id : null;
if (connSteamId is null) { return; }
protected override void SendMsgInternal(NetworkConnection conn, DeliveryMethod deliveryMethod, IWriteMessage msg)
SendMsgInternal(connSteamId, headers, body);
}
private void SendMsgInternal(SteamId connSteamId, PeerPacketHeaders headers, INetSerializableStruct? body)
{
IWriteMessage msgToSend = new WriteOnlyMessage();
WriteSteamId(msgToSend, conn.SteamID);
msgToSend.Write((byte)deliveryMethod);
msgToSend.Write(msg.Buffer, 0, msg.LengthBytes);
byte[] bufToSend = (byte[])msgToSend.Buffer.Clone();
Array.Resize(ref bufToSend, msgToSend.LengthBytes);
WriteSteamId(msgToSend, connSteamId);
msgToSend.WriteNetSerializableStruct(headers);
body?.Write(msgToSend);
ForwardToOwnerProcess(msgToSend);
}
private static void ForwardToOwnerProcess(IWriteMessage msg)
{
byte[] bufToSend = (byte[])msg.Buffer.Clone();
Array.Resize(ref bufToSend, msg.LengthBytes);
ChildServerRelay.Write(bufToSend);
}
protected override void ProcessAuthTicket(string name, int ownKey, ulong steamId, PendingClient pendingClient, byte[] ticket)
protected override void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient)
{
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
pendingClient.Connection.Name = name;
pendingClient.Name = name;
pendingClient.Name = packet.Name;
pendingClient.AuthSessionStarted = true;
}
}
}
}
@@ -8,11 +8,6 @@ namespace Barotrauma.Networking
{
partial class RespawnManager : Entity, IServerSerializable
{
/// <summary>
/// How much skills drop towards the job's default skill levels when respawning midround in the campaign
/// </summary>
const float SkillReductionOnCampaignMidroundRespawn = 0.75f;
private DateTime despawnTime;
private float shuttleEmptyTimer;
@@ -132,7 +127,7 @@ namespace Barotrauma.Networking
return characterToRespawnCount >= GetMinCharactersToRespawn();
}
partial void UpdateWaiting(float deltaTime)
partial void UpdateWaiting(float _)
{
if (RespawnShuttle != null)
{
@@ -465,26 +460,28 @@ namespace Barotrauma.Networking
}
clients[i].Character = character;
character.OwnerClientEndPoint = clients[i].Connection.EndPointString;
character.OwnerClientAddress = clients[i].Connection.Endpoint.Address;
character.OwnerClientName = clients[i].Name;
GameServer.Log(string.Format("Respawning {0} ({1}) as {2}", GameServer.ClientLogName(clients[i]), clients[i].Connection?.EndPointString, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
GameServer.Log(
$"Respawning {GameServer.ClientLogName(clients[i])} ({clients[i].Connection.Endpoint}) as {characterInfos[i].Job.Name}", ServerLog.MessageType.Spawning);
}
if (RespawnShuttle != null)
{
Vector2 pos = cargoSp == null ? character.Position : cargoSp.Position;
List<Item> newRespawnItems = new List<Item>();
Vector2 pos = cargoSp?.Position ?? character.Position;
if (divingSuitPrefab != null)
{
var divingSuit = new Item(divingSuitPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(divingSuit));
respawnItems.Add(divingSuit);
newRespawnItems.Add(divingSuit);
if (oxyPrefab != null && divingSuit.GetComponent<ItemContainer>() != null)
{
var oxyTank = new Item(oxyPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(oxyTank));
divingSuit.Combine(oxyTank, user: null);
respawnItems.Add(oxyTank);
newRespawnItems.Add(oxyTank);
}
}
@@ -494,13 +491,13 @@ namespace Barotrauma.Networking
{
var scooter = new Item(scooterPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(scooter));
respawnItems.Add(scooter);
newRespawnItems.Add(scooter);
if (batteryPrefab != null)
{
var battery = new Item(batteryPrefab, pos, respawnSub);
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(battery));
scooter.Combine(battery, user: null);
respawnItems.Add(battery);
newRespawnItems.Add(battery);
}
}
}
@@ -508,14 +505,31 @@ namespace Barotrauma.Networking
{
AutoItemPlacer.RegenerateLoot(RespawnShuttle, respawnContainer);
}
//try to put the items in containers in the shuttle
foreach (var respawnItem in newRespawnItems)
{
System.Diagnostics.Debug.Assert(!respawnItem.Removed);
foreach (Item shuttleItem in RespawnShuttle.GetItems(alsoFromConnectedSubs: false))
{
if (shuttleItem.NonInteractable || shuttleItem.NonPlayerTeamInteractable) { continue; }
var container = shuttleItem.GetComponent<ItemContainer>();
if (container != null && container.Inventory.TryPutItem(respawnItem, user: null))
{
break;
}
}
respawnItems.Add(respawnItem);
}
}
var characterData = campaign?.GetClientCharacterData(clients[i]);
if (characterData != null && Level.Loaded?.Type != LevelData.LevelType.Outpost && characterData.HasSpawned)
{
//we need to reapply the previous respawn penalty affliction or successive deaths won't make it stack
characterData.ApplyHealthData(character, (AfflictionPrefab ap) => ap == GetRespawnPenaltyAfflictionPrefab());
GiveRespawnPenaltyAffliction(character);
}
if (characterData == null || characterData.HasSpawned)
{
//give the character the items they would've gotten if they had spawned in the main sub
@@ -563,8 +577,8 @@ namespace Barotrauma.Networking
foreach (Skill skill in characterInfo.Job.GetSkills())
{
var skillPrefab = characterInfo.Job.Prefab.Skills.Find(s => skill.Identifier == s.Identifier);
if (skillPrefab == null) { continue; }
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.Start, SkillReductionOnCampaignMidroundRespawn);
if (skillPrefab == null || skill.Level < skillPrefab.LevelRange.End) { continue; }
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.End, SkillReductionOnDeath);
}
}
@@ -575,20 +589,20 @@ namespace Barotrauma.Networking
switch (CurrentState)
{
case State.Transporting:
msg.Write(ReturnCountdownStarted);
msg.Write(GameMain.Server.ServerSettings.MaxTransportTime);
msg.Write((float)(ReturnTime - DateTime.Now).TotalSeconds);
msg.WriteBoolean(ReturnCountdownStarted);
msg.WriteSingle(GameMain.Server.ServerSettings.MaxTransportTime);
msg.WriteSingle((float)(ReturnTime - DateTime.Now).TotalSeconds);
break;
case State.Waiting:
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
var matchingData = campaign?.GetClientCharacterData(c);
bool forceSpawnInMainSub = matchingData != null && !matchingData.HasSpawned;
msg.Write((ushort)pendingRespawnCount);
msg.Write((ushort)requiredRespawnCount);
msg.Write(IsRespawnPromptPendingForClient(c));
msg.Write(RespawnCountdownStarted);
msg.Write(forceSpawnInMainSub);
msg.Write((float)(RespawnTime - DateTime.Now).TotalSeconds);
msg.WriteUInt16((ushort)pendingRespawnCount);
msg.WriteUInt16((ushort)requiredRespawnCount);
msg.WriteBoolean(IsRespawnPromptPendingForClient(c));
msg.WriteBoolean(RespawnCountdownStarted);
msg.WriteBoolean(forceSpawnInMainSub);
msg.WriteSingle((float)(RespawnTime - DateTime.Now).TotalSeconds);
break;
case State.Returning:
break;
@@ -18,29 +18,39 @@ namespace Barotrauma.Networking
{
if (!PropEquals(lastSyncedValue, Value))
{
LastUpdateID = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID);
LastUpdateID = GameMain.NetLobbyScreen.LastUpdateID;
lastSyncedValue = Value;
}
}
public void ForceUpdate()
{
LastUpdateID = GameMain.NetLobbyScreen.LastUpdateID++;
}
}
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml";
public static readonly char SubmarineSeparatorChar = '|';
public readonly Dictionary<NetFlags, UInt16> LastUpdateIdForFlag = new Dictionary<NetFlags, UInt16>();
public UInt16 LastPropertyUpdateId { get; private set; } = 1;
public readonly Dictionary<NetFlags, UInt16> LastUpdateIdForFlag
= ((NetFlags[])Enum.GetValues(typeof(NetFlags)))
.Select(f => (f, (ushort)1))
.ToDictionary();
public void UpdateFlag(NetFlags flag)
=> LastUpdateIdForFlag[flag] = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID + 1);
public NetFlags UnsentFlags()
=> LastUpdateIdForFlag.Keys
.Where(k => NetIdUtils.IdMoreRecent(LastUpdateIdForFlag[k], GameMain.NetLobbyScreen.LastUpdateID))
.Aggregate(NetFlags.None, (f1, f2) => f1 | f2);
private bool IsFlagRequired(Client c, NetFlags flag)
=> NetIdUtils.IdMoreRecent(LastUpdateIdForFlag[flag], c.LastRecvLobbyUpdate);
public NetFlags GetRequiredFlags(Client c)
=> LastUpdateIdForFlag.Keys
.Where(k => IsFlagRequired(c, k))
.Concat(NetFlags.None.ToEnumerable()) //prevents InvalidOperationException in Aggregate
.Aggregate((f1, f2) => f1 | f2);
.Aggregate(NetFlags.None, (f1, f2) => f1 | f2);
partial void InitProjSpecific()
{
@@ -48,6 +58,15 @@ namespace Barotrauma.Networking
LoadClientPermissions();
}
public void ForcePropertyUpdate()
{
UpdateFlag(NetFlags.Properties);
foreach (NetPropertyData property in netProperties.Values)
{
property.ForceUpdate();
}
}
private void WriteNetProperties(IWriteMessage outMsg, Client c)
{
foreach (UInt32 key in netProperties.Keys)
@@ -56,40 +75,39 @@ namespace Barotrauma.Networking
property.SyncValue();
if (NetIdUtils.IdMoreRecent(property.LastUpdateID, c.LastRecvLobbyUpdate))
{
outMsg.Write(key);
outMsg.WriteUInt32(key);
netProperties[key].Write(outMsg);
}
}
outMsg.Write((UInt32)0);
outMsg.WriteUInt32((UInt32)0);
}
public void ServerAdminWrite(IWriteMessage outMsg, Client c)
{
c.LastSentServerSettingsUpdate = LastPropertyUpdateId;
c.LastSentServerSettingsUpdate = LastUpdateIdForFlag[NetFlags.Properties];
WriteNetProperties(outMsg, c);
WriteMonsterEnabled(outMsg);
BanList.ServerAdminWrite(outMsg, c);
Whitelist.ServerAdminWrite(outMsg, c);
}
public void ServerWrite(IWriteMessage outMsg, Client c)
{
NetFlags requiredFlags = GetRequiredFlags(c);
outMsg.Write((byte)requiredFlags);
outMsg.WriteByte((byte)requiredFlags);
if (requiredFlags.HasFlag(NetFlags.Name))
{
outMsg.Write(ServerName);
outMsg.WriteString(ServerName);
}
if (requiredFlags.HasFlag(NetFlags.Message))
{
outMsg.Write(ServerMessageText);
outMsg.WriteString(ServerMessageText);
}
outMsg.Write((byte)PlayStyle);
outMsg.Write((byte)MaxPlayers);
outMsg.Write(HasPassword);
outMsg.Write(IsPublic);
outMsg.Write(AllowFileTransfers);
outMsg.WriteByte((byte)PlayStyle);
outMsg.WriteByte((byte)MaxPlayers);
outMsg.WriteBoolean(HasPassword);
outMsg.WriteBoolean(IsPublic);
outMsg.WriteBoolean(AllowFileTransfers);
outMsg.WritePadBits();
outMsg.WriteRangedInteger(TickRate, 1, 60);
@@ -104,16 +122,18 @@ namespace Barotrauma.Networking
}
if (c.HasPermission(Networking.ClientPermissions.ManageSettings)
&& !NetIdUtils.IdMoreRecentOrMatches(c.LastRecvServerSettingsUpdate, LastPropertyUpdateId))
&& NetIdUtils.IdMoreRecent(
newID: LastUpdateIdForFlag[NetFlags.Properties],
oldID: c.LastRecvServerSettingsUpdate))
{
outMsg.Write(true);
outMsg.WriteBoolean(true);
outMsg.WritePadBits();
ServerAdminWrite(outMsg, c);
}
else
{
outMsg.Write(false);
outMsg.WriteBoolean(false);
outMsg.WritePadBits();
}
}
@@ -171,12 +191,10 @@ namespace Barotrauma.Networking
propertiesChanged |= changedMonsterSettings;
if (changedMonsterSettings) { ReadMonsterEnabled(incMsg); }
propertiesChanged |= BanList.ServerAdminRead(incMsg, c);
propertiesChanged |= Whitelist.ServerAdminRead(incMsg, c);
if (propertiesChanged)
{
UpdateFlag(NetFlags.Properties);
LastPropertyUpdateId = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID + 1);
}
changed |= propertiesChanged;
}
@@ -192,27 +210,32 @@ namespace Barotrauma.Networking
{
int orBits = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
int andBits = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
GameMain.NetLobbyScreen.MissionType = (Barotrauma.MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);
GameMain.NetLobbyScreen.MissionType = (MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);
int traitorSetting = (int)TraitorsEnabled + incMsg.ReadByte() - 1;
if (traitorSetting < 0) traitorSetting = 2;
if (traitorSetting > 2) traitorSetting = 0;
if (traitorSetting < 0) { traitorSetting = 2; }
if (traitorSetting > 2) { traitorSetting = 0; }
TraitorsEnabled = (YesNoMaybe)traitorSetting;
int botCount = BotCount + incMsg.ReadByte() - 1;
if (botCount < 0) botCount = MaxBotCount;
if (botCount > MaxBotCount) botCount = 0;
if (botCount < 0) { botCount = MaxBotCount; }
if (botCount > MaxBotCount) { botCount = 0; }
BotCount = botCount;
int botSpawnMode = (int)BotSpawnMode + incMsg.ReadByte() - 1;
if (botSpawnMode < 0) botSpawnMode = 1;
if (botSpawnMode > 1) botSpawnMode = 0;
if (botSpawnMode < 0) { botSpawnMode = 1; }
if (botSpawnMode > 1) { botSpawnMode = 0; }
BotSpawnMode = (BotSpawnMode)botSpawnMode;
float levelDifficulty = incMsg.ReadSingle();
if (levelDifficulty >= 0.0f) SelectedLevelDifficulty = levelDifficulty;
if (levelDifficulty >= 0.0f) { SelectedLevelDifficulty = levelDifficulty; }
UseRespawnShuttle = incMsg.ReadBoolean();
bool changedUseRespawnShuttle = incMsg.ReadBoolean();
bool useRespawnShuttle = incMsg.ReadBoolean();
if (changedUseRespawnShuttle)
{
UseRespawnShuttle = useRespawnShuttle;
}
bool changedAutoRestart = incMsg.ReadBoolean();
bool autoRestart = incMsg.ReadBoolean();
@@ -444,31 +467,27 @@ namespace Barotrauma.Networking
{
ClientPermissions.Clear();
if (!File.Exists(ClientPermissionsFile))
{
if (File.Exists("Data/clientpermissions.txt"))
{
LoadClientPermissionsOld("Data/clientpermissions.txt");
}
return;
}
if (!File.Exists(ClientPermissionsFile)) { return; }
XDocument doc = XMLExtensions.TryLoadXml(ClientPermissionsFile);
if (doc == null) { return; }
foreach (XElement clientElement in doc.Root.Elements())
{
string clientName = clientElement.GetAttributeString("name", "");
string clientEndPoint = clientElement.GetAttributeString("endpoint", null) ?? clientElement.GetAttributeString("ip", "");
string steamIdStr = clientElement.GetAttributeString("steamid", "");
string addressStr = clientElement.GetAttributeString("address", null)
?? clientElement.GetAttributeString("endpoint", null)
?? clientElement.GetAttributeString("ip", "");
string accountIdStr = clientElement.GetAttributeString("accountid", null)
?? clientElement.GetAttributeString("steamid", "");
if (string.IsNullOrWhiteSpace(clientName))
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have a name and an IP address.");
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have a name.");
continue;
}
if (string.IsNullOrWhiteSpace(clientEndPoint) && string.IsNullOrWhiteSpace(steamIdStr))
if (string.IsNullOrWhiteSpace(addressStr) && string.IsNullOrWhiteSpace(accountIdStr))
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have an IP address or a Steam ID.");
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - all clients must have an endpoint or a Steam ID.");
continue;
}
@@ -542,69 +561,33 @@ namespace Barotrauma.Networking
}
}
if (!string.IsNullOrEmpty(steamIdStr))
if (!string.IsNullOrEmpty(accountIdStr))
{
if (ulong.TryParse(steamIdStr, out ulong steamID))
if (AccountId.Parse(accountIdStr).TryUnwrap(out var accountId))
{
ClientPermissions.Add(new SavedClientPermission(clientName, steamID, permissions, permittedCommands));
ClientPermissions.Add(new SavedClientPermission(clientName, accountId, permissions, permittedCommands));
}
else
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + steamIdStr + "\" is not a valid Steam ID.");
continue;
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + accountIdStr + "\" is not a valid account ID.");
}
}
else
{
ClientPermissions.Add(new SavedClientPermission(clientName, clientEndPoint, permissions, permittedCommands));
}
}
}
/// <summary>
/// Method for loading old .txt client permission files to provide backwards compatibility
/// </summary>
private void LoadClientPermissionsOld(string file)
{
if (!File.Exists(file)) return;
string[] lines;
try
{
lines = File.ReadAllLines(file);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to open client permission file " + ClientPermissionsFile, e);
return;
}
ClientPermissions.Clear();
foreach (string line in lines)
{
string[] separatedLine = line.Split('|');
if (separatedLine.Length < 3) { continue; }
string name = string.Join("|", separatedLine.Take(separatedLine.Length - 2));
string ip = separatedLine[separatedLine.Length - 2];
ClientPermissions permissions;
if (Enum.TryParse(separatedLine.Last(), out permissions))
{
ClientPermissions.Add(new SavedClientPermission(name, ip, permissions, new HashSet<DebugConsole.Command>()));
if (Address.Parse(addressStr).TryUnwrap(out var address))
{
ClientPermissions.Add(new SavedClientPermission(clientName, address, permissions, permittedCommands));
}
else
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + addressStr + "\" is not a valid endpoint.");
}
}
}
}
public void SaveClientPermissions()
{
//delete old client permission file
if (File.Exists("Data/clientpermissions.txt"))
{
File.Delete("Data/clientpermissions.txt");
}
GameServer.Log("Saving client permissions", ServerLog.MessageType.ServerMessage);
XDocument doc = new XDocument(new XElement("ClientPermissions"));
@@ -612,6 +595,7 @@ namespace Barotrauma.Networking
foreach (SavedClientPermission clientPermission in ClientPermissions)
{
var matchingPreset = PermissionPreset.List.Find(p => p.MatchesPermissions(clientPermission.Permissions, clientPermission.PermittedCommands));
#warning TODO: this is broken because of localization
if (matchingPreset != null && matchingPreset.Name == "None")
{
continue;
@@ -620,23 +604,14 @@ namespace Barotrauma.Networking
XElement clientElement = new XElement("Client",
new XAttribute("name", clientPermission.Name));
if (clientPermission.SteamID > 0)
{
clientElement.Add(new XAttribute("steamid", clientPermission.SteamID));
}
else
{
clientElement.Add(new XAttribute("endpoint", clientPermission.EndPoint));
}
clientElement.Add(clientPermission.AddressOrAccountId.TryGet(out AccountId accountId)
? new XAttribute("accountid", accountId.StringRepresentation)
: new XAttribute("address", ((Address)clientPermission.AddressOrAccountId).StringRepresentation));
if (matchingPreset == null)
{
clientElement.Add(new XAttribute("permissions", clientPermission.Permissions.ToString()));
}
else
{
clientElement.Add(new XAttribute("preset", matchingPreset.Name));
}
clientElement.Add(matchingPreset == null
? new XAttribute("permissions", clientPermission.Permissions.ToString())
: new XAttribute("preset", matchingPreset.Name));
if (clientPermission.Permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands))
{
foreach (DebugConsole.Command command in clientPermission.PermittedCommands)
@@ -56,8 +56,8 @@ namespace Barotrauma.Networking
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.VOICE);
msg.Write((byte)queue.QueueID);
msg.WriteByte((byte)ServerPacketHeader.VOICE);
msg.WriteByte((byte)queue.QueueID);
queue.Write(msg);
netServer.Send(msg, recipient.Connection, DeliveryMethod.Unreliable);
@@ -216,6 +216,14 @@ namespace Barotrauma
}
}
public void ResetVotes(IEnumerable<Client> connectedClients, bool resetKickVotes)
{
foreach (Client client in connectedClients)
{
client.ResetVotes(resetKickVotes);
}
}
public void ServerRead(IReadMessage inc, Client sender)
{
if (GameMain.Server == null || sender == null) { return; }
@@ -254,15 +262,20 @@ namespace Barotrauma
break;
case VoteType.Kick:
byte kickedClientID = inc.ReadByte();
Client kicked = GameMain.Server.ConnectedClients.Find(c => c.ID == kickedClientID);
if (kicked != null && kicked.Connection != GameMain.Server.OwnerConnection && !kicked.HasKickVoteFrom(sender))
if ((DateTime.Now - sender.JoinTime).TotalSeconds < GameMain.Server.ServerSettings.DisallowKickVoteTime)
{
kicked.AddKickVote(sender);
Client.UpdateKickVotes(GameMain.Server.ConnectedClients);
GameMain.Server.SendChatMessage($"ServerMessage.HasVotedToKick~[initiator]={sender.Name}~[target]={kicked.Name}", ChatMessageType.Server, null);
GameMain.Server.SendDirectChatMessage($"ServerMessage.kickvotedisallowed", sender);
}
else
{
Client kicked = GameMain.Server.ConnectedClients.Find(c => c.SessionId == kickedClientID);
if (kicked != null && kicked.Connection != GameMain.Server.OwnerConnection && !kicked.HasKickVoteFrom(sender))
{
kicked.AddKickVote(sender);
Client.UpdateKickVotes(GameMain.Server.ConnectedClients);
GameMain.Server.SendChatMessage($"ServerMessage.HasVotedToKick~[initiator]={sender.Name}~[target]={kicked.Name}", ChatMessageType.Server, null);
}
}
break;
case VoteType.StartRound:
bool ready = inc.ReadBoolean();
@@ -287,9 +300,9 @@ namespace Barotrauma
if (!ShouldRejectVote(sender, voteType))
{
pendingVotes.Enqueue(new TransferVote(sender,
GameMain.Server.ConnectedClients.Find(c => c.ID == fromClientId),
GameMain.Server.ConnectedClients.Find(c => c.SessionId == fromClientId),
amount,
GameMain.Server.ConnectedClients.Find(c => c.ID == toClientId)));
GameMain.Server.ConnectedClients.Find(c => c.SessionId == toClientId)));
}
}
else
@@ -323,66 +336,66 @@ namespace Barotrauma
{
if (GameMain.Server == null) { return; }
msg.Write(GameMain.Server.ServerSettings.AllowSubVoting);
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowSubVoting);
if (GameMain.Server.ServerSettings.AllowSubVoting)
{
IReadOnlyDictionary<SubmarineInfo, int> voteList = GetVoteCounts<SubmarineInfo>(VoteType.Sub, GameMain.Server.ConnectedClients);
msg.Write((byte)voteList.Count);
msg.WriteByte((byte)voteList.Count);
foreach (KeyValuePair<SubmarineInfo, int> vote in voteList)
{
msg.Write((byte)vote.Value);
msg.Write(vote.Key.Name);
msg.WriteByte((byte)vote.Value);
msg.WriteString(vote.Key.Name);
}
}
msg.Write(GameMain.Server.ServerSettings.AllowModeVoting);
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowModeVoting);
if (GameMain.Server.ServerSettings.AllowModeVoting)
{
IReadOnlyDictionary<GameModePreset, int> voteList = GetVoteCounts<GameModePreset>(VoteType.Mode, GameMain.Server.ConnectedClients);
msg.Write((byte)voteList.Count);
msg.WriteByte((byte)voteList.Count);
foreach (KeyValuePair<GameModePreset, int> vote in voteList)
{
msg.Write((byte)vote.Value);
msg.Write(vote.Key.Identifier);
msg.WriteByte((byte)vote.Value);
msg.WriteIdentifier(vote.Key.Identifier);
}
}
msg.Write(GameMain.Server.ServerSettings.AllowEndVoting);
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowEndVoting);
if (GameMain.Server.ServerSettings.AllowEndVoting)
{
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound)));
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned));
msg.WriteByte((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound)));
msg.WriteByte((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned));
}
msg.Write(GameMain.Server.ServerSettings.AllowVoteKick);
msg.WriteBoolean(GameMain.Server.ServerSettings.AllowVoteKick);
msg.Write((byte)(ActiveVote?.State ?? VoteState.None));
msg.WriteByte((byte)(ActiveVote?.State ?? VoteState.None));
if (ActiveVote != null)
{
msg.Write((byte)ActiveVote.VoteType);
msg.WriteByte((byte)ActiveVote.VoteType);
if (ActiveVote.State != VoteState.None && ActiveVote.VoteType != VoteType.Unknown)
{
var eligibleClients = GameMain.Server.ConnectedClients.Where(c => c.InGame && c != ActiveVote.VoteStarter);
var yesClients = eligibleClients.Where(c => c.GetVote<int>(ActiveVote.VoteType) == 2);
msg.Write((byte)yesClients.Count());
msg.WriteByte((byte)yesClients.Count());
foreach (Client c in yesClients)
{
msg.Write(c.ID);
msg.WriteByte(c.SessionId);
}
var noClients = eligibleClients.Where(c => c.GetVote<int>(ActiveVote.VoteType) == 1);
msg.Write((byte)noClients.Count());
msg.WriteByte((byte)noClients.Count());
foreach (Client c in noClients)
{
msg.Write(c.ID);
msg.WriteByte(c.SessionId);
}
msg.Write((byte)eligibleClients.Count());
msg.WriteByte((byte)eligibleClients.Count());
switch (ActiveVote.State)
{
case VoteState.Started:
msg.Write(ActiveVote.VoteStarter.ID);
msg.Write((byte)GameMain.Server.ServerSettings.VoteTimeout);
msg.WriteByte(ActiveVote.VoteStarter.SessionId);
msg.WriteByte((byte)GameMain.Server.ServerSettings.VoteTimeout);
switch (ActiveVote.VoteType)
{
@@ -390,14 +403,14 @@ namespace Barotrauma
case VoteType.PurchaseAndSwitchSub:
case VoteType.SwitchSub:
SubmarineVote vote = ActiveVote as SubmarineVote;
msg.Write(vote.Sub.Name);
msg.Write(vote.TransferItems);
msg.WriteString(vote.Sub.Name);
msg.WriteBoolean(vote.TransferItems);
break;
case VoteType.TransferMoney:
var transferVote = (ActiveVote as TransferVote);
msg.Write(transferVote.From?.ID ?? 0);
msg.Write(transferVote.To?.ID ?? 0);
msg.Write(transferVote.TransferAmount);
msg.WriteByte(transferVote.From?.SessionId ?? 0);
msg.WriteByte(transferVote.To?.SessionId ?? 0);
msg.WriteInt32(transferVote.TransferAmount);
break;
}
@@ -407,16 +420,16 @@ namespace Barotrauma
break;
case VoteState.Passed:
case VoteState.Failed:
msg.Write(ActiveVote.State == VoteState.Passed);
msg.WriteBoolean(ActiveVote.State == VoteState.Passed);
switch (ActiveVote.VoteType)
{
case VoteType.PurchaseSub:
case VoteType.PurchaseAndSwitchSub:
case VoteType.SwitchSub:
var subVote = ActiveVote as SubmarineVote;
msg.Write(subVote.Sub.Name);
msg.Write(subVote.TransferItems);
msg.Write((short)subVote.DeliveryFee);
msg.WriteString(subVote.Sub.Name);
msg.WriteBoolean(subVote.TransferItems);
msg.WriteInt16((short)subVote.DeliveryFee);
break;
}
break;
@@ -424,11 +437,11 @@ namespace Barotrauma
}
}
var readyClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote<bool>(VoteType.StartRound));
msg.Write((byte)readyClients.Count);
var readyClients = GameMain.Server.ConnectedClients.Where(c => c.GetVote<bool>(VoteType.StartRound));
msg.WriteByte((byte)readyClients.Count());
foreach (Client c in readyClients)
{
msg.Write(c.ID);
msg.WriteByte(c.SessionId);
}
msg.WritePadBits();
@@ -1,207 +0,0 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Net;
namespace Barotrauma.Networking
{
partial class WhiteListedPlayer
{
private static UInt16 LastIdentifier = 0;
public WhiteListedPlayer(string name,string ip)
{
Name = name;
IP = ip;
UniqueIdentifier = LastIdentifier; LastIdentifier++;
}
}
partial class WhiteList
{
partial void InitProjSpecific()
{
if (!File.Exists(SavePath)) { return; }
string[] lines;
try
{
lines = File.ReadAllLines(SavePath);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to open whitelist in " + SavePath, e);
return;
}
foreach (string line in lines)
{
if (line[0] == '#')
{
string lineval = line.Substring(1, line.Length - 1);
Int32.TryParse(lineval, out int intVal);
if (lineval.ToLower() == "true" || intVal != 0)
{
Enabled = true;
}
else
{
Enabled = false;
}
}
else
{
string[] separatedLine = line.Split(',');
if (separatedLine.Length < 2) continue;
string name = string.Join(",", separatedLine.Take(separatedLine.Length - 1));
string ip = separatedLine.Last();
whitelistedPlayers.Add(new WhiteListedPlayer(name, ip));
}
}
}
public void Save()
{
GameServer.Log("Saving whitelist", ServerLog.MessageType.ServerMessage);
GameMain.Server?.ServerSettings?.UpdateFlag(ServerSettings.NetFlags.Properties);
List<string> lines = new List<string>();
if (Enabled)
{
lines.Add("#true");
}
else
{
lines.Add("#false");
}
foreach (WhiteListedPlayer wlp in whitelistedPlayers)
{
lines.Add(wlp.Name + "," + wlp.IP);
}
try
{
File.WriteAllLines(SavePath, lines);
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving the whitelist to " + SavePath + " failed", e);
}
}
public bool IsWhiteListed(string name, IPAddress address)
{
if (!Enabled) return true;
WhiteListedPlayer wlp = whitelistedPlayers.Find(p => p.Name == name);
if (wlp == null) return false;
if (!string.IsNullOrWhiteSpace(wlp.IP))
{
if (address.IsIPv4MappedToIPv6 && wlp.IP == address.MapToIPv4NoThrow().ToString())
{
return true;
}
else
{
return wlp.IP == address.ToString();
}
}
return true;
}
private void RemoveFromWhiteList(WhiteListedPlayer wlp)
{
GameServer.Log("Removing " + wlp.Name + " from whitelist", ServerLog.MessageType.ServerMessage);
whitelistedPlayers.Remove(wlp);
}
private void AddToWhiteList(string name, string ip)
{
if (string.IsNullOrWhiteSpace(name)) return;
if (whitelistedPlayers.Any(x => x.Name.ToLower() == name.ToLower() && x.IP == ip)) return;
whitelistedPlayers.Add(new WhiteListedPlayer(name, ip));
}
public void ServerAdminWrite(IWriteMessage outMsg, Client c)
{
if (!c.HasPermission(ClientPermissions.ManageSettings))
{
outMsg.Write(false); outMsg.WritePadBits();
return;
}
outMsg.Write(true);
outMsg.Write(c.Connection == GameMain.Server.OwnerConnection);
outMsg.Write(Enabled);
outMsg.WritePadBits();
outMsg.WriteVariableUInt32((UInt32)whitelistedPlayers.Count);
for (int i = 0; i < whitelistedPlayers.Count; i++)
{
WhiteListedPlayer whitelistedPlayer = whitelistedPlayers[i];
outMsg.Write(whitelistedPlayer.Name);
outMsg.Write(whitelistedPlayer.UniqueIdentifier);
if (c.Connection == GameMain.Server.OwnerConnection)
{
outMsg.Write(whitelistedPlayer.IP);
//outMsg.Write(whitelistedPlayer.SteamID); //TODO: add steamid to whitelisted players
}
}
}
public bool ServerAdminRead(IReadMessage incMsg, Client c)
{
if (!c.HasPermission(ClientPermissions.ManageSettings))
{
bool enabled = incMsg.ReadBoolean(); incMsg.ReadPadBits();
UInt16 removeCount = incMsg.ReadUInt16();
incMsg.BitPosition += removeCount * 4 * 8;
UInt16 addCount = incMsg.ReadUInt16();
for (int i = 0; i < addCount; i++)
{
incMsg.ReadString(); //skip name
incMsg.ReadString(); //skip ip
}
return false;
}
else
{
bool prevEnabled = Enabled;
bool enabled = incMsg.ReadBoolean(); incMsg.ReadPadBits();
Enabled = enabled;
UInt16 removeCount = incMsg.ReadUInt16();
for (int i = 0; i < removeCount; i++)
{
UInt16 id = incMsg.ReadUInt16();
WhiteListedPlayer whitelistedPlayer = whitelistedPlayers.Find(p => p.UniqueIdentifier == id);
if (whitelistedPlayer != null)
{
GameServer.Log(GameServer.ClientLogName(c) + " removed " + whitelistedPlayer.Name + " from whitelist (" + whitelistedPlayer.IP + ")", ServerLog.MessageType.ConsoleUsage);
RemoveFromWhiteList(whitelistedPlayer);
}
}
UInt16 addCount = incMsg.ReadUInt16();
for (int i = 0; i < addCount; i++)
{
string name = incMsg.ReadString();
string ip = incMsg.ReadString();
GameServer.Log(GameServer.ClientLogName(c) + " added " + name + " to whitelist (" + ip + ")", ServerLog.MessageType.ConsoleUsage);
AddToWhiteList(name, ip);
}
bool changed = removeCount > 0 || addCount > 0 || prevEnabled != enabled;
if (changed) { Save(); }
return changed;
}
}
}
}
@@ -11,8 +11,8 @@ namespace Barotrauma
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
msg.Write(SimPosition.X);
msg.Write(SimPosition.Y);
msg.WriteSingle(SimPosition.X);
msg.WriteSingle(SimPosition.Y);
#if DEBUG
if (Math.Abs(FarseerBody.LinearVelocity.X) > MaxVel ||
@@ -22,8 +22,8 @@ namespace Barotrauma
}
#endif
msg.Write(FarseerBody.Awake);
msg.Write(FarseerBody.FixedRotation);
msg.WriteBoolean(FarseerBody.Awake);
msg.WriteBoolean(FarseerBody.FixedRotation);
if (!FarseerBody.FixedRotation)
{
@@ -55,6 +55,10 @@ namespace Barotrauma
#if LINUX
setLinuxEnv();
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
GameMain.ShouldRun = false;
};
#endif
Console.WriteLine("Barotrauma Dedicated Server " + GameMain.Version +
" (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")");
@@ -102,7 +106,7 @@ namespace Barotrauma
private static void CrashHandler(object sender, UnhandledExceptionEventArgs args)
{
void swallowExceptions(Action action)
static void swallowExceptions(Action action)
{
try
{
@@ -197,7 +197,7 @@ namespace Barotrauma
public override void Select()
{
base.Select();
GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients);
GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients, resetKickVotes: false);
if (SelectedMode != GameModePreset.MultiPlayerCampaign && GameMain.GameSession?.GameMode is CampaignMode && Selected == this)
{
GameMain.GameSession = null;
@@ -1,4 +1,5 @@
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma.Steam
{
@@ -53,13 +54,13 @@ namespace Barotrauma.Steam
Steamworks.SteamServer.Passworded = server.ServerSettings.HasPassword;
Steamworks.SteamServer.MapName = GameMain.NetLobbyScreen?.SelectedSub?.DisplayName?.Value ?? "";
Steamworks.SteamServer.SetKey("haspassword", server.ServerSettings.HasPassword.ToString());
Steamworks.SteamServer.SetKey("message", GameMain.Server.ServerSettings.ServerMessageText);
Steamworks.SteamServer.SetKey("message", server.ServerSettings.ServerMessageText);
Steamworks.SteamServer.SetKey("version", GameMain.Version.ToString());
Steamworks.SteamServer.SetKey("playercount", GameMain.Server.ConnectedClients.Count.ToString());
Steamworks.SteamServer.SetKey("playercount", server.ConnectedClients.Count.ToString());
Steamworks.SteamServer.SetKey("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
Steamworks.SteamServer.SetKey("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.Hash.StringRepresentation)));
Steamworks.SteamServer.SetKey("contentpackageid", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopId)));
Steamworks.SteamServer.SetKey("usingwhitelist", (server.ServerSettings.Whitelist != null && server.ServerSettings.Whitelist.Enabled).ToString());
Steamworks.SteamServer.SetKey("contentpackageid", string.Join(",", contentPackages.Select(cp
=> cp.UgcId.TryUnwrap(out var ugcId) ? ugcId.StringRepresentation : "")));
Steamworks.SteamServer.SetKey("modeselectionmode", server.ServerSettings.ModeSelectionMode.ToString());
Steamworks.SteamServer.SetKey("subselectionmode", server.ServerSettings.SubSelectionMode.ToString());
Steamworks.SteamServer.SetKey("voicechatenabled", server.ServerSettings.VoiceChatEnabled.ToString());
@@ -77,12 +78,12 @@ namespace Barotrauma.Steam
return true;
}
public static Steamworks.BeginAuthResult StartAuthSession(byte[] authTicketData, ulong clientSteamID)
public static Steamworks.BeginAuthResult StartAuthSession(byte[] authTicketData, SteamId clientSteamID)
{
if (!IsInitialized || !Steamworks.SteamServer.IsValid) return Steamworks.BeginAuthResult.ServerNotConnectedToSteam;
DebugConsole.Log("SteamManager authenticating Steam client " + clientSteamID);
Steamworks.BeginAuthResult startResult = Steamworks.SteamServer.BeginAuthSession(authTicketData, clientSteamID);
Steamworks.BeginAuthResult startResult = Steamworks.SteamServer.BeginAuthSession(authTicketData, clientSteamID.Value);
if (startResult != Steamworks.BeginAuthResult.OK)
{
DebugConsole.Log("Authentication failed: failed to start auth session (" + startResult.ToString() + ")");
@@ -91,12 +92,12 @@ namespace Barotrauma.Steam
return startResult;
}
public static void StopAuthSession(ulong clientSteamID)
public static void StopAuthSession(SteamId clientSteamId)
{
if (!IsInitialized || !Steamworks.SteamServer.IsValid) return;
DebugConsole.Log("SteamManager ending auth session with Steam client " + clientSteamID);
Steamworks.SteamServer.EndSession(clientSteamID);
DebugConsole.Log("SteamManager ending auth session with Steam client " + clientSteamId);
Steamworks.SteamServer.EndSession(clientSteamId.Value);
}
public static bool CloseServer()
@@ -28,13 +28,13 @@ namespace Barotrauma
public void ServerWrite(IWriteMessage msg)
{
msg.Write(MissionIdentifier);
msg.Write(EndMessage);
msg.Write(Success);
msg.Write((byte)Characters.Count);
msg.WriteIdentifier(MissionIdentifier);
msg.WriteString(EndMessage);
msg.WriteBoolean(Success);
msg.WriteByte((byte)Characters.Count);
foreach (Character character in Characters)
{
msg.Write(character.ID);
msg.WriteUInt16(character.ID);
}
}
}