Unstable 0.17.0.0

This commit is contained in:
Markus Isberg
2022-02-26 02:43:01 +09:00
parent a83f375681
commit 3974067915
913 changed files with 32472 additions and 32364 deletions
@@ -7,8 +7,6 @@ namespace Barotrauma
{
public static Character Controlled = null;
partial void InitProjSpecific(XElement mainElement) { }
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun)
{
GameMain.Server.KarmaManager.OnCharacterHealthChanged(this, attacker, attackResult.Damage, stun, attackResult.Afflictions);
@@ -20,7 +18,7 @@ namespace Barotrauma
{
if (causeOfDeath == CauseOfDeathType.Affliction)
{
GameServer.Log(GameServer.CharacterLogName(this) + " has died (Cause of death: " + causeOfDeathAffliction.Prefab.Name + ")", ServerLog.MessageType.Attack);
GameServer.Log(GameServer.CharacterLogName(this) + " has died (Cause of death: " + causeOfDeathAffliction.Prefab.Name.Value + ")", ServerLog.MessageType.Attack);
}
else
{
@@ -8,9 +8,9 @@ namespace Barotrauma
{
partial class CharacterInfo
{
private readonly Dictionary<string, float> prevSentSkill = new Dictionary<string, float>();
private readonly Dictionary<Identifier, float> prevSentSkill = new Dictionary<Identifier, float>();
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel)
partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel)
{
if (Character == null || Character.Removed) { return; }
if (!prevSentSkill.ContainsKey(skillIdentifier))
@@ -45,16 +45,18 @@ namespace Barotrauma
msg.Write(ID);
msg.Write(Name);
msg.Write(OriginalName);
msg.Write((byte)Gender);
msg.Write((byte)Race);
msg.Write((byte)HeadSpriteId);
msg.Write((byte)HairIndex);
msg.Write((byte)BeardIndex);
msg.Write((byte)MoustacheIndex);
msg.Write((byte)FaceAttachmentIndex);
msg.WriteColorR8G8B8(SkinColor);
msg.WriteColorR8G8B8(HairColor);
msg.WriteColorR8G8B8(FacialHairColor);
msg.Write((byte)Head.Preset.TagSet.Count);
foreach (Identifier tag in Head.Preset.TagSet)
{
msg.Write(tag);
}
msg.Write((byte)Head.HairIndex);
msg.Write((byte)Head.BeardIndex);
msg.Write((byte)Head.MoustacheIndex);
msg.Write((byte)Head.FaceAttachmentIndex);
msg.WriteColorR8G8B8(Head.SkinColor);
msg.WriteColorR8G8B8(Head.HairColor);
msg.WriteColorR8G8B8(Head.FacialHairColor);
msg.Write(ragdollFileName);
if (Job != null)
@@ -73,20 +75,9 @@ namespace Barotrauma
msg.Write("");
msg.Write((byte)0);
}
// TODO: animations
msg.Write((byte)SavedStatValues.SelectMany(s => s.Value).Count());
foreach (var savedStatValuePair in SavedStatValues)
{
foreach (var savedStatValue in savedStatValuePair.Value)
{
msg.Write((byte)savedStatValuePair.Key);
msg.Write(savedStatValue.StatIdentifier);
msg.Write(savedStatValue.StatValue);
msg.Write(savedStatValue.RemoveOnDeath);
}
}
msg.Write((ushort)ExperiencePoints);
msg.Write((ushort)AdditionalTalentPoints);
msg.WriteRangedInteger(AdditionalTalentPoints, 0, MaxAdditionalTalentPoints);
}
}
}
@@ -283,12 +283,12 @@ namespace Barotrauma
// get the full list of talents from the player, only give the ones
// that are not already given (or otherwise not viable)
ushort talentCount = msg.ReadUInt16();
List<string> talentSelection = new List<string>();
List<Identifier> talentSelection = new List<Identifier>();
for (int i = 0; i < talentCount; i++)
{
UInt32 talentIdentifier = msg.ReadUInt32();
var prefab = TalentPrefab.TalentPrefabs.Find(p => p.UIntIdentifier == talentIdentifier);
if (prefab == null) { continue; }
var prefab = TalentPrefab.TalentPrefabs.Find(p => p.UintIdentifier == talentIdentifier);
if (prefab == null) { continue; }
if (TalentTree.IsViableTalentForCharacter(this, prefab.Identifier, talentSelection))
{
@@ -381,28 +381,27 @@ namespace Barotrauma
if (type == 1)
{
var currentOrderInfo = controller.ObjectiveManager.GetCurrentOrderInfo();
bool validOrder = currentOrderInfo.HasValue;
bool validOrder = currentOrderInfo != null;
msg.Write(validOrder);
if (!validOrder) { break; }
var orderPrefab = currentOrderInfo.Value.Order.Prefab;
int orderIndex = Order.PrefabList.IndexOf(orderPrefab);
msg.WriteRangedInteger(orderIndex, 0, Order.PrefabList.Count);
var orderPrefab = currentOrderInfo.Prefab;
msg.Write(orderPrefab.UintIdentifier);
if (!orderPrefab.HasOptions) { break; }
int optionIndex = orderPrefab.AllOptions.IndexOf(currentOrderInfo.Value.OrderOption);
int optionIndex = orderPrefab.AllOptions.IndexOf(currentOrderInfo.Option);
if (optionIndex == -1)
{
DebugConsole.AddWarning($"Error while writing order data. Order option \"{(currentOrderInfo.Value.OrderOption ?? null)}\" not found in the order prefab \"{orderPrefab.Name}\".");
DebugConsole.AddWarning($"Error while writing order data. Order option \"{currentOrderInfo.Option}\" not found in the order prefab \"{orderPrefab.Name}\".");
}
msg.WriteRangedInteger(optionIndex, -1, orderPrefab.AllOptions.Length);
}
else if (type == 2)
{
var objective = controller.ObjectiveManager.CurrentObjective;
bool validObjective = !string.IsNullOrEmpty(objective?.Identifier);
bool validObjective = objective != null && objective.Identifier != Identifier.Empty;
msg.Write(validObjective);
if (!validObjective) { break; }
msg.Write(objective.Identifier);
msg.Write(objective.Option ?? "");
msg.Write(objective.Option);
UInt16 targetEntityId = 0;
if (objective is AIObjectiveOperateItem operateObjective && operateObjective.OperateTarget != null)
{
@@ -435,7 +434,7 @@ namespace Barotrauma
foreach (var unlockedTalent in characterTalents)
{
msg.Write(unlockedTalent.AddedThisRound);
msg.Write(unlockedTalent.Prefab.UIntIdentifier);
msg.Write(unlockedTalent.Prefab.UintIdentifier);
}
break;
case NetEntityEvent.Type.UpdateMoney:
@@ -623,9 +622,9 @@ namespace Barotrauma
public void WriteSpawnData(IWriteMessage msg, UInt16 entityId, bool restrictMessageSize)
{
if (GameMain.Server == null) return;
if (GameMain.Server == null) { return; }
int msgLength = msg.LengthBytes;
int initialMsgLength = msg.LengthBytes;
msg.Write(Info == null);
msg.Write(entityId);
@@ -671,43 +670,60 @@ namespace Barotrauma
msg.Write((byte)TeamID);
msg.Write(this is AICharacter);
msg.Write(info.SpeciesName);
int msgLengthBeforeInfo = msg.LengthBytes;
info.ServerWrite(msg);
int infoLength = msg.LengthBytes - msgLengthBeforeInfo;
msg.Write((byte)CampaignInteractionType);
int msgLengthBeforeOrders = msg.LengthBytes;
// Current orders
msg.Write((byte)info.CurrentOrders.Count(o => o.Order != null));
msg.Write((byte)info.CurrentOrders.Count(o => o != null));
foreach (var orderInfo in info.CurrentOrders)
{
if (orderInfo.Order == null) { continue; }
msg.Write((byte)Order.PrefabList.IndexOf(orderInfo.Order.Prefab));
msg.Write(orderInfo.Order.TargetEntity == null ? (UInt16)0 : orderInfo.Order.TargetEntity.ID);
var hasOrderGiver = orderInfo.Order.OrderGiver != null;
if (orderInfo == null) { continue; }
msg.Write(orderInfo.Prefab.UintIdentifier);
msg.Write(orderInfo.TargetEntity == null ? (UInt16)0 : orderInfo.TargetEntity.ID);
var hasOrderGiver = orderInfo.OrderGiver != null;
msg.Write(hasOrderGiver);
if (hasOrderGiver) { msg.Write(orderInfo.Order.OrderGiver.ID); }
msg.Write((byte)(string.IsNullOrWhiteSpace(orderInfo.OrderOption) ? 0 : Array.IndexOf(orderInfo.Order.Prefab.Options, orderInfo.OrderOption)));
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);
var hasTargetPosition = orderInfo.Order.TargetPosition != null;
var hasTargetPosition = orderInfo.TargetPosition != null;
msg.Write(hasTargetPosition);
if (hasTargetPosition)
{
msg.Write(orderInfo.Order.TargetPosition.Position.X);
msg.Write(orderInfo.Order.TargetPosition.Position.Y);
msg.Write(orderInfo.Order.TargetPosition.Hull == null ? (UInt16)0 : orderInfo.Order.TargetPosition.Hull.ID);
msg.Write(orderInfo.TargetPosition.Position.X);
msg.Write(orderInfo.TargetPosition.Position.Y);
msg.Write(orderInfo.TargetPosition.Hull == null ? (UInt16)0 : orderInfo.TargetPosition.Hull.ID);
}
}
int ordersLength = msg.LengthBytes - msgLengthBeforeOrders;
if (msg.LengthBytes - initialMsgLength >= 255 && restrictMessageSize)
{
string errorMsg = $"Error when writing character spawn data: data exceeded 255 bytes (info: {infoLength}, orders: {ordersLength}, total: {msg.LengthBytes - initialMsgLength})";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Character.WriteSpawnData:TooMuchData", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
}
TryWriteStatus(msg);
void TryWriteStatus(IWriteMessage msg)
{
int msgLengthBeforeStatus = msg.LengthBytes - initialMsgLength;
var tempBuffer = new ReadWriteMessage();
WriteStatus(tempBuffer);
if (msg.LengthBytes + tempBuffer.LengthBytes >= 255 && restrictMessageSize)
if (msgLengthBeforeStatus + tempBuffer.LengthBytes >= 255 && restrictMessageSize)
{
msg.Write(false);
DebugConsole.ThrowError($"Error when writing character spawn data: status data caused the length of the message to exceed 255 bytes ({msg.LengthBytes} + {tempBuffer.LengthBytes})");
if (msgLengthBeforeStatus < 255)
{
string errorMsg = $"Error when writing character spawn data: status data caused the length of the message to exceed 255 bytes ({msgLengthBeforeStatus} + {tempBuffer.LengthBytes})";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Character.WriteSpawnData:TooMuchDataForStatus", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
}
}
else
{
@@ -716,7 +732,7 @@ namespace Barotrauma
}
}
DebugConsole.Log("Character spawn message length: " + (msg.LengthBytes - msgLength));
DebugConsole.Log("Character spawn message length: " + (msg.LengthBytes - initialMsgLength));
}
}
}
@@ -98,7 +98,7 @@ namespace Barotrauma
{
ColoredText msg = queuedMessages.Dequeue();
Messages.Add(msg);
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.SaveDebugConsoleLogs || GameSettings.CurrentConfig.VerboseLogging)
{
unsavedMessages.Add(msg);
if (unsavedMessages.Count >= messagesPerFile)
@@ -281,7 +281,7 @@ namespace Barotrauma
{
var msg = queuedMessages.Dequeue();
Messages.Add(msg);
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.SaveDebugConsoleLogs || GameSettings.CurrentConfig.VerboseLogging)
{
unsavedMessages.Add(msg);
if (unsavedMessages.Count >= messagesPerFile)
@@ -392,7 +392,7 @@ namespace Barotrauma
if (float.TryParse(args[0], out seconds))
{
seconds = Math.Max(0, seconds);
GameMain.Server.SendConsoleMessage("Set kill disconnected timer to " + ToolBox.SecondsToReadableTime(seconds), client);
GameMain.Server.SendConsoleMessage("Set kill disconnected timer to " + ToolBox.SecondsToReadableTime(seconds).Value, client);
NewMessage(client.Name + " set kill disconnected timer to " + ToolBox.SecondsToReadableTime(seconds), Color.White);
}
else
@@ -955,7 +955,7 @@ namespace Barotrauma
return;
}
client.Muted = true;
GameMain.Server.SendDirectChatMessage(TextManager.Get("MutedByServer"), client, ChatMessageType.MessageBox);
GameMain.Server.SendDirectChatMessage(TextManager.Get("MutedByServer").Value, client, ChatMessageType.MessageBox);
},
() =>
{
@@ -975,7 +975,7 @@ namespace Barotrauma
return;
}
client.Muted = false;
GameMain.Server.SendDirectChatMessage(TextManager.Get("UnmutedByServer"), client, ChatMessageType.MessageBox);
GameMain.Server.SendDirectChatMessage(TextManager.Get("UnmutedByServer").Value, client, ChatMessageType.MessageBox);
},
() =>
{
@@ -1102,7 +1102,7 @@ namespace Barotrauma
TraitorManager traitorManager = GameMain.Server.TraitorManager;
if (traitorManager == null || traitorManager.Traitors == null || !traitorManager.Traitors.Any())
{
GameMain.Server.SendTraitorMessage(client, "There are no traitors at the moment.", "", TraitorMessageType.Console);
GameMain.Server.SendTraitorMessage(client, "There are no traitors at the moment.", Identifier.Empty, TraitorMessageType.Console);
return;
}
foreach (Traitor t in traitorManager.Traitors)
@@ -1116,11 +1116,11 @@ namespace Barotrauma
$"[traitorgoals]={traitorGoals.Substring(traitorGoalsStart)}",
$"[traitorname]={t.Character.Name}",
"Traitor [traitorname]'s current goals are:\n[traitorgoals]"
}.Where(s => !string.IsNullOrEmpty(s))), t.Mission?.Identifier, TraitorMessageType.Console);
}.Where(s => !string.IsNullOrEmpty(s))), t.Mission.Identifier, TraitorMessageType.Console);
}
else
{
GameMain.Server.SendTraitorMessage(client, string.Format("- Traitor {0} has no current objective.", "", t.Character.Name), "", TraitorMessageType.Console);
GameMain.Server.SendTraitorMessage(client, string.Format("- Traitor {0} has no current objective.", "", t.Character.Name), Identifier.Empty, TraitorMessageType.Console);
}
}
//GameMain.Server.SendTraitorMessage(client, "The code words are: " + traitorManager.CodeWords + ", response: " + traitorManager.CodeResponse + ".", TraitorMessageType.Console);
@@ -1296,7 +1296,7 @@ namespace Barotrauma
{
return new string[][]
{
GameModePreset.List.Select(gm => gm.Name).ToArray()
GameModePreset.List.Select(gm => gm.Name.Value).ToArray()
};
}));
@@ -1668,7 +1668,7 @@ namespace Barotrauma
AfflictionPrefab afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(a =>
a.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase) ||
a.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase));
a.Identifier == args[0]);
if (afflictionPrefab == null)
{
GameMain.Server.SendConsoleMessage("Affliction \"" + args[0] + "\" not found.", client, Color.Red);
@@ -1756,7 +1756,7 @@ namespace Barotrauma
if (targetCharacter == null) { return; }
TalentPrefab talentPrefab = TalentPrefab.TalentPrefabs.Find(c =>
c.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase) ||
c.Identifier == args[0] ||
c.DisplayName.Equals(args[0], StringComparison.OrdinalIgnoreCase));
if (talentPrefab == null)
{
@@ -1789,7 +1789,7 @@ namespace Barotrauma
GameMain.Server.SendConsoleMessage($"Failed to find the job \"{args[0]}\".", client, Color.Red);
return;
}
if (!TalentTree.JobTalentTrees.TryGetValue(job.Identifier, out TalentTree talentTree))
if (!TalentTree.JobTalentTrees.TryGet(job.Identifier, out TalentTree talentTree))
{
GameMain.Server.SendConsoleMessage($"No talents configured for the job \"{args[0]}\".", client, Color.Red);
return;
@@ -2010,7 +2010,7 @@ namespace Barotrauma
client.SetPermissions(preset.Permissions, preset.PermittedCommands);
GameMain.Server.UpdateClientPermissions(client);
GameMain.Server.SendConsoleMessage("Assigned the rank \"" + preset.Name + "\" to " + client.Name + ".", senderClient);
GameMain.Server.SendConsoleMessage($"Assigned the rank \"{preset.Name}\" to {client.Name}.", senderClient);
NewMessage(senderClient.Name + " granted the rank \"" + preset.Name + "\" to " + client.Name + ".", Color.White);
}
);
@@ -2158,7 +2158,7 @@ namespace Barotrauma
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
{
if (permission == ClientPermissions.None || !client.HasPermission(permission)) { continue; }
GameMain.Server.SendConsoleMessage(" - " + TextManager.Get("ClientPermission." + permission), senderClient);
GameMain.Server.SendConsoleMessage($" - {TextManager.Get("ClientPermission." + permission)}", senderClient);
}
if (client.HasPermission(ClientPermissions.ConsoleCommands))
{
@@ -2257,7 +2257,7 @@ namespace Barotrauma
var tagList = MapEntityPrefab.List.SelectMany(p => p.Tags.Select(t => t)).Distinct();
foreach (var tag in tagList)
{
NewMessage(tag, Color.Yellow);
NewMessage(tag.Value, Color.Yellow);
}
}));
@@ -2298,7 +2298,7 @@ namespace Barotrauma
return;
}
string skillIdentifier = args[0];
Identifier skillIdentifier = args[0].ToIdentifier();
string levelString = args[1];
Character character = args.Length >= 3 ? FindMatchingCharacter(args.Skip(2).ToArray(), false) : senderClient.Character;
@@ -2313,7 +2313,7 @@ namespace Barotrauma
if (float.TryParse(levelString, NumberStyles.Number, CultureInfo.InvariantCulture, out float level) || isMax)
{
if (isMax) { level = 100; }
if (skillIdentifier.Equals("all", StringComparison.OrdinalIgnoreCase))
if (skillIdentifier == "all")
{
foreach (Skill skill in character.Info.Job.Skills)
{
@@ -2397,7 +2397,7 @@ namespace Barotrauma
{
GameMain.Server.CreateEntityEvent(c, new object[] { NetEntityEvent.Type.Status });
}*/
foreach (Hull hull in Hull.hullList)
foreach (Hull hull in Hull.HullList)
{
GameMain.Server.CreateEntityEvent(hull);
}
@@ -80,7 +80,7 @@ namespace Barotrauma
partial void ShowDialog(Character speaker, Character targetCharacter)
{
targetClients.Clear();
if (!string.IsNullOrEmpty(TargetTag))
if (!TargetTag.IsEmpty)
{
IEnumerable<Entity> entities = ParentEvent.GetTargets(TargetTag);
foreach (Entity e in entities)
@@ -15,7 +15,7 @@ namespace Barotrauma
msg.Write((ushort)spawnedItems.Count);
foreach (Item item in spawnedItems)
{
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0);
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0, -1);
}
msg.Write((byte)characters.Count);
@@ -27,7 +27,7 @@ namespace Barotrauma
msg.Write((ushort)characterItems[character].Count());
foreach (Item item in characterItems[character])
{
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0);
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0, item.ParentInventory?.FindIndex(item) ?? -1);
}
}
}
@@ -13,7 +13,8 @@ namespace Barotrauma
item.WriteSpawnData(msg,
item.ID,
parentInventoryIDs.ContainsKey(item) ? parentInventoryIDs[item] : Entity.NullEntityID,
parentItemContainerIndices.ContainsKey(item) ? parentItemContainerIndices[item] : (byte)0);
parentItemContainerIndices.ContainsKey(item) ? parentItemContainerIndices[item] : (byte)0,
inventorySlotIndices.ContainsKey(item) ? inventorySlotIndices[item] : -1);
}
}
}
@@ -11,11 +11,11 @@ namespace Barotrauma
private bool initialized = false;
public override string Description
public override LocalizedString Description
{
get
{
if (descriptions == null) return "";
if (descriptions == null) { return ""; }
//non-team-specific description
return descriptions[0];
@@ -24,7 +24,7 @@ namespace Barotrauma
msg.Write((ushort)characterItems[character].Count());
foreach (Item item in characterItems[character])
{
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0);
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0, item.ParentInventory?.FindIndex(item) ?? -1);
}
}
}
@@ -16,11 +16,11 @@ namespace Barotrauma
foreach (var kvp in spawnedResources)
{
msg.Write((byte)kvp.Value.Count);
var rotation = resourceClusters[kvp.Key].rotation;
var rotation = resourceClusters[kvp.Key].Rotation;
msg.Write(rotation);
foreach (var r in kvp.Value)
{
r.WriteSpawnData(msg, r.ID, Entity.NullEntityID, 0);
r.WriteSpawnData(msg, r.ID, Entity.NullEntityID, 0, -1);
}
}
@@ -7,13 +7,13 @@ namespace Barotrauma
partial void ShowMessageProjSpecific(int missionState)
{
int messageIndex = missionState - 1;
if (messageIndex >= Headers.Count && messageIndex >= Messages.Count) { return; }
if (messageIndex >= Headers.Length && messageIndex >= Messages.Length) { return; }
if (messageIndex < 0) { return; }
string header = messageIndex < Headers.Count ? Headers[messageIndex] : "";
string message = messageIndex < Messages.Count ? Messages[messageIndex] : "";
LocalizedString header = messageIndex < Headers.Length ? Headers[messageIndex] : "";
LocalizedString message = messageIndex < Messages.Length ? Messages[messageIndex] : "";
GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage);
GameServer.Log($"{TextManager.Get("MissionInfo")}: {header} - {message}", ServerLog.MessageType.ServerMessage);
}
public virtual void ServerWriteInitial(IWriteMessage msg, Client c)
@@ -15,7 +15,7 @@ namespace Barotrauma
msg.Write((ushort)items.Count);
foreach (Item item in items)
{
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0);
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0, -1);
}
}
}
@@ -24,7 +24,7 @@ namespace Barotrauma
msg.Write((ushort)characterItems[character].Count());
foreach (Item item in characterItems[character])
{
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0);
item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? Entity.NullEntityID, 0, item.ParentInventory?.FindIndex(item) ?? -1);
}
}
}
@@ -10,6 +10,7 @@ namespace Barotrauma
private UInt16 originalInventoryID;
private byte originalItemContainerIndex;
private int originalSlotIndex;
private readonly List<Pair<int, int>> executedEffectIndices = new List<Pair<int, int>>();
@@ -24,7 +25,7 @@ namespace Barotrauma
}
else
{
item.WriteSpawnData(msg, item.ID, originalInventoryID, originalItemContainerIndex);
item.WriteSpawnData(msg, item.ID, originalInventoryID, originalItemContainerIndex, originalSlotIndex);
}
msg.Write((byte)executedEffectIndices.Count);
@@ -13,7 +13,8 @@ namespace Barotrauma
item.WriteSpawnData(msg,
item.ID,
parentInventoryIDs.ContainsKey(item) ? parentInventoryIDs[item] : Entity.NullEntityID,
parentItemContainerIndices.ContainsKey(item) ? parentItemContainerIndices[item] : (byte)0);
parentItemContainerIndices.ContainsKey(item) ? parentItemContainerIndices[item] : (byte)0,
inventorySlotIndices.ContainsKey(item) ? inventorySlotIndices[item] : -1);
}
ServerWriteScanTargetStatus(msg);
}
@@ -10,6 +10,7 @@ using System.Linq;
using System.Reflection;
using System.Threading;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -20,7 +21,6 @@ namespace Barotrauma
public static bool IsSingleplayer => NetworkMember == null;
public static bool IsMultiplayer => NetworkMember != null;
private static World world;
public static World World
{
@@ -31,7 +31,6 @@ namespace Barotrauma
}
set { world = value; }
}
public static GameSettings Config;
public static GameServer Server;
public static NetworkMember NetworkMember
@@ -58,28 +57,14 @@ namespace Barotrauma
//TODO: maybe clean up instead of having these constants
public static readonly Screen SubEditorScreen = UnimplementedScreen.Instance;
public static DecalManager DecalManager;
public static bool ShouldRun = true;
private static Stopwatch stopwatch;
private static Queue<int> prevUpdateRates = new Queue<int>();
private static readonly Queue<int> prevUpdateRates = new Queue<int>();
private static int updateCount = 0;
private static ContentPackage vanillaContent;
public static ContentPackage VanillaContent
{
get
{
if (vanillaContent == null)
{
// TODO: Dynamic method for defining and finding the vanilla content package.
vanillaContent = ContentPackage.CorePackages.SingleOrDefault(cp => Path.GetFileName(cp.Path).Equals("vanilla 0.9.xml", StringComparison.OrdinalIgnoreCase));
}
return vanillaContent;
}
}
public static ContentPackage VanillaContent => ContentPackageManager.VanillaCorePackage;
public readonly string[] CommandLineArgs;
@@ -96,13 +81,14 @@ namespace Barotrauma
FarseerPhysics.Settings.PositionIterations = 1;
Console.WriteLine("Loading game settings");
Config = new GameSettings();
GameSettings.Init();
Console.WriteLine("Loading MD5 hash cache");
Md5Hash.LoadCache();
Md5Hash.Cache.Load();
Console.WriteLine("Initializing SteamManager");
SteamManager.Initialize();
//TODO: figure out how consent is supposed to work for servers
//Console.WriteLine("Initializing GameAnalytics");
//GameAnalyticsManager.InitIfConsented();
@@ -115,36 +101,11 @@ namespace Barotrauma
public void Init()
{
NPCSet.LoadSets();
FactionPrefab.LoadFactions();
CharacterPrefab.LoadAll();
MissionPrefab.Init();
TraitorMissionPrefab.Init();
MapEntityPrefab.Init();
MapGenerationParams.Init();
LevelGenerationParams.LoadPresets();
CaveGenerationParams.LoadPresets();
OutpostGenerationParams.LoadPresets();
EventSet.LoadPrefabs();
Order.Init();
EventManagerSettings.Init();
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure));
UpgradePrefab.LoadAll(GetFilesOfType(ContentType.UpgradeModules));
JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs));
CorpsePrefab.LoadAll(GetFilesOfType(ContentType.Corpses));
NPCConversation.LoadAll(GetFilesOfType(ContentType.NPCConversations));
ItemAssemblyPrefab.LoadAll();
LevelObjectPrefab.LoadAll();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
TalentPrefab.LoadAll(GetFilesOfType(ContentType.Talents));
TalentTree.LoadAll(GetFilesOfType(ContentType.TalentTrees));
CoreEntityPrefab.InitCorePrefabs();
GameModePreset.Init();
DecalManager = new DecalManager();
LocationType.Init();
ContentPackageManager.Init().Consume();
SubmarineInfo.RefreshSavedSubs();
@@ -180,23 +141,6 @@ namespace Barotrauma
}*/
}
/// <summary>
/// Returns the file paths of all files of the given type in the content packages.
/// </summary>
/// <param name="type"></param>
/// <param name="searchAllContentPackages">If true, also returns files in content packages that are installed but not currently selected.</param>
public IEnumerable<ContentFile> GetFilesOfType(ContentType type, bool searchAllContentPackages = false)
{
if (searchAllContentPackages)
{
return ContentPackage.GetFilesOfType(ContentPackage.AllPackages, type);
}
else
{
return ContentPackage.GetFilesOfType(Config.AllEnabledPackages, type);
}
}
public bool TryStartChildServerRelay()
{
for (int i = 0; i < CommandLineArgs.Length; i++)
@@ -383,6 +327,9 @@ namespace Barotrauma
//otherwise it snowballs and becomes unplayable
Timing.Accumulator = Timing.Step;
}
CrossThread.ProcessTasks();
prevTicks = currTicks;
while (Timing.Accumulator >= Timing.Step)
{
@@ -455,7 +402,8 @@ namespace Barotrauma
SaveUtil.CleanUnnecessarySaveFiles();
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameSettings.CurrentConfig.SaveDebugConsoleLogs
|| GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.ShutDown(); }
MainThread = null;
@@ -63,12 +63,12 @@ namespace Barotrauma
if (!item.Removed && canAddToRemoveQueue && Entity.FindEntityByID(item.ID) is Item entity)
{
item.Removed = true;
Entity.Spawner.AddToRemoveQueue(entity);
Entity.Spawner.AddItemToRemoveQueue(entity);
}
SoldItems.Add(item);
Location.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier);
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier.Value);
}
OnSoldItemsChanged?.Invoke();
}
@@ -46,14 +46,14 @@ namespace Barotrauma
public void ServerWriteActiveOrders(IWriteMessage msg)
{
ushort count = (ushort)ActiveOrders.Count(o => o.First != null && !o.Second.HasValue);
ushort count = (ushort)ActiveOrders.Count(o => o.Order != null && !o.FadeOutTime.HasValue);
msg.Write(count);
if (count > 0)
{
foreach (var activeOrder in ActiveOrders)
{
if (!(activeOrder?.First is Order order) || activeOrder.Second.HasValue) { continue; }
OrderChatMessage.WriteOrder(msg, order, targetCharacter: null, order.TargetSpatialEntity, orderOption: null, orderPriority: 0, order.WallSectionIndex, isNewOrder: true);
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);
if (hasOrderGiver)
@@ -14,8 +14,8 @@ namespace Barotrauma
{
foreach (Mission mission in Missions)
{
GameServer.Log(TextManager.Get("Mission") + ": " + mission.Name, ServerLog.MessageType.ServerMessage);
GameServer.Log(mission.Description, ServerLog.MessageType.ServerMessage);
GameServer.Log($"{TextManager.Get("Mission")}: {mission.Name}", ServerLog.MessageType.ServerMessage);
GameServer.Log(mission.Description.Value, ServerLog.MessageType.ServerMessage);
}
}
}
@@ -94,7 +94,7 @@ namespace Barotrauma
{
throw new System.InvalidOperationException($"Failed to spawn inventory items for the character \"{character.Name}\". No saved inventory data.");
}
character.SpawnInventoryItems(inventory, itemData);
character.SpawnInventoryItems(inventory, itemData.FromPackage(null));
}
public void ApplyHealthData(Character character)
@@ -6,8 +6,8 @@
{
foreach (Mission mission in missions)
{
Networking.GameServer.Log(TextManager.Get("Mission") + ": " + mission.Name, Networking.ServerLog.MessageType.ServerMessage);
Networking.GameServer.Log(mission.Description, Networking.ServerLog.MessageType.ServerMessage);
Networking.GameServer.Log($"{TextManager.Get("Mission")}: {mission.Name}", Networking.ServerLog.MessageType.ServerMessage);
Networking.GameServer.Log(mission.Description.Value, Networking.ServerLog.MessageType.ServerMessage);
}
}
}
@@ -607,7 +607,7 @@ namespace Barotrauma
foreach (var itemSwap in UpgradeManager.PurchasedItemSwaps)
{
msg.Write(itemSwap.ItemToRemove.ID);
msg.Write(itemSwap.ItemToInstall?.Identifier ?? string.Empty);
msg.Write(itemSwap.ItemToInstall?.Identifier ?? Identifier.Empty);
}
var characterData = GetClientCharacterData(c);
@@ -681,10 +681,10 @@ namespace Barotrauma
List<PurchasedUpgrade> purchasedUpgrades = new List<PurchasedUpgrade>();
for (int i = 0; i < purchasedUpgradeCount; i++)
{
string upgradeIdentifier = msg.ReadString();
Identifier upgradeIdentifier = msg.ReadIdentifier();
UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
string categoryIdentifier = msg.ReadString();
Identifier categoryIdentifier = msg.ReadIdentifier();
UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
int upgradeLevel = msg.ReadByte();
@@ -698,8 +698,8 @@ namespace Barotrauma
for (int i = 0; i < purchasedItemSwapCount; i++)
{
UInt16 itemToRemoveID = msg.ReadUInt16();
string itemToInstallIdentifier = msg.ReadString();
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
Identifier itemToInstallIdentifier = msg.ReadIdentifier();
ItemPrefab itemToInstall = itemToInstallIdentifier.IsEmpty ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
@@ -9,11 +9,11 @@ namespace Barotrauma.Items.Components
msg.Write(tainted);
if (tainted)
{
msg.Write(selectedTaintedEffect?.UIntIdentifier ?? 0);
msg.Write(selectedTaintedEffect?.UintIdentifier ?? 0);
}
else
{
msg.Write(selectedEffect?.UIntIdentifier ?? 0);
msg.Write(selectedEffect?.UintIdentifier ?? 0);
}
}
}
@@ -9,9 +9,9 @@ namespace Barotrauma.Items.Components
private const int serverHealthUpdateDelay = 10;
private int serverHealthUpdateTimer;
partial void LoadVines(XElement element)
partial void LoadVines(ContentXElement element)
{
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
{
partial class ItemComponent : ISerializableEntity
{
private bool LoadElemProjSpecific(XElement subElement)
private bool LoadElemProjSpecific(ContentXElement subElement)
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -11,28 +11,28 @@ namespace Barotrauma.Items.Components
private string lastSentText;
private float sendStateTimer;
[Serialize("", true, description: "The text to display on the label.", alwaysUseInstanceValues: true), Editable(100)]
[Serialize("", IsPropertySaveable.Yes, description: "The text to display on the label.", alwaysUseInstanceValues: true), Editable(100)]
public string Text
{
get;
set;
}
[Editable, Serialize("0,0,0,255", true, description: "The color of the text displayed on the label.", alwaysUseInstanceValues: true)]
[Editable, Serialize("0,0,0,255", IsPropertySaveable.Yes, description: "The color of the text displayed on the label.", alwaysUseInstanceValues: true)]
public Color TextColor
{
get;
set;
}
[Editable, Serialize(1.0f, true, description: "The scale of the text displayed on the label.", alwaysUseInstanceValues: true)]
[Editable, Serialize(1.0f, IsPropertySaveable.Yes, description: "The scale of the text displayed on the label.", alwaysUseInstanceValues: true)]
public float TextScale
{
get;
set;
}
[Serialize("0,0,0,0", true, description: "The amount of padding around the text in pixels (left,top,right,bottom).")]
[Serialize("0,0,0,0", IsPropertySaveable.Yes, description: "The amount of padding around the text in pixels (left,top,right,bottom).")]
public Vector4 Padding
{
get;
@@ -44,7 +44,7 @@ namespace Barotrauma.Items.Components
//do nothing
}
public ItemLabel(Item item, XElement element)
public ItemLabel(Item item, ContentXElement element)
: base(item, element)
{
}
@@ -11,23 +11,23 @@ namespace Barotrauma.Items.Components
{
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
{
int itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);
uint recipeHash = msg.ReadUInt32();
item.CreateServerEvent(this);
if (!item.CanClientAccess(c)) return;
if (itemIndex == -1)
if (recipeHash == 0)
{
CancelFabricating(c.Character);
}
else
{
//if already fabricating the selected item, return
if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex) return;
if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count) return;
if (fabricatedItem != null && fabricatedItem.RecipeHash == recipeHash) { return; }
if (recipeHash == 0) { return; }
StartFabricating(fabricationRecipes[itemIndex], c.Character);
StartFabricating(fabricationRecipes[recipeHash], c.Character);
}
}
@@ -48,9 +48,9 @@ namespace Barotrauma.Items.Components
FabricatorState stateAtEvent = (FabricatorState)extraData[3];
msg.Write((byte)stateAtEvent);
msg.Write(timeUntilReady);
int itemIndex = fabricatedItem == null ? -1 : fabricationRecipes.IndexOf(fabricatedItem);
msg.WriteRangedInteger(itemIndex, -1, fabricationRecipes.Count - 1);
UInt16 userID = fabricatedItem == null || user == null ? (UInt16)0 : user.ID;
uint recipeHash = fabricatedItem?.RecipeHash ?? 0;
msg.Write(recipeHash);
UInt16 userID = fabricatedItem is null || user is null ? (UInt16)0 : user.ID;
msg.Write(userID);
}
}
@@ -4,7 +4,10 @@ namespace Barotrauma.Items.Components
{
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
{
void InitProjSpecific()
private Character prevLoggedFixer;
private FixActions prevLoggedFixAction;
partial void InitProjSpecific(ContentXElement _)
{
//let the clients know the initial deterioration delay
item.CreateServerEvent(this);
@@ -19,7 +22,7 @@ namespace Barotrauma.Items.Components
{
if (!c.Character.IsTraitor && requestedFixAction == FixActions.Sabotage)
{
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.Log($"Non traitor \"{c.Character.Name}\" attempted to sabotage item.");
}
@@ -33,7 +33,7 @@ namespace Barotrauma
{
accessible = false;
}
else if (!characterInventory.AccessibleWhenAlive && !ownerCharacter.IsDead)
else if (!characterInventory.AccessibleWhenAlive && !ownerCharacter.IsDead && !characterInventory.AccessibleByOwner)
{
accessible = false;
}
@@ -2,6 +2,7 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
@@ -14,7 +15,7 @@ namespace Barotrauma
public override Sprite Sprite
{
get { return prefab?.sprite; }
get { return base.Prefab?.Sprite; }
}
partial void AssignCampaignInteractionTypeProjSpecific(CampaignMode.InteractionType interactionType)
@@ -232,14 +233,14 @@ namespace Barotrauma
}
}
public void WriteSpawnData(IWriteMessage msg, UInt16 entityID, UInt16 originalInventoryID, byte originalItemContainerIndex)
public void WriteSpawnData(IWriteMessage msg, UInt16 entityID, UInt16 originalInventoryID, byte originalItemContainerIndex, int originalSlotIndex)
{
if (GameMain.Server == null) { return; }
msg.Write(Prefab.OriginalName);
msg.Write(Prefab.Identifier);
msg.Write(Description != prefab.Description);
if (Description != prefab.Description)
msg.Write(Description != base.Prefab.Description);
if (Description != base.Prefab.Description)
{
msg.Write(Description);
}
@@ -259,9 +260,7 @@ namespace Barotrauma
{
msg.Write(originalInventoryID);
msg.Write(originalItemContainerIndex);
int slotIndex = ParentInventory.FindIndex(this);
msg.Write(slotIndex < 0 ? (byte)255 : (byte)slotIndex);
msg.Write(originalSlotIndex < 0 ? (byte)255 : (byte)originalSlotIndex);
}
msg.Write(body == null ? (byte)0 : (byte)body.BodyType);
@@ -285,13 +284,13 @@ namespace Barotrauma
}
msg.Write(teamID);
bool tagsChanged = tags.Count != prefab.Tags.Count || !tags.All(t => prefab.Tags.Contains(t));
bool tagsChanged = tags.Count != base.Prefab.Tags.Count || !tags.All(t => base.Prefab.Tags.Contains(t));
msg.Write(tagsChanged);
if (tagsChanged)
{
string[] splitTags = Tags.Split(',');
msg.Write(string.Join(',', splitTags.Where(t => !prefab.Tags.Contains(t))));
msg.Write(string.Join(',', prefab.Tags.Where(t => !splitTags.Contains(t))));
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))));
}
var nameTag = GetComponent<NameTag>();
msg.Write(nameTag != null);
@@ -386,7 +385,7 @@ namespace Barotrauma
if (!ItemList.Contains(this))
{
string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg);
DebugConsole.AddWarning(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return;
}
@@ -7,9 +7,11 @@ namespace Barotrauma.MapCreatures.Behavior
{
partial class BallastFloraBehavior
{
partial void LoadPrefab(XElement element)
private float damageUpdateTimer;
partial void LoadPrefab(ContentXElement element)
{
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -29,7 +31,24 @@ namespace Barotrauma.MapCreatures.Behavior
}
}
partial void UpdateDamage(float deltaTime)
{
if (damageUpdateTimer <= 0)
{
foreach (BallastFloraBranch branch in Branches)
{
if (Math.Abs(branch.AccumulatedDamage) > 1.0f)
{
SendNetworkMessage(this, NetworkHeader.BranchDamage, branch);
branch.AccumulatedDamage = 0f;
}
}
damageUpdateTimer = 1f;
}
damageUpdateTimer -= deltaTime;
}
public void ServerWriteSpawn(IWriteMessage msg)
{
msg.Write(Prefab.Identifier);
@@ -49,12 +68,12 @@ namespace Barotrauma.MapCreatures.Behavior
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));
}
public void ServerWriteBranchDamage(IWriteMessage msg, BallastFloraBranch branch, float damage)
public void ServerWriteBranchDamage(IWriteMessage msg, BallastFloraBranch branch)
{
msg.Write((int)branch.ID);
msg.Write(damage);
msg.Write(branch.Health);
}
@@ -82,12 +82,13 @@ namespace Barotrauma
behavior.ServerWriteSpawn(message);
break;
case BallastFloraBehavior.NetworkHeader.Kill:
case BallastFloraBehavior.NetworkHeader.Remove:
break;
case BallastFloraBehavior.NetworkHeader.BranchCreate when extraData.Length >= 4 && extraData[2] is BallastFloraBranch branch && extraData[3] is int parentId:
behavior.ServerWriteBranchGrowth(message, branch, parentId);
break;
case BallastFloraBehavior.NetworkHeader.BranchDamage when extraData.Length >= 4 && extraData[2] is BallastFloraBranch branch && extraData[3] is float damage:
behavior.ServerWriteBranchDamage(message, branch, damage);
case BallastFloraBehavior.NetworkHeader.BranchDamage when extraData.Length >= 4 && extraData[2] is BallastFloraBranch branch:
behavior.ServerWriteBranchDamage(message, branch);
break;
case BallastFloraBehavior.NetworkHeader.BranchRemove when extraData.Length >= 3 && extraData[2] is BallastFloraBranch branch:
behavior.ServerWriteBranchRemove(message, branch);
@@ -147,7 +148,7 @@ namespace Barotrauma
message.WriteRangedInteger(decals.Count, 0, MaxDecalsPerHull);
foreach (Decal decal in decals)
{
message.Write(decal.Prefab.UIntIdentifier);
message.Write(decal.Prefab.UintIdentifier);
message.Write((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);
@@ -20,12 +20,13 @@ namespace Barotrauma.Networking
OrderTarget orderTargetPosition = null;
Order.OrderTargetType orderTargetType = Order.OrderTargetType.Entity;
int? wallSectionIndex = null;
Order order = null;
if (type == ChatMessageType.Order)
{
var orderMessageInfo = OrderChatMessage.ReadOrder(msg);
if (orderMessageInfo.OrderIndex < 0 || orderMessageInfo.OrderIndex >= Order.PrefabList.Count)
if (orderMessageInfo.OrderIdentifier == Identifier.Empty)
{
DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order index out of bounds ({orderMessageInfo.OrderIndex}).");
DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order identifier is empty.");
if (NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { c.LastSentChatMsgID = ID; }
return;
}
@@ -34,14 +35,29 @@ namespace Barotrauma.Networking
orderTargetPosition = orderMessageInfo.TargetPosition;
orderTargetType = orderMessageInfo.TargetType;
wallSectionIndex = orderMessageInfo.WallSectionIndex;
var orderPrefab = orderMessageInfo.OrderPrefab ?? Order.PrefabList[orderMessageInfo.OrderIndex];
string orderOption = orderMessageInfo.OrderOption ??
(orderMessageInfo.OrderOptionIndex == null || orderMessageInfo.OrderOptionIndex < 0 || orderMessageInfo.OrderOptionIndex >= orderPrefab.Options.Length ?
"" : orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value]);
orderMsg = new OrderChatMessage(orderPrefab, orderOption, orderMessageInfo.Priority, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character, isNewOrder: orderMessageInfo.IsNewOrder)
var orderPrefab = orderMessageInfo.OrderPrefab ?? OrderPrefab.Prefabs[orderMessageInfo.OrderIdentifier];
Identifier orderOption = orderMessageInfo.OrderOption;
if (orderOption.IsEmpty)
{
WallSectionIndex = wallSectionIndex
};
orderOption = orderMessageInfo.OrderOptionIndex == null || orderMessageInfo.OrderOptionIndex < 0 || orderMessageInfo.OrderOptionIndex >= orderPrefab.Options.Length ?
Identifier.Empty : orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value];
}
if (orderTargetType == Order.OrderTargetType.Position)
{
order = new Order(orderPrefab, orderOption, orderTargetPosition, orderGiver: c.Character)
.WithManualPriority(orderMessageInfo.Priority);
}
else if (orderTargetType == Order.OrderTargetType.WallSection)
{
order = new Order(orderPrefab, orderOption, orderTargetEntity as Structure, wallSectionIndex, orderGiver: c.Character)
.WithManualPriority(orderMessageInfo.Priority);
}
else
{
order = new Order(orderPrefab, orderOption, orderTargetEntity, orderPrefab.GetTargetItemComponent(orderTargetEntity as Item), orderGiver: c.Character)
.WithManualPriority(orderMessageInfo.Priority);
}
orderMsg = new OrderChatMessage(order, orderTargetCharacter, c.Character);
txt = orderMsg.Text;
}
else
@@ -95,11 +111,11 @@ namespace Barotrauma.Networking
if (c.ChatSpamCount > 3)
{
//kick for spamming too much
GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked"));
GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked").Value);
}
else
{
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null);
c.ChatSpamTimer = 10.0f;
GameMain.Server.SendDirectChatMessage(denyMsg, c);
}
@@ -110,7 +126,7 @@ namespace Barotrauma.Networking
if (c.ChatSpamTimer > 0.0f && !isOwner)
{
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null);
c.ChatSpamTimer = 10.0f;
GameMain.Server.SendDirectChatMessage(denyMsg, c);
return;
@@ -123,16 +139,6 @@ namespace Barotrauma.Networking
{
HumanAIController.ReportProblem(orderMsg.Sender, orderMsg.Order);
}
Order order = orderTargetType switch
{
Order.OrderTargetType.Entity =>
new Order(orderMsg.Order, orderTargetEntity, orderMsg.Order?.GetTargetItemComponent(orderTargetEntity as Item), orderGiver: orderMsg.Sender),
Order.OrderTargetType.Position =>
new Order(orderMsg.Order, orderTargetPosition, orderGiver: orderMsg.Sender),
Order.OrderTargetType.WallSection when orderTargetEntity is Structure s && wallSectionIndex.HasValue =>
new Order(orderMsg.Order, s, wallSectionIndex, orderGiver: orderMsg.Sender),
_ => throw new NotImplementedException()
};
if (order != null)
{
if (order.TargetAllCharacters)
@@ -159,7 +165,7 @@ namespace Barotrauma.Networking
}
else if (orderTargetCharacter != null)
{
orderTargetCharacter.SetOrder(order, orderMsg.OrderOption, orderMsg.OrderPriority, orderMsg.Sender);
orderTargetCharacter.SetOrder(order);
}
}
GameMain.Server.SendOrderChatMessage(orderMsg);
@@ -57,8 +57,8 @@ namespace Barotrauma.Networking
public bool ReadyToStart;
public List<Pair<JobPrefab, int>> JobPreferences;
public Pair<JobPrefab, int> AssignedJob;
public List<JobVariant> JobPreferences;
public JobVariant AssignedJob;
public float DeleteDisconnectedTimer;
@@ -104,7 +104,7 @@ namespace Barotrauma.Networking
partial void InitProjSpecific()
{
JobPreferences = new List<Pair<JobPrefab, int>>();
JobPreferences = new List<JobVariant>();
VoipQueue = new VoipQueue(ID, true, true);
GameMain.Server.VoipServer.RegisterQueue(VoipQueue);
@@ -149,7 +149,7 @@ namespace Barotrauma.Networking
foreach (char character in name)
{
if (!serverSettings.AllowedClientNameChars.Any(charRange => (int)character >= charRange.First && (int)character <= charRange.Second)) { return false; }
if (!serverSettings.AllowedClientNameChars.Any(charRange => (int)character >= charRange.Start && (int)character <= charRange.End)) { return false; }
}
return true;
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
@@ -12,15 +11,21 @@ namespace Barotrauma
partial void CreateNetworkEventProjSpecific(Entity entity, bool remove)
{
if (GameMain.Server != null && entity != null)
if (GameMain.Server == null || entity == null) { return; }
GameMain.Server.CreateEntityEvent(this, new object[] { new SpawnOrRemove(entity, remove) });
if (entity is Character character && character.Info != null)
{
GameMain.Server.CreateEntityEvent(this, new object[] { new SpawnOrRemove(entity, remove) });
}
foreach (var statKey in character.Info.SavedStatValues.Keys)
{
GameMain.NetworkMember.CreateEntityEvent(character, new object[] { NetEntityEvent.Type.UpdatePermanentStats, statKey });
}
}
}
public void ServerWrite(IWriteMessage message, Client client, object[] extraData = null)
{
if (GameMain.Server == null) return;
if (GameMain.Server == null) { return; }
SpawnOrRemove entities = (SpawnOrRemove)extraData[0];
@@ -31,17 +36,17 @@ namespace Barotrauma
}
else
{
if (entities.Entity is Item)
if (entities.Entity is Item item)
{
message.Write((byte)SpawnableType.Item);
DebugConsole.Log("Writing item spawn data " + entities.Entity.ToString() + " (original ID: " + entities.OriginalID + ", current ID: " + entities.Entity.ID + ")");
((Item)entities.Entity).WriteSpawnData(message, entities.OriginalID, entities.OriginalInventoryID, entities.OriginalItemContainerIndex);
item.WriteSpawnData(message, entities.OriginalID, entities.OriginalInventoryID, entities.OriginalItemContainerIndex, entities.OriginalSlotIndex);
}
else if (entities.Entity is Character)
else if (entities.Entity is Character character)
{
message.Write((byte)SpawnableType.Character);
DebugConsole.Log("Writing character spawn data: " + entities.Entity.ToString() + " (original ID: " + entities.OriginalID + ", current ID: " + entities.Entity.ID + ")");
((Character)entities.Entity).WriteSpawnData(message, entities.OriginalID, restrictMessageSize: true);
character.WriteSpawnData(message, entities.OriginalID, restrictMessageSize: true);
}
}
}
@@ -36,12 +36,25 @@ namespace Barotrauma.Networking
get { return KnownReceivedOffset / (float)Data.Length; }
}
private float waitTimer;
public float WaitTimer
{
get;
set;
get => waitTimer;
set
{
if (value > 0.0f)
{
//setting a wait timer means that network conditions
//aren't ideal, slow down the packet rate
PacketsPerUpdate = Math.Max(PacketsPerUpdate / 2.0f, 1.0f);
}
waitTimer = value;
}
}
public const int MaxPacketsPerUpdate = 4;
public float PacketsPerUpdate { get; set; } = 1.0f;
public byte[] Data { get; }
public bool Acknowledged;
@@ -112,10 +125,7 @@ namespace Barotrauma.Networking
public float StallPacketsTime { get; set; }
#endif
public List<FileTransferOut> ActiveTransfers
{
get { return activeTransfers; }
}
public IReadOnlyList<FileTransferOut> ActiveTransfers => activeTransfers;
public FileSender(ServerPeer serverPeer, int mtu)
{
@@ -197,9 +207,6 @@ namespace Barotrauma.Networking
private void Send(FileTransferOut transfer)
{
// send another part of the file
long remaining = transfer.Data.Length - transfer.SentOffset;
int sendByteCount = (remaining > chunkLen ? chunkLen : (int)remaining);
IWriteMessage message;
try
@@ -234,7 +241,7 @@ namespace Barotrauma.Networking
transfer.Status = FileTransferStatus.Sending;
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.Log("Sending file transfer initiation message: ");
DebugConsole.Log(" File: " + transfer.FileName);
@@ -246,28 +253,44 @@ namespace Barotrauma.Networking
return;
}
message = new WriteOnlyMessage();
message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
message.Write((byte)FileTransferMessageType.Data);
message.Write((byte)transfer.ID);
message.Write(transfer.SentOffset);
byte[] sendBytes = new byte[sendByteCount];
Array.Copy(transfer.Data, transfer.SentOffset, sendBytes, 0, sendByteCount);
message.Write((ushort)sendByteCount);
message.Write(sendBytes, 0, sendByteCount);
transfer.SentOffset += sendByteCount;
if (transfer.SentOffset > transfer.KnownReceivedOffset + chunkLen * 10 ||
transfer.SentOffset >= transfer.Data.Length)
for (int i = 0; i < Math.Floor(transfer.PacketsPerUpdate); i++)
{
transfer.SentOffset = transfer.KnownReceivedOffset;
transfer.WaitTimer = 0.5f;
}
long remaining = transfer.Data.Length - transfer.SentOffset;
int sendByteCount = (remaining > chunkLen ? chunkLen : (int)remaining);
message = new WriteOnlyMessage();
message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
message.Write((byte)FileTransferMessageType.Data);
peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
message.Write((byte)transfer.ID);
message.Write(transfer.SentOffset);
message.Write((ushort)sendByteCount);
int chunkDestPos = message.BytePosition;
message.BitPosition += sendByteCount * 8;
message.LengthBits = Math.Max(message.LengthBits, message.BitPosition);
Array.Copy(transfer.Data, transfer.SentOffset, message.Buffer, chunkDestPos, sendByteCount);
transfer.SentOffset += sendByteCount;
if (transfer.SentOffset >= transfer.Data.Length)
{
transfer.SentOffset = transfer.KnownReceivedOffset;
transfer.WaitTimer = 0.5f;
}
peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable, compressPastThreshold: false);
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.Log($"Sending {sendByteCount} bytes of the file {transfer.FileName} ({transfer.SentOffset / 1000}/{transfer.Data.Length / 1000} kB sent)");
}
//try to increase the packet rate so large files get sent faster,
//this gets reset when packet loss or disorder sets in
transfer.PacketsPerUpdate = Math.Min(FileTransferOut.MaxPacketsPerUpdate,
transfer.PacketsPerUpdate + 0.05f);
}
#if DEBUG
transfer.WaitTimer = Math.Max(transfer.WaitTimer, StallPacketsTime);
#endif
@@ -283,11 +306,6 @@ namespace Barotrauma.Networking
transfer.Status = FileTransferStatus.Error;
return;
}
if (GameSettings.VerboseLogging)
{
DebugConsole.Log($"Sending {sendByteCount} bytes of the file {transfer.FileName} ({transfer.SentOffset / 1000}/{transfer.Data.Length / 1000} kB sent)");
}
}
public void CancelTransfer(FileTransferOut transfer)
@@ -302,9 +320,9 @@ namespace Barotrauma.Networking
public void ReadFileRequest(IReadMessage inc, Client client)
{
byte messageType = inc.ReadByte();
FileTransferMessageType messageType = (FileTransferMessageType)inc.ReadByte();
if (messageType == (byte)FileTransferMessageType.Cancel)
if (messageType == FileTransferMessageType.Cancel)
{
byte transferId = inc.ReadByte();
var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId);
@@ -312,20 +330,28 @@ namespace Barotrauma.Networking
return;
}
else if (messageType == (byte)FileTransferMessageType.Data)
else if (messageType == FileTransferMessageType.Data)
{
byte transferId = inc.ReadByte();
var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId);
if (matchingTransfer != null)
{
matchingTransfer.Acknowledged = true;
int offset = inc.ReadInt32();
matchingTransfer.KnownReceivedOffset = offset > matchingTransfer.KnownReceivedOffset ? offset : matchingTransfer.KnownReceivedOffset;
int expecting = inc.ReadInt32(); //the offset the client is waiting for
int lastSeen = Math.Min(matchingTransfer.SentOffset, inc.ReadInt32()); //the last offset the client got from us
matchingTransfer.KnownReceivedOffset = Math.Max(expecting, matchingTransfer.KnownReceivedOffset);
if (matchingTransfer.SentOffset < matchingTransfer.KnownReceivedOffset)
{
matchingTransfer.WaitTimer = 0.0f;
matchingTransfer.SentOffset = matchingTransfer.KnownReceivedOffset;
}
if (lastSeen - matchingTransfer.KnownReceivedOffset >= chunkLen * 10 ||
matchingTransfer.SentOffset >= matchingTransfer.Data.Length)
{
matchingTransfer.SentOffset = matchingTransfer.KnownReceivedOffset;
matchingTransfer.WaitTimer = 0.5f;
}
if (matchingTransfer.KnownReceivedOffset >= matchingTransfer.Data.Length)
{
@@ -334,20 +360,20 @@ namespace Barotrauma.Networking
}
}
byte fileType = inc.ReadByte();
FileTransferType fileType = (FileTransferType)inc.ReadByte();
switch (fileType)
{
case (byte)FileTransferType.Submarine:
case FileTransferType.Submarine:
string fileName = inc.ReadString();
string fileHash = inc.ReadString();
var requestedSubmarine = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == fileName && s.MD5Hash.Hash == fileHash);
var requestedSubmarine = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == fileName && s.MD5Hash.StringRepresentation == fileHash);
if (requestedSubmarine != null)
{
StartTransfer(inc.Sender, FileTransferType.Submarine, requestedSubmarine.FilePath);
}
break;
case (byte)FileTransferType.CampaignSave:
case FileTransferType.CampaignSave:
if (GameMain.GameSession != null &&
!ActiveTransfers.Any(t => t.Connection == inc.Sender && t.FileType == FileTransferType.CampaignSave))
{
@@ -357,6 +383,23 @@ namespace Barotrauma.Networking
client.LastCampaignSaveSendTime = new Pair<ushort, float>(campaign.LastSaveID, (float)Lidgren.Network.NetTime.Now);
}
}
break;
case FileTransferType.Mod:
string modName = inc.ReadString();
Md5Hash modHash = Md5Hash.StringAsHash(inc.ReadString());
if (!GameMain.Server.ServerSettings.AllowModDownloads) { return; }
if (!(GameMain.Server.ModSender is { Ready: true })) { return; }
ContentPackage mod = ContentPackageManager.AllPackages.FirstOrDefault(p => p.Hash.Equals(modHash));
if (mod is null) { return; }
string modCompressedPath = ModSender.GetCompressedModPath(mod);
if (!File.Exists(modCompressedPath)) { return; }
StartTransfer(inc.Sender, FileTransferType.Mod, modCompressedPath);
break;
}
}
@@ -0,0 +1,55 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Barotrauma.Networking
{
class ModSender : IDisposable
{
public const string UploadFolder = "TempMods_Upload";
public const string Extension = ".barodir.gz";
public bool Ready { get; private set; } = false;
public ModSender()
{
DeleteDir();
Directory.CreateDirectory(UploadFolder);
TaskPool.Add(
"ModSender",
Task.WhenAll(
ContentPackageManager.EnabledPackages.All
.Where(p => p != ContentPackageManager.VanillaCorePackage && p.HasMultiplayerIncompatibleContent)
.Select(CompressMod)),
(t) => Ready = true);
}
public static string GetCompressedModPath(ContentPackage mod)
{
string dir = mod.Dir;
string resultFileName = dir.Replace('\\', '_').Replace('/', '_');
resultFileName = $"{resultFileName}{Extension}";
return Path.Combine(UploadFolder, resultFileName);
}
public async Task CompressMod(ContentPackage mod)
{
await Task.Yield();
string dir = mod.Dir;
SaveUtil.CompressDirectory(dir, GetCompressedModPath(mod), fileName => { });
}
private void DeleteDir()
{
if (Directory.Exists(UploadFolder)) { Directory.Delete(UploadFolder, recursive: true); }
}
public bool IsDisposed { get; private set; } = false;
public void Dispose()
{
IsDisposed = true;
DeleteDir();
}
}
}
@@ -7,6 +7,7 @@ using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
@@ -16,13 +17,9 @@ namespace Barotrauma.Networking
{
partial class GameServer : NetworkMember
{
public override bool IsServer
{
get { return true; }
}
public override bool IsServer => true;
private string serverName;
public string ServerName
{
get { return serverName; }
@@ -74,16 +71,14 @@ namespace Barotrauma.Networking
private readonly ServerEntityEventManager entityEventManager;
private FileSender fileSender;
public FileSender FileSender { get; private set; }
public FileSender FileSender
{
get { return fileSender; }
}
public ModSender ModSender { get; private set; }
#if DEBUG
public void PrintSenderTransters()
{
foreach (var transfer in fileSender.ActiveTransfers)
foreach (var transfer in FileSender.ActiveTransfers)
{
DebugConsole.NewMessage(transfer.FileName + " " + transfer.Progress.ToString());
}
@@ -167,9 +162,11 @@ namespace Barotrauma.Networking
serverPeer.OnShutdown = GameMain.Instance.CloseServer;
serverPeer.OnOwnerDetermined = OnOwnerDetermined;
fileSender = new FileSender(serverPeer, MsgConstants.MTU);
fileSender.OnEnded += FileTransferChanged;
fileSender.OnStarted += FileTransferChanged;
FileSender = new FileSender(serverPeer, MsgConstants.MTU);
FileSender.OnEnded += FileTransferChanged;
FileSender.OnStarted += FileTransferChanged;
if (serverSettings.AllowModDownloads) { ModSender = new ModSender(); }
serverPeer.Start();
@@ -344,7 +341,7 @@ namespace Barotrauma.Networking
base.Update(deltaTime);
fileSender.Update(deltaTime);
FileSender.Update(deltaTime);
KarmaManager.UpdateClients(ConnectedClients, deltaTime);
UpdatePing();
@@ -455,7 +452,7 @@ namespace Barotrauma.Networking
#if !DEBUG
if (endRoundTimer <= 0.0f)
{
SendChatMessage(TextManager.GetWithVariable("CrewDeadNoRespawns", "[time]", "60"), ChatMessageType.Server);
SendChatMessage(TextManager.GetWithVariable("CrewDeadNoRespawns", "[time]", "60").Value, ChatMessageType.Server);
}
endRoundDelay = 60.0f;
endRoundTimer += deltaTime;
@@ -645,10 +642,10 @@ namespace Barotrauma.Networking
if (registeredToMaster && (DateTime.Now > refreshMasterTimer || serverSettings.ServerDetailsChanged))
{
if (GameMain.Config.UseSteamMatchmaking)
if (GameSettings.CurrentConfig.UseSteamMatchmaking)
{
bool refreshSuccessful = SteamManager.RefreshServerDetails(this);
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
Log(refreshSuccessful ?
"Refreshed server info on the server list." :
@@ -668,7 +665,7 @@ namespace Barotrauma.Networking
if (Timing.TotalTime > lastPingTime + 1.0)
{
lastPingData ??= new byte[64];
for (int i=0;i<lastPingData.Length;i++)
for (int i = 0; i < lastPingData.Length; i++)
{
lastPingData[i] = (byte)Rand.Range(33, 126);
}
@@ -756,18 +753,18 @@ namespace Barotrauma.Networking
string subHash = inc.ReadString();
CampaignSettings settings = new CampaignSettings(inc);
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.StringRepresentation == subHash);
if (gameStarted)
{
SendDirectChatMessage(TextManager.Get("CampaignStartFailedRoundRunning"), connectedClient, ChatMessageType.MessageBox);
SendDirectChatMessage(TextManager.Get("CampaignStartFailedRoundRunning").Value, connectedClient, ChatMessageType.MessageBox);
return;
}
if (matchingSub == null)
{
SendDirectChatMessage(
TextManager.GetWithVariable("CampaignStartFailedSubNotFound", "[subname]", subName),
TextManager.GetWithVariable("CampaignStartFailedSubNotFound", "[subname]", subName).Value,
connectedClient, ChatMessageType.MessageBox);
}
else
@@ -787,7 +784,7 @@ namespace Barotrauma.Networking
string saveName = inc.ReadString();
if (gameStarted)
{
SendDirectChatMessage(TextManager.Get("CampaignStartFailedRoundRunning"), connectedClient, ChatMessageType.MessageBox);
SendDirectChatMessage(TextManager.Get("CampaignStartFailedRoundRunning").Value, connectedClient, ChatMessageType.MessageBox);
return;
}
if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign)) { MultiPlayerCampaign.LoadCampaign(saveName); }
@@ -829,7 +826,7 @@ namespace Barotrauma.Networking
case ClientPacketHeader.FILE_REQUEST:
if (serverSettings.AllowFileTransfers)
{
fileSender.ReadFileRequest(inc, connectedClient);
FileSender.ReadFileRequest(inc, connectedClient);
}
break;
case ClientPacketHeader.EVENTMANAGER_RESPONSE:
@@ -881,12 +878,15 @@ namespace Barotrauma.Networking
{
errorStr = errorStrNoName = $"Missing entity {entity}, sub: {entity.Submarine?.Info?.Name ?? "none"} (event id {eventID}, entity id {entityID}).";
}
var serverSubNames = Submarine.Loaded.Select(s => s.Info.Name);
if (subCount != Submarine.Loaded.Count || !subNames.SequenceEqual(serverSubNames))
if (gameStarted)
{
string subErrorStr = $" Loaded submarines don't match (client: {string.Join(", ", subNames)}, server: {string.Join(", ", serverSubNames)}).";
errorStr += subErrorStr;
errorStrNoName += subErrorStr;
var serverSubNames = Submarine.Loaded.Select(s => s.Info.Name);
if (subCount != Submarine.Loaded.Count || !subNames.SequenceEqual(serverSubNames))
{
string subErrorStr = $" Loaded submarines don't match (client: {string.Join(", ", subNames)}, server: {string.Join(", ", serverSubNames)}).";
errorStr += subErrorStr;
errorStrNoName += subErrorStr;
}
}
break;
}
@@ -922,7 +922,7 @@ namespace Barotrauma.Networking
Directory.CreateDirectory(ServerLog.SavePath);
}
string filePath = "event_error_log_server_" + client.Name + "_" + DateTime.UtcNow.ToShortTimeString() + ".log";
string filePath = $"event_error_log_server_{client.Name}_{DateTime.UtcNow.ToShortTimeString()}.log";
filePath = Path.Combine(ServerLog.SavePath, ToolBox.RemoveInvalidFileNameChars(filePath));
if (File.Exists(filePath)) { return; }
@@ -933,7 +933,7 @@ namespace Barotrauma.Networking
if (GameMain.GameSession?.GameMode != null)
{
errorLines.Add("Game mode: " + GameMain.GameSession.GameMode.Name);
errorLines.Add("Game mode: " + GameMain.GameSession.GameMode.Name.Value);
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign)
{
errorLines.Add("Campaign ID: " + campaign.CampaignID);
@@ -957,19 +957,18 @@ namespace Barotrauma.Networking
errorLines.Add("Level: " + Level.Loaded.Seed + ", " + string.Join(", ", Level.Loaded.EqualityCheckValues.Select(cv => cv.ToString("X"))));
errorLines.Add("Entity count before generating level: " + Level.Loaded.EntityCountBeforeGenerate);
errorLines.Add("Entities:");
foreach (Entity e in Level.Loaded.EntitiesBeforeGenerate)
foreach (Entity e in Level.Loaded.EntitiesBeforeGenerate.OrderBy(e => e.CreationIndex))
{
errorLines.Add(" " + e.ID + ": " + e.ToString());
errorLines.Add(e.ErrorLine);
}
errorLines.Add("Entity count after generating level: " + Level.Loaded.EntityCountAfterGenerate);
}
errorLines.Add("Entity IDs:");
List<Entity> sortedEntities = Entity.GetEntities().ToList();
sortedEntities.Sort((e1, e2) => e1.ID.CompareTo(e2.ID));
Entity[] sortedEntities = Entity.GetEntities().OrderBy(e => e.CreationIndex).ToArray();
foreach (Entity e in sortedEntities)
{
errorLines.Add(e.ID + ": " + e.ToString());
errorLines.Add(e.ErrorLine);
}
errorLines.Add("");
@@ -1160,7 +1159,7 @@ namespace Barotrauma.Networking
{
c.LastRecvChatMsgID = lastRecvChatMsgID;
}
else if (lastRecvChatMsgID != c.LastRecvChatMsgID && GameSettings.VerboseLogging)
else if (lastRecvChatMsgID != c.LastRecvChatMsgID && GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.ThrowError(
"Invalid lastRecvChatMsgID " + lastRecvChatMsgID +
@@ -1179,8 +1178,13 @@ namespace Barotrauma.Networking
}
c.LastRecvEntityEventID = lastRecvEntityEventID;
#warning TODO: remove this later
/*if (!CoroutineManager.IsCoroutineRunning("RoundRestartLoop"))
{
CoroutineManager.StartCoroutine(RoundRestartLoop(), "RoundRestartLoop");
}*/
}
else if (lastRecvEntityEventID != c.LastRecvEntityEventID && GameSettings.VerboseLogging)
else if (lastRecvEntityEventID != c.LastRecvEntityEventID && GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.ThrowError(
"Invalid lastRecvEntityEventID " + lastRecvEntityEventID +
@@ -1224,6 +1228,16 @@ namespace Barotrauma.Networking
}
}
#warning TODO: remove this later
/*private IEnumerable<object> RoundRestartLoop()
{
yield return new WaitForSeconds(8.0f);
EndGame();
yield return new WaitForSeconds(8.0f);
StartGame();
yield return CoroutineStatus.Success;
}*/
private void ReadCrewMessage(IReadMessage inc, Client sender)
{
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign mpCampaign)
@@ -1304,7 +1318,7 @@ namespace Barotrauma.Networking
}
else
{
SendDirectChatMessage(TextManager.GetServerMessage($"ServerMessage.PlayerNotFound~[player]={kickedName}"), sender, ChatMessageType.Console);
SendDirectChatMessage(TextManager.GetServerMessage($"ServerMessage.PlayerNotFound~[player]={kickedName}").Value, sender, ChatMessageType.Console);
}
break;
case ClientPermissions.Ban:
@@ -1332,7 +1346,7 @@ namespace Barotrauma.Networking
}
else
{
SendDirectChatMessage(TextManager.GetServerMessage($"ServerMessage.PlayerNotFound~[player]={bannedName}"), sender, ChatMessageType.Console);
SendDirectChatMessage(TextManager.GetServerMessage($"ServerMessage.PlayerNotFound~[player]={bannedName}").Value, sender, ChatMessageType.Console);
}
}
break;
@@ -1433,9 +1447,9 @@ namespace Barotrauma.Networking
case ClientPermissions.SelectMode:
UInt16 modeIndex = inc.ReadUInt16();
GameMain.NetLobbyScreen.SelectedModeIndex = modeIndex;
Log("Gamemode changed to " + GameMain.NetLobbyScreen.GameModes[GameMain.NetLobbyScreen.SelectedModeIndex].Name, ServerLog.MessageType.ServerMessage);
Log("Gamemode changed to " + GameMain.NetLobbyScreen.GameModes[GameMain.NetLobbyScreen.SelectedModeIndex].Name.Value, ServerLog.MessageType.ServerMessage);
if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier.Equals("multiplayercampaign", StringComparison.OrdinalIgnoreCase))
if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier == "multiplayercampaign")
{
string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer, includeInCompatible: false).ToArray();
for (int i = 0; i < saveFiles.Length; i++)
@@ -1446,9 +1460,9 @@ namespace Barotrauma.Networking
saveFiles[i] =
string.Join(";",
saveFiles[i].Replace(';', ' '),
doc.Root.GetAttributeString("submarine", ""),
doc.Root.GetAttributeString("savetime", ""),
doc.Root.GetAttributeString("selectedcontentpackages", ""));
doc.Root.GetAttributeStringUnrestricted("submarine", ""),
doc.Root.GetAttributeStringUnrestricted("savetime", ""),
doc.Root.GetAttributeStringUnrestricted("selectedcontentpackages", ""));
}
}
@@ -1543,9 +1557,9 @@ namespace Barotrauma.Networking
}
}
if (!fileSender.ActiveTransfers.Any(t => t.Connection == c.Connection && t.FileType == FileTransferType.CampaignSave))
if (!FileSender.ActiveTransfers.Any(t => t.Connection == c.Connection && t.FileType == FileTransferType.CampaignSave))
{
fileSender.StartTransfer(c.Connection, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
FileSender.StartTransfer(c.Connection, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
c.LastCampaignSaveSendTime = new Pair<ushort, float>(campaign.LastSaveID, (float)NetTime.Now);
}
}
@@ -1556,7 +1570,7 @@ namespace Barotrauma.Networking
/// </summary>
private void ClientWriteInitial(Client c, IWriteMessage outmsg)
{
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.NewMessage("Sending initial lobby update", Color.Gray);
}
@@ -1701,15 +1715,15 @@ namespace Barotrauma.Networking
{
var entity = c.PendingPositionUpdates.Peek();
if (entity == null || entity.Removed ||
(entity is Item item && item.PositionUpdateInterval == float.PositiveInfinity))
(entity is Item item && float.IsInfinity(item.PositionUpdateInterval)))
{
c.PendingPositionUpdates.Dequeue();
continue;
}
IWriteMessage tempBuffer = new ReadWriteMessage();
tempBuffer.Write((byte)ServerNetObject.ENTITY_POSITION);
tempBuffer.Write(entity is Item);
tempBuffer.Write(entity is Item); tempBuffer.WritePadBits();
tempBuffer.Write(entity is MapEntity me ? me.Prefab.UintIdentifier : (UInt32)0);
if (entity is Item)
{
((Item)entity).ServerWritePosition(tempBuffer, c);
@@ -1725,6 +1739,8 @@ namespace Barotrauma.Networking
break;
}
outmsg.Write((byte)ServerNetObject.ENTITY_POSITION);
outmsg.WritePadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
outmsg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
outmsg.WritePadBits();
@@ -1805,7 +1821,7 @@ namespace Barotrauma.Networking
outmsg.Write(client.SteamID);
outmsg.Write(client.NameID);
outmsg.Write(client.Name);
outmsg.Write(client.Character?.Info?.Job != null && gameStarted ? client.Character.Info.Job.Prefab.Identifier : (client.PreferredJob ?? ""));
outmsg.Write(client.Character?.Info?.Job != null && gameStarted ? client.Character.Info.Job.Prefab.Identifier : client.PreferredJob);
outmsg.Write((byte)client.PreferredTeam);
outmsg.Write(client.Character == null || !gameStarted ? (ushort)0 : client.Character.ID);
if (c.HasPermission(ClientPermissions.ServerLog))
@@ -1953,7 +1969,7 @@ namespace Barotrauma.Networking
#if DEBUG || UNSTABLE
DebugConsole.ThrowError(warningMsg);
#else
if (GameSettings.VerboseLogging) { DebugConsole.AddWarning(warningMsg); }
if (GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.AddWarning(warningMsg); }
#endif
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:ClientWriteLobby" + outmsg.LengthBytes, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
}
@@ -2043,11 +2059,11 @@ namespace Barotrauma.Networking
msg.Write((byte)ServerPacketHeader.QUERY_STARTGAME);
msg.Write(selectedSub.Name);
msg.Write(selectedSub.MD5Hash.Hash);
msg.Write(selectedSub.MD5Hash.StringRepresentation);
msg.Write(serverSettings.UseRespawnShuttle || (gameStarted && respawnManager.UsingShuttle));
msg.Write(selectedShuttle.Name);
msg.Write(selectedShuttle.MD5Hash.Hash);
msg.Write(selectedShuttle.MD5Hash.StringRepresentation);
var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
msg.Write(campaign == null ? (byte)0 : campaign.CampaignID);
@@ -2069,10 +2085,10 @@ namespace Barotrauma.Networking
yield return CoroutineStatus.Running;
}
if (fileSender.ActiveTransfers.Count > 0)
if (FileSender.ActiveTransfers.Count > 0)
{
float waitForTransfersTimer = 20.0f;
while (fileSender.ActiveTransfers.Count > 0 && waitForTransfersTimer > 0.0f)
while (FileSender.ActiveTransfers.Count > 0 && waitForTransfersTimer > 0.0f)
{
waitForTransfersTimer -= CoroutineManager.UnscaledDeltaTime;
yield return CoroutineStatus.Running;
@@ -2156,7 +2172,7 @@ namespace Barotrauma.Networking
GameMain.GameSession.StartRound(campaign.NextLevel, mirrorLevel: campaign.MirrorLevel);
SubmarineSwitchLoad = false;
campaign.AssignClientCharacterInfos(connectedClients);
Log("Game mode: " + selectedMode.Name, ServerLog.MessageType.ServerMessage);
Log("Game mode: " + selectedMode.Name.Value, ServerLog.MessageType.ServerMessage);
Log("Submarine: " + GameMain.GameSession.SubmarineInfo.Name, ServerLog.MessageType.ServerMessage);
Log("Level seed: " + campaign.NextLevel.Seed, ServerLog.MessageType.ServerMessage);
}
@@ -2164,14 +2180,14 @@ namespace Barotrauma.Networking
{
SendStartMessage(roundStartSeed, GameMain.NetLobbyScreen.LevelSeed, GameMain.GameSession, connectedClients, false);
GameMain.GameSession.StartRound(GameMain.NetLobbyScreen.LevelSeed, serverSettings.SelectedLevelDifficulty);
Log("Game mode: " + selectedMode.Name, ServerLog.MessageType.ServerMessage);
Log("Game mode: " + selectedMode.Name.Value, ServerLog.MessageType.ServerMessage);
Log("Submarine: " + selectedSub.Name, ServerLog.MessageType.ServerMessage);
Log("Level seed: " + GameMain.NetLobbyScreen.LevelSeed, ServerLog.MessageType.ServerMessage);
}
foreach (Mission mission in GameMain.GameSession.Missions)
{
Log("Mission: " + mission.Prefab.Name, ServerLog.MessageType.ServerMessage);
Log("Mission: " + mission.Prefab.Name.Value, ServerLog.MessageType.ServerMessage);
}
if (GameMain.GameSession.SubmarineInfo.IsFileCorrupted)
@@ -2256,9 +2272,9 @@ namespace Barotrauma.Networking
client.CharacterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, client.Name);
}
characterInfos.Add(client.CharacterInfo);
if (client.CharacterInfo.Job == null || client.CharacterInfo.Job.Prefab != client.AssignedJob.First)
if (client.CharacterInfo.Job == null || client.CharacterInfo.Job.Prefab != client.AssignedJob.Prefab)
{
client.CharacterInfo.Job = new Job(client.AssignedJob.First, Rand.RandSync.Unsynced, client.AssignedJob.Second);
client.CharacterInfo.Job = new Job(client.AssignedJob.Prefab, Rand.RandSync.Unsynced, client.AssignedJob.Variant);
}
}
@@ -2305,7 +2321,7 @@ namespace Barotrauma.Networking
wp.SpawnType == SpawnType.Human &&
wp.Submarine == Level.Loaded.StartOutpost &&
wp.CurrentHull?.OutpostModuleTags != null &&
wp.CurrentHull.OutpostModuleTags.Contains("airlock"));
wp.CurrentHull.OutpostModuleTags.Contains("airlock".ToIdentifier()));
while (spawnWaypoints.Count > characterInfos.Count)
{
spawnWaypoints.RemoveAt(Rand.Int(spawnWaypoints.Count));
@@ -2481,14 +2497,14 @@ namespace Barotrauma.Networking
msg.Write(levelSeed);
msg.Write(serverSettings.SelectedLevelDifficulty);
msg.Write(gameSession.SubmarineInfo.Name);
msg.Write(gameSession.SubmarineInfo.MD5Hash.Hash);
msg.Write(gameSession.SubmarineInfo.MD5Hash.StringRepresentation);
var selectedShuttle = gameStarted && respawnManager.UsingShuttle ? respawnManager.RespawnShuttle.Info : GameMain.NetLobbyScreen.SelectedShuttle;
msg.Write(selectedShuttle.Name);
msg.Write(selectedShuttle.MD5Hash.Hash);
msg.Write(selectedShuttle.MD5Hash.StringRepresentation);
msg.Write((byte)GameMain.GameSession.GameMode.Missions.Count());
foreach (Mission mission in GameMain.GameSession.GameMode.Missions)
{
msg.Write((short)MissionPrefab.List.IndexOf(mission.Prefab));
msg.Write(mission.Prefab.UintIdentifier);
}
}
else
@@ -2526,8 +2542,7 @@ namespace Barotrauma.Networking
msg.Write((ushort)contentToPreload.Count());
foreach (ContentFile contentFile in contentToPreload)
{
msg.Write((byte)contentFile.Type);
msg.Write(contentFile.Path);
msg.Write(contentFile.Path.Value);
}
msg.Write(Submarine.MainSub?.Info.EqualityCheckVal ?? 0);
msg.Write((byte)GameMain.GameSession.Missions.Count());
@@ -2555,7 +2570,7 @@ namespace Barotrauma.Networking
return;
}
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
Log("Ending the round...\n" + Environment.StackTrace.CleanupStackTrace(), ServerLog.MessageType.ServerMessage);
@@ -2664,7 +2679,7 @@ namespace Barotrauma.Networking
{
UInt16 nameId = inc.ReadUInt16();
string newName = inc.ReadString();
string newJob = inc.ReadString();
Identifier newJob = inc.ReadIdentifier();
CharacterTeamType newTeam = (CharacterTeamType)inc.ReadByte();
if (c == null || string.IsNullOrEmpty(newName) || !NetIdUtils.IdMoreRecent(nameId, c.NameID)) { return false; }
@@ -3150,7 +3165,7 @@ namespace Barotrauma.Networking
if (type.Value != ChatMessageType.MessageBox)
{
string myReceivedMessage = type == ChatMessageType.Server || type == ChatMessageType.Error ? TextManager.GetServerMessage(message) : message;
string myReceivedMessage = type == ChatMessageType.Server || type == ChatMessageType.Error ? TextManager.GetServerMessage(message).Value : message;
if (!string.IsNullOrWhiteSpace(myReceivedMessage))
{
AddChatMessage(myReceivedMessage, (ChatMessageType)type, senderName, senderClient, senderCharacter);
@@ -3169,11 +3184,11 @@ namespace Barotrauma.Networking
//too far to hear the msg -> don't send
if (!client.Character.CanHearCharacter(message.Sender)) { continue; }
}
SendDirectChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, message.TargetEntity, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder), client);
SendDirectChatMessage(new OrderChatMessage(message.Order, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder), client);
}
if (!string.IsNullOrWhiteSpace(message.Text))
{
AddChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, message.Text, message.TargetEntity, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder));
AddChatMessage(new OrderChatMessage(message.Order, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder));
}
}
@@ -3355,9 +3370,8 @@ namespace Barotrauma.Networking
serverPeer.Send(msg, recipient.Connection, DeliveryMethod.Reliable);
}
public void GiveAchievement(Character character, string achievementIdentifier)
public void GiveAchievement(Character character, Identifier achievementIdentifier)
{
achievementIdentifier = achievementIdentifier.ToLowerInvariant();
foreach (Client client in connectedClients)
{
if (client.Character == character)
@@ -3368,9 +3382,8 @@ namespace Barotrauma.Networking
}
}
public void IncrementStat(Character character, string achievementIdentifier, int amount)
public void IncrementStat(Character character, Identifier achievementIdentifier, int amount)
{
achievementIdentifier = achievementIdentifier.ToLowerInvariant();
foreach (Client client in connectedClients)
{
if (client.Character == character)
@@ -3381,7 +3394,7 @@ namespace Barotrauma.Networking
}
}
public void GiveAchievement(Client client, string achievementIdentifier)
public void GiveAchievement(Client client, Identifier achievementIdentifier)
{
if (client.GivenAchievements.Contains(achievementIdentifier)) { return; }
client.GivenAchievements.Add(achievementIdentifier);
@@ -3394,7 +3407,7 @@ namespace Barotrauma.Networking
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
public void IncrementStat(Client client, string achievementIdentifier, int amount)
public void IncrementStat(Client client, Identifier achievementIdentifier, int amount)
{
if (client.GivenAchievements.Contains(achievementIdentifier)) { return; }
@@ -3406,13 +3419,13 @@ namespace Barotrauma.Networking
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
public void SendTraitorMessage(Client client, string message, string missionIdentifier, TraitorMessageType messageType)
public void SendTraitorMessage(Client client, string message, Identifier missionIdentifier, TraitorMessageType messageType)
{
if (client == null) { return; }
var msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.TRAITOR_MESSAGE);
msg.Write((byte)messageType);
msg.Write(missionIdentifier ?? "");
msg.Write(missionIdentifier);
msg.Write(message);
serverPeer.Send(msg, client.Connection, DeliveryMethod.ReliableOrdered);
}
@@ -3484,18 +3497,11 @@ namespace Barotrauma.Networking
return;
}
Gender gender = Gender.Male;
Race race = Race.White;
int headSpriteId = 0;
try
int tagCount = message.ReadByte();
HashSet<Identifier> tagSet = new HashSet<Identifier>();
for (int i = 0; i < tagCount; i++)
{
gender = (Gender)message.ReadByte();
race = (Race)message.ReadByte();
headSpriteId = message.ReadByte();
}
catch (Exception e)
{
DebugConsole.Log("Received invalid characterinfo from \"" + sender.Name + "\"! { " + e.Message + " }");
tagSet.Add(message.ReadIdentifier());
}
int hairIndex = message.ReadByte();
int beardIndex = message.ReadByte();
@@ -3505,7 +3511,7 @@ namespace Barotrauma.Networking
Color hairColor = message.ReadColorR8G8B8();
Color facialHairColor = message.ReadColorR8G8B8();
List<Pair<JobPrefab, int>> jobPreferences = new List<Pair<JobPrefab, int>>();
List<JobVariant> jobPreferences = new List<JobVariant>();
int count = message.ReadByte();
// TODO: modding support?
for (int i = 0; i < Math.Min(count, 3); i++)
@@ -3514,15 +3520,15 @@ namespace Barotrauma.Networking
int variant = message.ReadByte();
if (JobPrefab.Prefabs.ContainsKey(jobIdentifier))
{
jobPreferences.Add(new Pair<JobPrefab, int>(JobPrefab.Prefabs[jobIdentifier], variant));
jobPreferences.Add(new JobVariant(JobPrefab.Prefabs[jobIdentifier], variant));
}
}
sender.CharacterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, sender.Name);
sender.CharacterInfo.RecreateHead(headSpriteId, race, gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
sender.CharacterInfo.SkinColor = skinColor;
sender.CharacterInfo.HairColor = hairColor;
sender.CharacterInfo.FacialHairColor = facialHairColor;
sender.CharacterInfo.RecreateHead(tagSet.ToImmutableHashSet(), hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
sender.CharacterInfo.Head.SkinColor = skinColor;
sender.CharacterInfo.Head.HairColor = hairColor;
sender.CharacterInfo.Head.FacialHairColor = facialHairColor;
if (jobPreferences.Count > 0)
{
@@ -3556,7 +3562,7 @@ namespace Barotrauma.Networking
foreach (KeyValuePair<Client, Job> clientJob in campaignAssigned)
{
assignedClientCount[clientJob.Value.Prefab]++;
clientJob.Key.AssignedJob = new Pair<JobPrefab, int>(clientJob.Value.Prefab, clientJob.Value.Variant);
clientJob.Key.AssignedJob = new JobVariant(clientJob.Value.Prefab, clientJob.Value.Variant);
}
}
@@ -3574,7 +3580,7 @@ namespace Barotrauma.Networking
for (int i = unassigned.Count - 1; i >= 0; i--)
{
if (unassigned[i].JobPreferences.Count == 0) { continue; }
if (!unassigned[i].JobPreferences.Any() || !unassigned[i].JobPreferences[0].First.AllowAlways) { continue; }
if (!unassigned[i].JobPreferences.Any() || !unassigned[i].JobPreferences[0].Prefab.AllowAlways) { continue; }
unassigned[i].AssignedJob = unassigned[i].JobPreferences[0];
unassigned.RemoveAt(i);
}
@@ -3611,8 +3617,8 @@ namespace Barotrauma.Networking
void AssignJob(Client client, JobPrefab jobPrefab)
{
client.AssignedJob =
client.JobPreferences.FirstOrDefault(jp => jp.First == jobPrefab) ??
new Pair<JobPrefab, int>(jobPrefab, Rand.Int(jobPrefab.Variants));
client.JobPreferences.FirstOrDefault(jp => jp.Prefab == jobPrefab) ??
new JobVariant(jobPrefab, Rand.Int(jobPrefab.Variants));
assignedClientCount[jobPrefab]++;
unassigned.Remove(client);
@@ -3657,7 +3663,7 @@ namespace Barotrauma.Networking
Client client = unassigned[i];
if (preferenceIndex >= client.JobPreferences.Count) { continue; }
var preferredJob = client.JobPreferences[preferenceIndex];
JobPrefab jobPrefab = preferredJob.First;
JobPrefab jobPrefab = preferredJob.Prefab;
if (assignedClientCount[jobPrefab] >= jobPrefab.MaxNumber || client.Karma < jobPrefab.MinKarma)
{
//can't assign this job if maximum number has reached or the clien't karma is too low
@@ -3690,24 +3696,24 @@ namespace Barotrauma.Networking
if (skips >= jobList.Count) { break; }
}
c.AssignedJob =
c.JobPreferences.FirstOrDefault(jp => jp.First == jobList[jobIndex]) ??
new Pair<JobPrefab, int>(jobList[jobIndex], 0);
assignedClientCount[c.AssignedJob.First]++;
c.JobPreferences.FirstOrDefault(jp => jp.Prefab == jobList[jobIndex]) ??
new JobVariant(jobList[jobIndex], 0);
assignedClientCount[c.AssignedJob.Prefab]++;
}
//if one of the client's preferences is still available, give them that job
else if (c.JobPreferences.Any(jp => remainingJobs.Contains(jp.First)))
else if (c.JobPreferences.Any(jp => remainingJobs.Contains(jp.Prefab)))
{
foreach (Pair<JobPrefab, int> preferredJob in c.JobPreferences)
foreach (JobVariant preferredJob in c.JobPreferences)
{
c.AssignedJob = preferredJob;
assignedClientCount[preferredJob.First]++;
assignedClientCount[preferredJob.Prefab]++;
break;
}
}
else //none of the client's preferred jobs available, choose a random job
{
c.AssignedJob = new Pair<JobPrefab, int>(remainingJobs[Rand.Range(0, remainingJobs.Count)], 0);
assignedClientCount[c.AssignedJob.First]++;
c.AssignedJob = new JobVariant(remainingJobs[Rand.Range(0, remainingJobs.Count)], 0);
assignedClientCount[c.AssignedJob.Prefab]++;
}
}
}
@@ -3751,11 +3757,11 @@ namespace Barotrauma.Networking
{
if (unassignedBots.Count == 0) { break; }
JobPrefab jobPrefab = spawnPoint.AssignedJob ?? JobPrefab.Prefabs.GetRandom();
JobPrefab jobPrefab = spawnPoint.AssignedJob ?? JobPrefab.Prefabs.GetRandomUnsynced();
if (assignedPlayerCount[jobPrefab] >= jobPrefab.MaxNumber) { continue; }
var variant = Rand.Range(0, jobPrefab.Variants, Rand.RandSync.Server);
unassignedBots[0].Job = new Job(jobPrefab, Rand.RandSync.Server, variant);
var variant = Rand.Range(0, jobPrefab.Variants, Rand.RandSync.ServerAndClient);
unassignedBots[0].Job = new Job(jobPrefab, Rand.RandSync.ServerAndClient, variant);
assignedPlayerCount[jobPrefab]++;
unassignedBots.Remove(unassignedBots[0]);
canAssign = true;
@@ -3768,15 +3774,16 @@ namespace Barotrauma.Networking
//find all jobs that are still available
var remainingJobs = JobPrefab.Prefabs.Where(jp => assignedPlayerCount[jp] < jp.MaxNumber);
//all jobs taken, give a random job
if (remainingJobs.Count() == 0)
if (remainingJobs.None())
{
DebugConsole.ThrowError("Failed to assign a suitable job for bot \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
c.Job = Job.Random();
#warning TODO: is this randsync correct?
c.Job = Job.Random(Rand.RandSync.ServerAndClient);
assignedPlayerCount[c.Job.Prefab]++;
}
else //some jobs still left, choose one of them by random
{
var job = remainingJobs.GetRandom();
var job = remainingJobs.GetRandomUnsynced();
var variant = Rand.Range(0, job.Variants);
c.Job = new Job(job, Rand.RandSync.Unsynced, variant);
assignedPlayerCount[c.Job.Prefab]++;
@@ -3791,7 +3798,7 @@ namespace Barotrauma.Networking
foreach (Client c in clients)
{
if (ServerSettings.KarmaEnabled && c.Karma < job.MinKarma) { continue; }
int index = c.JobPreferences.IndexOf(c.JobPreferences.Find(j => j.First == job));
int index = c.JobPreferences.IndexOf(c.JobPreferences.Find(j => j.Prefab == job));
if (index > -1 && index < bestPreference)
{
bestPreference = index;
@@ -3867,6 +3874,8 @@ namespace Barotrauma.Networking
serverSettings.SaveSettings();
ModSender.Dispose();
if (serverSettings.SaveServerLogs)
{
Log("Shutting down the server...", ServerLog.MessageType.ServerMessage);
@@ -128,7 +128,7 @@ namespace Barotrauma
clientMemory.PreviousNotifiedKarma >= KickBanThreshold + KarmaNotificationInterval &&
client.Karma < KickBanThreshold + KarmaNotificationInterval)
{
GameMain.Server.SendDirectChatMessage(TextManager.Get("KarmaBanWarning"), client);
GameMain.Server.SendDirectChatMessage(TextManager.Get("KarmaBanWarning").Value, client);
GameServer.Log(GameServer.ClientLogName(client) + " has been warned for having dangerously low karma.", ServerLog.MessageType.Karma);
clientMemory.PreviousNotifiedKarma = client.Karma;
clientMemory.PreviousKarmaNotificationTime = Timing.TotalTime;
@@ -170,7 +170,7 @@ namespace Barotrauma
existingAffliction.Strength = herpesStrength;
if (herpesStrength <= 0.0f)
{
client.Character.CharacterHealth.ReduceAffliction(null, "invertcontrols", 100.0f);
client.Character.CharacterHealth.ReduceAfflictionOnAllLimbs("invertcontrols".ToIdentifier(), 100.0f);
}
}
@@ -283,7 +283,7 @@ namespace Barotrauma
if (foundItem == null) { return; }
bool isIdCard = foundItem.prefab.Identifier == "idcard";
bool isIdCard = ((MapEntity)foundItem).Prefab.Identifier == "idcard";
bool isWeapon = foundItem.GetComponent<RangedWeapon>() != null || foundItem.GetComponent<MeleeWeapon>() != null;
if (isIdCard)
@@ -394,8 +394,8 @@ namespace Barotrauma
}
//attacking/healing clowns has a smaller effect on karma
if (target.HasEquippedItem("clownmask") &&
target.HasEquippedItem("clowncostume"))
if (target.HasEquippedItem("clownmask".ToIdentifier()) &&
target.HasEquippedItem("clowncostume".ToIdentifier()))
{
damage *= 0.5f;
stun *= 0.5f;
@@ -604,8 +604,8 @@ namespace Barotrauma
if (client == null) { return; }
//all penalties/rewards are halved when wearing a clown costume
if (target.HasEquippedItem("clownmask") &&
target.HasEquippedItem("clowncostume"))
if (target.HasEquippedItem("clownmask".ToIdentifier()) &&
target.HasEquippedItem("clowncostume".ToIdentifier()))
{
amount *= 0.5f;
}
@@ -179,7 +179,7 @@ namespace Barotrauma.Networking
catch (Exception e)
{
string entityName = bufferedEvent.TargetEntity == null ? "null" : bufferedEvent.TargetEntity.ToString();
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
string errorMsg = "Failed to read server event for entity \"" + entityName + "\"!";
GameServer.Log(errorMsg + "\n" + e.StackTrace.CleanupStackTrace(), ServerLog.MessageType.Error);
@@ -347,7 +347,7 @@ namespace Barotrauma.Networking
count++;
if (count > 3) { break; }
}
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
GameServer.Log(warningMsg, ServerLog.MessageType.Error);
}
@@ -482,7 +482,7 @@ namespace Barotrauma.Networking
//skip the event if we've already received it
if (thisEventID != (UInt16)(sender.LastSentEntityEventID + 1))
{
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.NewMessage("Received msg " + thisEventID + ", expecting " + sender.LastSentEntityEventID, Color.Red);
}
@@ -493,7 +493,7 @@ namespace Barotrauma.Networking
//entity not found -> consider the even read and skip over it
//(can happen, for example, when a client uses a medical item repeatedly
//and creates an event for it before receiving the event about it being removed)
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.NewMessage(
"Received msg " + thisEventID + ", entity " + entityID + " not found",
@@ -504,7 +504,7 @@ namespace Barotrauma.Networking
}
else
{
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.NewMessage("Received msg " + thisEventID, Microsoft.Xna.Framework.Color.Green);
}
@@ -129,7 +129,7 @@ namespace Barotrauma.Networking
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#else
if (GameSettings.VerboseLogging) { DebugConsole.ThrowError(errorMsg); }
if (GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.ThrowError(errorMsg); }
#endif
}
@@ -326,7 +326,7 @@ namespace Barotrauma.Networking
}
}
public override void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod)
public override void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true)
{
if (netServer == null) { return; }
@@ -353,7 +353,7 @@ namespace Barotrauma.Networking
NetOutgoingMessage lidgrenMsg = netServer.CreateMessage();
byte[] msgData = new byte[msg.LengthBytes];
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
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);
@@ -422,7 +422,7 @@ namespace Barotrauma.Networking
{
if (pendingClient.SteamID == null)
{
bool requireSteamAuth = GameMain.Config.RequireSteamAuthentication;
bool requireSteamAuth = GameSettings.CurrentConfig.RequireSteamAuthentication;
#if DEBUG
requireSteamAuth = false;
#endif
@@ -123,7 +123,7 @@ namespace Barotrauma.Networking
return;
}
string language = inc.ReadString();
LanguageIdentifier language = inc.ReadIdentifier().ToLanguageIdentifier();
pendingClient.Connection.Language = language;
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
@@ -246,12 +246,14 @@ namespace Barotrauma.Networking
case ConnectionInitialization.ContentPackageOrder:
outMsg.Write(GameMain.Server.ServerName);
var mpContentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
var mpContentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerIncompatibleContent).ToList();
outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count);
for (int i = 0; i < mpContentPackages.Count; i++)
{
outMsg.Write(mpContentPackages[i].Name);
outMsg.Write(mpContentPackages[i].MD5hash.Hash);
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);
@@ -294,7 +296,7 @@ namespace Barotrauma.Networking
}
}
public abstract void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod);
public abstract void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true);
public abstract void Disconnect(NetworkConnection conn, string msg = null);
}
}
@@ -106,7 +106,7 @@ namespace Barotrauma.Networking
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#else
if (GameSettings.VerboseLogging) { DebugConsole.ThrowError(errorMsg); }
if (GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.ThrowError(errorMsg); }
#endif
}
@@ -223,7 +223,7 @@ namespace Barotrauma.Networking
string ownerName = inc.ReadString();
OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID)
{
Language = GameMain.Config.Language
Language = GameSettings.CurrentConfig.Language
};
OwnerConnection.SetOwnerSteamIDIfUnknown(OwnerSteamID);
@@ -250,7 +250,7 @@ namespace Barotrauma.Networking
throw new InvalidOperationException("Called InitializeSteamServerCallbacks on SteamP2PServerPeer!");
}
public override void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod)
public override void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true)
{
if (!started) { return; }
@@ -263,7 +263,7 @@ namespace Barotrauma.Networking
IWriteMessage msgToSend = new WriteOnlyMessage();
byte[] msgData = new byte[16];
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
msgToSend.Write(conn.SteamID);
msgToSend.Write((byte)deliveryMethod);
msgToSend.Write((byte)((isCompressed ? PacketHeader.IsCompressed : PacketHeader.None) | PacketHeader.IsServerMessage));
@@ -227,7 +227,7 @@ namespace Barotrauma.Networking
}
var shuttleGaps = Gap.GapList.FindAll(g => g.Submarine == RespawnShuttle && g.ConnectedWall != null);
shuttleGaps.ForEach(g => Spawner.AddToRemoveQueue(g));
shuttleGaps.ForEach(g => Spawner.AddEntityToRemoveQueue(g));
var dockingPorts = Item.ItemList.FindAll(i => i.Submarine == RespawnShuttle && i.GetComponent<DockingPort>() != null);
dockingPorts.ForEach(d => d.GetComponent<DockingPort>().Undock());
@@ -355,7 +355,7 @@ namespace Barotrauma.Networking
{
if (campaign?.GetClientCharacterData(c) == null || c.CharacterInfo.Job == null)
{
c.CharacterInfo.Job = new Job(c.AssignedJob.First, Rand.RandSync.Unsynced, c.AssignedJob.Second);
c.CharacterInfo.Job = new Job(c.AssignedJob.Prefab, Rand.RandSync.Unsynced, c.AssignedJob.Variant);
}
}
@@ -369,17 +369,17 @@ namespace Barotrauma.Networking
if ((shuttlePos != null && Level.Loaded.GetRealWorldDepth(shuttlePos.Value.Y) > Level.DefaultRealWorldCrushDepth) ||
Level.Loaded.GetRealWorldDepth(Submarine.MainSub.WorldPosition.Y) > Level.DefaultRealWorldCrushDepth)
{
divingSuitPrefab = ItemPrefab.Prefabs.FirstOrDefault(it => it.Tags.Any(t => t.Equals("respawnsuitdeep", StringComparison.OrdinalIgnoreCase)));
divingSuitPrefab = ItemPrefab.Prefabs.FirstOrDefault(it => it.Tags.Any(t => t == "respawnsuitdeep"));
}
if (divingSuitPrefab == null)
{
divingSuitPrefab =
ItemPrefab.Prefabs.FirstOrDefault(it => it.Tags.Any(t => t.Equals("respawnsuit", StringComparison.OrdinalIgnoreCase))) ??
ItemPrefab.Find(null, "divingsuit");
ItemPrefab.Prefabs.FirstOrDefault(it => it.Tags.Any(t => t == "respawnsuit")) ??
ItemPrefab.Find(null, "divingsuit".ToIdentifier());
}
ItemPrefab oxyPrefab = ItemPrefab.Find(null, "oxygentank");
ItemPrefab scooterPrefab = ItemPrefab.Find(null, "underwaterscooter");
ItemPrefab batteryPrefab = ItemPrefab.Find(null, "batterycell");
ItemPrefab oxyPrefab = ItemPrefab.Find(null, "oxygentank".ToIdentifier());
ItemPrefab scooterPrefab = ItemPrefab.Find(null, "underwaterscooter".ToIdentifier());
ItemPrefab batteryPrefab = ItemPrefab.Find(null, "batterycell".ToIdentifier());
var cargoSp = WayPoint.WayPointList.Find(wp => wp.Submarine == respawnSub && wp.SpawnType == SpawnType.Cargo);
@@ -522,7 +522,7 @@ namespace Barotrauma.Networking
if (characterInfo?.Job == null) { return; }
foreach (Skill skill in characterInfo.Job.Skills)
{
var skillPrefab = characterInfo.Job.Prefab.Skills.Find(s => skill.Identifier.Equals(s.Identifier, StringComparison.OrdinalIgnoreCase));
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);
}
@@ -268,7 +268,7 @@ namespace Barotrauma.Networking
doc.Root.SetAttributeValue("HiddenSubs", string.Join(",", HiddenSubs));
doc.Root.SetAttributeValue("AllowedRandomMissionTypes", string.Join(",", AllowedRandomMissionTypes));
doc.Root.SetAttributeValue("AllowedClientNameChars", string.Join(",", AllowedClientNameChars.Select(c => c.First + "-" + c.Second)));
doc.Root.SetAttributeValue("AllowedClientNameChars", string.Join(",", AllowedClientNameChars.Select(c => $"{c.Start}-{c.End}")));
SerializableProperty.SerializeProperties(this, doc.Root, true);
@@ -307,7 +307,7 @@ namespace Barotrauma.Networking
if (string.IsNullOrEmpty(doc.Root.GetAttributeString("losmode", "")))
{
LosMode = GameMain.Config.LosMode;
LosMode = GameSettings.CurrentConfig.Graphics.LosMode;
}
AutoRestart = doc.Root.GetAttributeBool("autorestart", false);
@@ -370,7 +370,12 @@ namespace Barotrauma.Networking
}
}
if (min > -1 && max > -1) { AllowedClientNameChars.Add(new Pair<int, int>(min, max)); }
if (min > max)
{
//swap min and max
(min, max) = (max, min);
}
if (min > -1 && max > -1) { AllowedClientNameChars.Add(new Range<int>(min, max)); }
}
AllowedRandomMissionTypes = new List<MissionType>();
@@ -399,12 +404,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SetBotSpawnMode(BotSpawnMode);
GameMain.NetLobbyScreen.SetBotCount(BotCount);
List<string> monsterNames = CharacterPrefab.Prefabs.Select(p => p.Identifier).ToList();
MonsterEnabled = new Dictionary<string, bool>();
foreach (string s in monsterNames)
{
if (!MonsterEnabled.ContainsKey(s)) MonsterEnabled.Add(s, true);
}
MonsterEnabled ??= CharacterPrefab.Prefabs.Select(p => (p.Identifier, true)).ToDictionary();
}
public string SelectNonHiddenSubmarine(string current = null)
@@ -85,7 +85,7 @@ namespace Barotrauma
string hash = equalityCheckVal > 0 ? string.Empty : inc.ReadString();
SubmarineInfo sub = equalityCheckVal > 0 ?
SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Type == SubmarineType.Player && s.EqualityCheckVal == equalityCheckVal) :
SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Type == SubmarineType.Player && s.MD5Hash.Hash == hash);
SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Type == SubmarineType.Player && s.MD5Hash.StringRepresentation == hash);
sender.SetVote(voteType, sub);
break;
case VoteType.Mode:
@@ -108,13 +108,10 @@ namespace Barotrauma
sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! ");
sb.AppendLine("\n");
sb.AppendLine("Game version " + GameMain.Version + " (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")");
if (GameMain.Config != null)
sb.AppendLine("Language: " + GameSettings.CurrentConfig.Language);
if (ContentPackageManager.EnabledPackages.All != null)
{
sb.AppendLine("Language: " + (GameMain.Config.Language ?? "none"));
if (GameMain.Config.AllEnabledPackages != null)
{
sb.AppendLine("Selected content packages: " + (!GameMain.Config.AllEnabledPackages.Any() ? "None" : string.Join(", ", GameMain.Config.AllEnabledPackages.Select(c => c.Name))));
}
sb.AppendLine("Selected content packages: " + (!ContentPackageManager.EnabledPackages.All.Any() ? "None" : string.Join(", ", ContentPackageManager.EnabledPackages.All.Select(c => c.Name))));
}
sb.AppendLine("Level seed: " + ((Level.Loaded == null) ? "no level loaded" : Level.Loaded.Seed));
sb.AppendLine("Loaded submarine: " + ((Submarine.MainSub == null) ? "None" : Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash + ")"));
@@ -176,7 +173,8 @@ namespace Barotrauma
File.WriteAllText(filePath, sb.ToString());
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameSettings.CurrentConfig.SaveDebugConsoleLogs
|| GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameAnalyticsManager.SendUserStatistics)
{
@@ -54,14 +54,14 @@ namespace Barotrauma
}
}
public string SelectedModeIdentifier
public Identifier SelectedModeIdentifier
{
get { return GameModes[SelectedModeIndex].Identifier; }
set
{
for (int i = 0; i < GameModes.Length; i++)
{
if (GameModes[i].Identifier.ToLower() == value.ToLower())
if (GameModes[i].Identifier == value)
{
SelectedModeIndex = i;
break;
@@ -4,13 +4,11 @@ namespace Barotrauma.Steam
{
partial class SteamManager
{
#region Server
private static void InitializeProjectSpecific() { isInitialized = true; }
private static void InitializeProjectSpecific() { IsInitialized = true; }
public static bool CreateServer(Networking.GameServer server, bool isPublic)
{
isInitialized = true;
IsInitialized = true;
Steamworks.SteamServerInit options = new Steamworks.SteamServerInit("Barotrauma", "Barotrauma")
{
@@ -39,26 +37,26 @@ namespace Barotrauma.Steam
public static bool RefreshServerDetails(Networking.GameServer server)
{
if (!isInitialized || !Steamworks.SteamServer.IsValid)
if (!IsInitialized || !Steamworks.SteamServer.IsValid)
{
return false;
}
var contentPackages = GameMain.Config.AllEnabledPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
var contentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerIncompatibleContent);
// These server state variables may be changed at any time. Note that there is no longer a mechanism
// to send the player count. The player count is maintained by steam and you should use the player
// to send the player count. The player count is maintained by Steam and you should use the player
// creation/authentication functions to maintain your player count.
Steamworks.SteamServer.ServerName = server.ServerName;
Steamworks.SteamServer.MaxPlayers = server.ServerSettings.MaxPlayers;
Steamworks.SteamServer.Passworded = server.ServerSettings.HasPassword;
Steamworks.SteamServer.MapName = GameMain.NetLobbyScreen?.SelectedSub?.DisplayName ?? "";
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("version", GameMain.Version.ToString());
Steamworks.SteamServer.SetKey("playercount", GameMain.Server.ConnectedClients.Count.ToString());
Steamworks.SteamServer.SetKey("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
Steamworks.SteamServer.SetKey("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.MD5hash.Hash)));
Steamworks.SteamServer.SetKey("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("modeselectionmode", server.ServerSettings.ModeSelectionMode.ToString());
@@ -68,7 +66,7 @@ namespace Barotrauma.Steam
Steamworks.SteamServer.SetKey("allowrespawn", server.ServerSettings.AllowRespawn.ToString());
Steamworks.SteamServer.SetKey("traitors", server.ServerSettings.TraitorsEnabled.ToString());
Steamworks.SteamServer.SetKey("gamestarted", server.GameStarted.ToString());
Steamworks.SteamServer.SetKey("gamemode", server.ServerSettings.GameModeIdentifier);
Steamworks.SteamServer.SetKey("gamemode", server.ServerSettings.GameModeIdentifier.Value);
Steamworks.SteamServer.SetKey("playstyle", server.ServerSettings.PlayStyle.ToString());
Steamworks.SteamServer.DedicatedServer = true;
@@ -78,7 +76,7 @@ namespace Barotrauma.Steam
public static Steamworks.BeginAuthResult StartAuthSession(byte[] authTicketData, ulong clientSteamID)
{
if (!isInitialized || !Steamworks.SteamServer.IsValid) return Steamworks.BeginAuthResult.ServerNotConnectedToSteam;
if (!IsInitialized || !Steamworks.SteamServer.IsValid) return Steamworks.BeginAuthResult.ServerNotConnectedToSteam;
DebugConsole.Log("SteamManager authenticating Steam client " + clientSteamID);
Steamworks.BeginAuthResult startResult = Steamworks.SteamServer.BeginAuthSession(authTicketData, clientSteamID);
@@ -92,7 +90,7 @@ namespace Barotrauma.Steam
public static void StopAuthSession(ulong clientSteamID)
{
if (!isInitialized || !Steamworks.SteamServer.IsValid) return;
if (!IsInitialized || !Steamworks.SteamServer.IsValid) return;
DebugConsole.Log("SteamManager ending auth session with Steam client " + clientSteamID);
Steamworks.SteamServer.EndSession(clientSteamID);
@@ -100,13 +98,11 @@ namespace Barotrauma.Steam
public static bool CloseServer()
{
if (!isInitialized || !Steamworks.SteamServer.IsValid) return false;
if (!IsInitialized || !Steamworks.SteamServer.IsValid) return false;
Steamworks.SteamServer.Shutdown();
return true;
}
#endregion
}
}
@@ -29,7 +29,8 @@ namespace Barotrauma
public virtual IEnumerable<string> CompletedTextKeys => new string[] { };
public virtual IEnumerable<string> CompletedTextValues(Traitor traitor) => new string[] { };
protected virtual string FormatText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => TextManager.FormatServerMessageWithGenderPronouns(traitor?.Character?.Info?.Gender ?? Gender.None, textId, keys, values);
protected virtual string FormatText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values)
=> TextManager.FormatServerMessageWithPronouns(traitor.Character.Info, textId, keys.Zip(values, (k,v) => (k,v)).ToArray());
protected internal virtual string GetStatusText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => FormatText(traitor, textId, keys, values);
protected internal virtual string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values) => FormatText(traitor, textId, keys, values);
@@ -51,11 +51,11 @@ namespace Barotrauma
{
continue;
}
var identifierMatches = matchIdentifier && item.prefab.Identifier == tag;
var identifierMatches = matchIdentifier && ((MapEntity)item).Prefab.Identifier == tag;
if (identifierMatches && tagPrefabName == null)
{
var textId = item.Prefab.GetItemNameTextId();
tagPrefabName = textId != null ? TextManager.FormatServerMessage(textId) : item.Prefab.Name;
tagPrefabName = textId != null ? TextManager.FormatServerMessage(textId) : item.Prefab.Name.Value;
}
if (identifierMatches || (matchTag && item.HasTag(tag)))
{
@@ -28,7 +28,7 @@ namespace Barotrauma
private enum EntityTypes { Character, Item }
private string[] entities;
private Identifier[] entities;
private EntityTypes[] entityTypes;
public override void Update(float deltaTime)
@@ -67,7 +67,7 @@ namespace Barotrauma
{
continue;
}
if (character.SpeciesName.Equals(entities[activeEntityIndex], StringComparison.OrdinalIgnoreCase) && Vector2.Distance(activeEntitySavedPosition, character.WorldPosition) < graceDistance)
if (character.SpeciesName == entities[activeEntityIndex] && Vector2.Distance(activeEntitySavedPosition, character.WorldPosition) < graceDistance)
{
activeEntity = character;
transformationTime = 0.0;
@@ -82,7 +82,7 @@ namespace Barotrauma
{
continue;
}
if (item.prefab.Identifier == entities[activeEntityIndex] && Vector2.Distance(activeEntitySavedPosition, item.WorldPosition) < graceDistance)
if (((MapEntity)item).Prefab.Identifier == entities[activeEntityIndex] && Vector2.Distance(activeEntitySavedPosition, item.WorldPosition) < graceDistance)
{
activeEntity = item;
transformationTime = 0.0;
@@ -117,7 +117,7 @@ namespace Barotrauma
{
continue;
}
if (character.SpeciesName.Equals(entities[activeEntityIndex], StringComparison.OrdinalIgnoreCase))
if (character.SpeciesName == entities[activeEntityIndex])
{
activeEntity = character;
break;
@@ -131,7 +131,7 @@ namespace Barotrauma
{
continue;
}
if (item.prefab.Identifier.Equals(entities[0], StringComparison.OrdinalIgnoreCase))
if (((MapEntity)item).Prefab.Identifier == entities[0])
{
activeEntity = item;
break;
@@ -146,7 +146,7 @@ namespace Barotrauma
public GoalEntityTransformation(string[] entities, string[] entityTypes, string catalystItemIdentifier) : base()
{
this.entities = entities;
this.entities = entities.ToIdentifiers().ToArray();
this.entityTypes = new EntityTypes[entityTypes.Length];
@@ -15,7 +15,7 @@ namespace Barotrauma
private readonly bool preferNew;
private readonly bool allowNew;
private readonly bool allowExisting;
private readonly HashSet<string> allowedContainerIdentifiers = new HashSet<string>();
private readonly HashSet<Identifier> allowedContainerIdentifiers = new HashSet<Identifier>();
private ItemPrefab targetPrefab;
private ItemPrefab containedPrefab;
@@ -83,7 +83,7 @@ namespace Barotrauma
{
continue;
}
if (item.GetComponent<ItemContainer>() != null && allowedContainerIdentifiers.Contains(item.prefab.Identifier))
if (item.GetComponent<ItemContainer>() != null && allowedContainerIdentifiers.Contains(((MapEntity)item).Prefab.Identifier))
{
if ((includeNew && !item.OwnInventory.IsFull()) || (includeExisting && item.OwnInventory.FindItemByIdentifier(targetPrefabCandidate.Identifier) != null))
{
@@ -166,7 +166,7 @@ namespace Barotrauma
targetPrefabTextId = targetPrefab.GetItemNameTextId();
}
targetNameText = targetPrefabTextId != null ? TextManager.FormatServerMessage(targetPrefabTextId) : targetPrefab.Name;
targetNameText = targetPrefabTextId != null ? TextManager.FormatServerMessage(targetPrefabTextId) : targetPrefab.Name.Value;
targetContainer = FindTargetContainer(Traitors, targetPrefab);
if (targetContainer == null)
{
@@ -175,9 +175,9 @@ namespace Barotrauma
return false;
}
var containerPrefabTextId = targetContainer.Prefab.GetItemNameTextId();
targetContainerNameText = containerPrefabTextId != null ? TextManager.FormatServerMessage(containerPrefabTextId) : targetContainer.Prefab.Name;
var targetHullTextId = targetContainer.CurrentHull?.prefab.GetHullNameTextId();
targetHullNameText = targetHullTextId != null ? TextManager.FormatServerMessage(targetHullTextId) : targetContainer?.CurrentHull?.DisplayName ?? "";
targetContainerNameText = containerPrefabTextId != null ? TextManager.FormatServerMessage(containerPrefabTextId) : targetContainer.Prefab.Name.Value;
var targetHullTextId = targetContainer.CurrentHull?.Prefab.GetHullNameTextId();
targetHullNameText = targetHullTextId != null ? TextManager.FormatServerMessage(targetHullTextId) : targetContainer?.CurrentHull?.DisplayName.Value ?? "";
if (allowNew && !targetContainer.OwnInventory.IsFull())
{
existingItems.Clear();
@@ -185,7 +185,7 @@ namespace Barotrauma
{
existingItems.Add(item);
}
Entity.Spawner.AddToSpawnQueue(targetPrefab, targetContainer.OwnInventory, onSpawned: item =>
Entity.Spawner.AddItemToSpawnQueue(targetPrefab, targetContainer.OwnInventory, onSpawned: item =>
{
item.AddTag("traitormissionitem");
});
@@ -216,7 +216,7 @@ namespace Barotrauma
{
for (int i = 0; i < spawnAmount; i++)
{
Entity.Spawner.AddToSpawnQueue(containedPrefab, target.OwnInventory);
Entity.Spawner.AddItemToSpawnQueue(containedPrefab, target.OwnInventory);
}
}
existingItems.Clear();
@@ -224,7 +224,7 @@ namespace Barotrauma
}
}
public GoalFindItem(TraitorMission.CharacterFilter filter, string identifier, bool preferNew, bool allowNew, bool allowExisting, float percentage, params string[] allowedContainerIdentifiers)
public GoalFindItem(TraitorMission.CharacterFilter filter, string identifier, bool preferNew, bool allowNew, bool allowExisting, float percentage, params Identifier[] allowedContainerIdentifiers)
{
this.filter = filter;
this.identifier = identifier;
@@ -21,7 +21,7 @@ namespace Barotrauma
base.Update(deltaTime);
var validHullsCount = 0;
var floodingAmount = 0.0f;
foreach (Hull hull in Hull.hullList)
foreach (Hull hull in Hull.HullList)
{
if (hull.Submarine == null || hull.Submarine.Info.IsOutpost || Traitors.All(traitor => hull.Submarine.TeamID != traitor.Character.TeamID)) { continue; }
if (hull.Submarine == GameMain.Server?.RespawnManager?.RespawnShuttle) { continue; }
@@ -15,7 +15,7 @@ namespace Barotrauma
private bool isCompleted;
private const float gracePeriod = 1f;
private string speciesId;
private Identifier speciesId;
private string targetCharacterName;
private Character targetCharacter;
private float timer;
@@ -52,7 +52,7 @@ namespace Barotrauma
{
continue;
}
if (character.SpeciesName.Equals(speciesId, StringComparison.OrdinalIgnoreCase))
if (character.SpeciesName == speciesId)
{
targetCharacter = character;
break;
@@ -64,9 +64,9 @@ namespace Barotrauma
return targetCharacter != null;
}
public GoalKeepTransformedAlive(string speciesId) : base()
public GoalKeepTransformedAlive(Identifier speciesId) : base()
{
this.speciesId = speciesId.ToLowerInvariant();
this.speciesId = speciesId;
}
}
}
@@ -13,12 +13,12 @@ namespace Barotrauma
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[targetname]", "[causeofdeath]", "[targethullname]" });
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[]
{ traitor.Mission.GetTargetNames(Targets) ?? "(unknown)", GetCauseOfDeath(), targetHull != null ? TextManager.Get($"roomname.{targetHull}") : string.Empty });
{ traitor.Mission.GetTargetNames(Targets) ?? "(unknown)", GetCauseOfDeath().Value, targetHull != null ? TextManager.Get($"roomname.{targetHull}").Value : string.Empty });
private bool isCompleted = false;
public override bool IsCompleted => isCompleted;
public override bool IsEnemy(Character character) => base.IsEnemy(character) || (!isCompleted && Targets.Contains(character));
public override bool IsEnemy(Character character) => base.IsEnemy(character) || (!isCompleted && Targets.Contains(character));
private CauseOfDeathType requiredCauseOfDeath;
private string afflictionId;
@@ -102,7 +102,7 @@ namespace Barotrauma
return true;
}
private string GetCauseOfDeath()
private LocalizedString GetCauseOfDeath()
{
if (requiredCauseOfDeath != CauseOfDeathType.Affliction || afflictionId == string.Empty)
{
@@ -8,8 +8,8 @@ namespace Barotrauma
{
public class GoalReplaceInventory : HumanoidGoal
{
private readonly HashSet<string> sabotageContainerIds = new HashSet<string>();
private readonly HashSet<string> validReplacementIds = new HashSet<string>();
private readonly HashSet<Identifier> sabotageContainerIds = new HashSet<Identifier>();
private readonly HashSet<Identifier> validReplacementIds = new HashSet<Identifier>();
private readonly float replaceAmount;
@@ -33,7 +33,7 @@ namespace Barotrauma
{
continue;
}
if (sabotageContainerIds.Contains(item.prefab.Identifier))
if (sabotageContainerIds.Contains(((MapEntity)item).Prefab.Identifier))
{
++totalAmount;
if (item.OwnInventory.AllItems.All(containedItem => !validReplacementIds.Contains(containedItem.Prefab.Identifier)))
@@ -59,7 +59,7 @@ namespace Barotrauma
return true;
}
public GoalReplaceInventory(string[] containerIds, string[] replacementIds, float replaceAmount)
public GoalReplaceInventory(Identifier[] containerIds, Identifier[] replacementIds, float replaceAmount)
{
sabotageContainerIds.UnionWith(containerIds);
validReplacementIds.UnionWith(replacementIds);
@@ -37,15 +37,10 @@ namespace Barotrauma
targetItems.Add(item);
}
}
//only target items in the main sub if there are any
if (targetItems.Count > 1 && targetItems.Any(it => it.Submarine == Submarine.MainSub))
{
targetItems.RemoveAll(it => it.Submarine != Submarine.MainSub);
}
if (targetItems.Count > 0)
{
var textId = targetItems[0].Prefab.GetItemNameTextId();
targetItemPrefabName = TextManager.FormatServerMessage(textId) ?? targetItems[0].Prefab.Name;
targetItemPrefabName = TextManager.FormatServerMessage(textId) ?? targetItems[0].Prefab.Name.Value;
}
return targetItems.Count > 0;
}
@@ -46,7 +46,7 @@ namespace Barotrauma
if (targetConnectionPanels.Count > 0)
{
var textId = targetConnectionPanels[0].Item.Prefab.GetItemNameTextId();
targetItemPrefabName = TextManager.FormatServerMessage(textId) ?? targetConnectionPanels[0].Item.Prefab.Name;
targetItemPrefabName = TextManager.FormatServerMessage(textId) ?? targetConnectionPanels[0].Item.Prefab.Name.Value;
}
return targetConnectionPanels.Count > 0;
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma
@@ -14,12 +15,13 @@ namespace Barotrauma
public override IEnumerable<string> InfoTextKeys => base.InfoTextKeys.Concat(new string[] { "[duration]" });
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { requiredDuration.ToString() });
public override IEnumerable<string> InfoTextValues(Traitor traitor) => base.InfoTextValues(traitor).Concat(new string[] { requiredDuration.ToString(CultureInfo.InvariantCulture) });
protected internal override string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values)
{
var infoText = base.GetInfoText(traitor, textId, keys, values);
return !string.IsNullOrEmpty(durationInfoTextId) && !infoText.Contains("[duration]") ? TextManager.FormatServerMessage(durationInfoTextId, new[] { "[infotext]", "[duration]" }, new[] { infoText, requiredDuration.ToString() }) : infoText;
return !string.IsNullOrEmpty(durationInfoTextId) && !infoText.Contains("[duration]") ? TextManager.FormatServerMessage(durationInfoTextId,
("[infotext]", infoText), ("[duration]", requiredDuration.ToString(CultureInfo.InvariantCulture))) : infoText;
}
private bool isCompleted = false;
@@ -17,7 +17,7 @@ namespace Barotrauma
protected internal override string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values)
{
var infoText = base.GetInfoText(traitor, textId, keys, values);
return !string.IsNullOrEmpty(timeLimitInfoTextId) ? TextManager.FormatServerMessage(timeLimitInfoTextId, new[] { "[infotext]", "[timelimit]" }, new[] { infoText, $"{TimeSpan.FromSeconds(timeLimit):g}" }) : infoText;
return !string.IsNullOrEmpty(timeLimitInfoTextId) ? TextManager.FormatServerMessage(timeLimitInfoTextId, ("[infotext]", infoText), ("[timelimit]", $"{TimeSpan.FromSeconds(timeLimit):g}")) : infoText;
}
public override bool CanBeCompleted(ICollection<Traitor> traitors) => base.CanBeCompleted(traitors) && (!Traitors.Any(IsStarted) || timeRemaining > 0.0f);
@@ -14,7 +14,7 @@ namespace Barotrauma
public override IEnumerable<string> StatusTextValues(Traitor traitor)
{
var values = base.StatusTextValues(traitor).ToArray();
values[1] = TextManager.GetServerMessage(StatusValueTextId);
values[1] = TextManager.FormatServerMessage(StatusValueTextId);
return values;
}
@@ -24,7 +24,7 @@ namespace Barotrauma
protected internal override string GetInfoText(Traitor traitor, string textId, IEnumerable<string> keys, IEnumerable<string> values)
{
var infoText = base.GetInfoText(traitor, textId, keys, values);
return !string.IsNullOrEmpty(optionalInfoTextId) ? TextManager.FormatServerMessage(optionalInfoTextId, new[] { "[infotext]" }, new[] { infoText }) : infoText;
return !string.IsNullOrEmpty(optionalInfoTextId) ? TextManager.FormatServerMessage(optionalInfoTextId, ("[infotext]", infoText)) : infoText;
}
public GoalIsOptional(Goal goal, string optionalInfoTextId) : base(goal)
@@ -30,7 +30,7 @@ namespace Barotrauma
}
public override IEnumerable<string> StatusTextKeys => Goal.StatusTextKeys;
public override IEnumerable<string> StatusTextValues(Traitor traitor) => new [] { InfoText(traitor), TextManager.FormatServerMessage(StatusValueTextId) };
public override IEnumerable<string> StatusTextValues(Traitor traitor) => new string[] { InfoText(traitor), TextManager.FormatServerMessage(StatusValueTextId) };
public override IEnumerable<string> InfoTextKeys => Goal.InfoTextKeys;
public override IEnumerable<string> InfoTextValues(Traitor traitor) => Goal.InfoTextValues(traitor);
@@ -39,7 +39,7 @@ namespace Barotrauma
{
var statusText = goal.StatusText(Traitor);
var startIndex = statusText.LastIndexOf('/') + 1;
return $"{statusText.Substring(0, startIndex)}[{index}.st]={statusText.Substring(startIndex)}/[{index}.sl]={TextManager.FormatServerMessage(GoalInfoFormatId, new string[] { "[statustext]" }, new string[] { $"[{index}.st]" })}";
return $"{statusText.Substring(0, startIndex)}[{index}.st]={statusText.Substring(startIndex)}/[{index}.sl]={TextManager.FormatServerMessage(GoalInfoFormatId, ("[statustext]", $"[{index}.st]"))}";
}).ToArray()),
string.Join("", activeGoals.Select((goal, index) => $"[{index}.sl]").ToArray()));
@@ -49,7 +49,7 @@ namespace Barotrauma
{
var statusText = goal.StatusText(Traitor);
var startIndex = statusText.LastIndexOf('/') + 1;
return $"{statusText.Substring(0, startIndex)}[{index}.st]={statusText.Substring(startIndex)}/[{index}.sl]={TextManager.FormatServerMessage(GoalInfoFormatId, new string[] { "[statustext]" }, new string[] { $"[{index}.st]" })}";
return $"{statusText.Substring(0, startIndex)}[{index}.st]={statusText.Substring(startIndex)}/[{index}.sl]={TextManager.FormatServerMessage(GoalInfoFormatId, ("[statustext]", $"[{index}.st]"))}";
}).ToArray()),
string.Join("", allGoals.Select((goal, index) => $"[{index}.sl]").ToArray()));
@@ -57,13 +57,14 @@ namespace Barotrauma
public virtual IEnumerable<string> StartMessageKeys => new string[] { "[traitorgoalinfos]" };
public virtual IEnumerable<string> StartMessageValues => new string[] { GoalInfos };
public virtual string StartMessageText => TextManager.FormatServerMessageWithGenderPronouns(Traitor?.Character?.Info?.Gender ?? Gender.None, StartMessageTextId, StartMessageKeys, StartMessageValues);
public virtual LocalizedString StartMessageText
=> TextManager.FormatServerMessageWithPronouns(Traitor.Character.Info, StartMessageTextId, StartMessageKeys.Zip(StartMessageValues, (k,v) => (k,v)).ToArray());
public virtual string StartMessageServerTextId { get; set; } = "TraitorObjectiveStartMessageServer";
public virtual IEnumerable<string> StartMessageServerKeys => StartMessageKeys.Concat(new string[] { "[traitorname]" });
public virtual IEnumerable<string> StartMessageServerValues => StartMessageValues.Concat(new string[] { Traitor?.Character?.Name ?? "(unknown)" });
public virtual string StartMessageServerText => TextManager.FormatServerMessageWithGenderPronouns(Traitor?.Character?.Info?.Gender ?? Gender.None, StartMessageServerTextId, StartMessageServerKeys, StartMessageServerValues);
public virtual LocalizedString StartMessageServerText => TextManager.FormatServerMessageWithPronouns(Traitor.Character.Info, StartMessageServerTextId, StartMessageServerKeys.Zip(StartMessageServerValues, (k,v) => (k,v)).ToArray());
public virtual string EndMessageSuccessTextId { get; set; } = "TraitorObjectiveEndMessageSuccess";
public virtual string EndMessageSuccessDeadTextId { get; set; } = "TraitorObjectiveEndMessageSuccessDead";
@@ -83,7 +84,7 @@ namespace Barotrauma
var messageId = IsCompleted
? (traitorIsDead ? EndMessageSuccessDeadTextId : traitorIsDetained ? EndMessageSuccessDetainedTextId : EndMessageSuccessTextId)
: (traitorIsDead ? EndMessageFailureDeadTextId : traitorIsDetained ? EndMessageFailureDetainedTextId : EndMessageFailureTextId);
return TextManager.FormatServerMessageWithGenderPronouns(Traitor?.Character?.Info?.Gender ?? Gender.None, messageId, EndMessageKeys.ToArray(), EndMessageValues.ToArray());
return TextManager.FormatServerMessageWithPronouns(Traitor.Character.Info, messageId, EndMessageKeys.Zip(EndMessageValues, (k,v)=>(k,v)).ToArray());
}
}
@@ -133,21 +134,21 @@ namespace Barotrauma
IsStarted = true;
traitor.SendChatMessageBox(StartMessageText, traitor.Mission?.Identifier);
traitor.UpdateCurrentObjective(GoalInfos, traitor.Mission?.Identifier);
traitor.SendChatMessageBox(StartMessageText.Value, traitor.Mission.Identifier);
traitor.UpdateCurrentObjective(GoalInfos, traitor.Mission.Identifier);
return true;
}
public void StartMessage()
{
Traitor.SendChatMessage(StartMessageText, Traitor.Mission?.Identifier);
Traitor.SendChatMessage(StartMessageText.Value, Traitor.Mission.Identifier);
}
public void EndMessage()
{
Traitor.SendChatMessageBox(EndMessageText, Traitor.Mission?.Identifier);
Traitor.SendChatMessage(EndMessageText, Traitor.Mission?.Identifier);
Traitor.SendChatMessageBox(EndMessageText, Traitor.Mission.Identifier);
Traitor.SendChatMessage(EndMessageText, Traitor.Mission.Identifier);
}
public void Update(float deltaTime)
@@ -170,12 +171,12 @@ namespace Barotrauma
pendingGoals.RemoveAt(i);
if (GameMain.Server != null)
{
Traitor.SendChatMessage(goal.CompletedText(Traitor), Traitor.Mission?.Identifier);
Traitor.SendChatMessage(goal.CompletedText(Traitor), Traitor.Mission.Identifier);
if (pendingGoals.Count > 0)
{
Traitor.SendChatMessageBox(goal.CompletedText(Traitor), Traitor.Mission?.Identifier);
Traitor.SendChatMessageBox(goal.CompletedText(Traitor), Traitor.Mission.Identifier);
}
Traitor.UpdateCurrentObjective(GoalInfos, Traitor.Mission?.Identifier);
Traitor.UpdateCurrentObjective(GoalInfos, Traitor.Mission.Identifier);
}
}
}
@@ -22,37 +22,35 @@ namespace Barotrauma
public delegate void MessageSender(string message);
public void Greet(GameServer server, string codeWords, string codeResponse, MessageSender messageSender)
{
string greetingMessage = TextManager.FormatServerMessage(Mission.StartText, new string[] {
"[codewords]", "[coderesponse]"
}, new string[] {
codeWords, codeResponse
});
string greetingMessage = TextManager.FormatServerMessage(Mission.StartText,
("[codewords]", codeWords),
("[coderesponse]", codeResponse));
messageSender(greetingMessage);
Client traitorClient = server.ConnectedClients.Find(c => c.Character == Character);
Client ownerClient = server.ConnectedClients.Find(c => c.Connection == server.OwnerConnection);
if (traitorClient != ownerClient && ownerClient != null && ownerClient.Character == null)
{
GameMain.Server.SendTraitorMessage(ownerClient, CurrentObjective.StartMessageServerText, Mission?.Identifier, TraitorMessageType.ServerMessageBox);
GameMain.Server.SendTraitorMessage(ownerClient, CurrentObjective.StartMessageServerText.Value, Mission.Identifier, TraitorMessageType.ServerMessageBox);
}
}
public void SendChatMessage(string serverText, string iconIdentifier)
public void SendChatMessage(string serverText, Identifier iconIdentifier)
{
Client traitorClient = GameMain.Server.ConnectedClients.Find(c => c.Character == Character);
GameMain.Server.SendTraitorMessage(traitorClient, serverText, iconIdentifier, TraitorMessageType.Server);
}
public void SendChatMessageBox(string serverText, string iconIdentifier)
public void SendChatMessageBox(string serverText, Identifier iconIdentifier)
{
Client traitorClient = GameMain.Server.ConnectedClients.Find(c => c.Character == Character);
GameMain.Server.SendTraitorMessage(traitorClient, serverText, iconIdentifier, TraitorMessageType.ServerMessageBox);
}
public void UpdateCurrentObjective(string objectiveText, string iconIdentifier)
public void UpdateCurrentObjective(string objectiveText, Identifier iconIdentifier)
{
Client traitorClient = GameMain.Server.ConnectedClients.Find(c => c.Character == Character);
Character.TraitorCurrentObjective = objectiveText;
GameMain.Server.SendTraitorMessage(traitorClient, Character.TraitorCurrentObjective, iconIdentifier, TraitorMessageType.Objective);
GameMain.Server.SendTraitorMessage(traitorClient, Character.TraitorCurrentObjective.Value, iconIdentifier, TraitorMessageType.Objective);
}
}
}
@@ -169,7 +169,8 @@ namespace Barotrauma
else
{
var mission = TraitorMissionPrefab.RandomPrefab()?.Instantiate();
if (mission != null) {
if (mission != null)
{
if (mission.CanBeStarted(server, this, CharacterTeamType.None))
{
if (mission.Start(server, this, CharacterTeamType.None))
@@ -43,7 +43,7 @@ namespace Barotrauma
public string GlobalEndMessageFailureDeadTextId { get; private set; }
public string GlobalEndMessageFailureDetainedTextId { get; private set; }
public readonly string Identifier;
public readonly Identifier Identifier;
public virtual IEnumerable<string> GlobalEndMessageKeys => new string[] { "[traitorname]", "[traitorgoalinfos]" };
public virtual IEnumerable<string> GlobalEndMessageValues {
@@ -60,7 +60,7 @@ namespace Barotrauma
{
get
{
if (Traitors.Any() && allObjectives.Count > 0)
if (Traitors.Any() && allObjectives.Count > 0)
{
return TextManager.JoinServerMessages("\n",
Traitors.Values.Select(traitor =>
@@ -71,7 +71,7 @@ namespace Barotrauma
var messageId = isSuccess
? (traitorIsDead ? GlobalEndMessageSuccessDeadTextId : traitorIsDetained ? GlobalEndMessageSuccessDetainedTextId : GlobalEndMessageSuccessTextId)
: (traitorIsDead ? GlobalEndMessageFailureDeadTextId : traitorIsDetained ? GlobalEndMessageFailureDetainedTextId : GlobalEndMessageFailureTextId);
return TextManager.FormatServerMessageWithGenderPronouns(traitor.Character?.Info?.Gender ?? Gender.None, messageId, GlobalEndMessageKeys.ToArray(), GlobalEndMessageValues.ToArray());
return TextManager.FormatServerMessageWithPronouns(traitor.Character.Info, messageId, GlobalEndMessageKeys.Zip(GlobalEndMessageValues).ToArray());
}).ToArray());
}
return "";
@@ -376,7 +376,7 @@ namespace Barotrauma
}
}
public TraitorMission(string identifier, string startText, string globalEndMessageSuccessTextId, string globalEndMessageSuccessDeadTextId, string globalEndMessageSuccessDetainedTextId, string globalEndMessageFailureTextId, string globalEndMessageFailureDeadTextId, string globalEndMessageFailureDetainedTextId, IEnumerable<KeyValuePair<string, RoleFilter>> roles, ICollection<Objective> objectives)
public TraitorMission(Identifier identifier, string startText, string globalEndMessageSuccessTextId, string globalEndMessageSuccessDeadTextId, string globalEndMessageSuccessDetainedTextId, string globalEndMessageFailureTextId, string globalEndMessageFailureDeadTextId, string globalEndMessageFailureDetainedTextId, IEnumerable<KeyValuePair<string, RoleFilter>> roles, ICollection<Objective> objectives)
{
Identifier = identifier;
StartText = startText;
@@ -9,38 +9,27 @@ namespace Barotrauma
{
class TraitorMissionPrefab
{
public class TraitorMissionEntry
public class TraitorMissionEntry : Prefab
{
public static PrefabCollection<TraitorMissionEntry> Prefabs => TraitorMissionPrefab.Prefabs;
public readonly TraitorMissionPrefab Prefab;
public float SelectedWeight;
public TraitorMissionEntry(XElement element)
public TraitorMissionEntry(ContentXElement element, TraitorMissionsFile file) : base(file, element)
{
Prefab = new TraitorMissionPrefab(element);
}
}
public static readonly List<TraitorMissionEntry> List = new List<TraitorMissionEntry>();
public static void Init()
{
var files = GameMain.Instance.GetFilesOfType(ContentType.TraitorMissions);
foreach (ContentFile file in files)
{
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
if (doc?.Root == null) continue;
foreach (XElement element in doc.Root.Elements())
{
List.Add(new TraitorMissionEntry(element));
}
}
public override void Dispose() { }
}
public static readonly PrefabCollection<TraitorMissionEntry> Prefabs = new PrefabCollection<TraitorMissionEntry>();
public static TraitorMissionPrefab RandomPrefab()
{
var selected = ToolBox.SelectWeightedRandom(List, List.Select(mission => Math.Max(mission.SelectedWeight, 0.1f)).ToList(), TraitorManager.Random);
var selected = ToolBox.SelectWeightedRandom(Prefabs.ToList(), Prefabs.Select(mission => Math.Max(mission.SelectedWeight, 0.1f)).ToList(), TraitorManager.Random);
//the weight of the missions that didn't get selected keeps growing the make them more likely to get picked
foreach (var mission in List)
foreach (var mission in Prefabs)
{
mission.SelectedWeight += 10;
}
@@ -103,7 +92,7 @@ namespace Barotrauma
private delegate bool TargetFilter(string value, Character character);
private static Dictionary<string, TargetFilter> targetFilters = new Dictionary<string, TargetFilter>()
{
{ "job", (value, character) => value.Equals(character.Info.Job.Prefab.Identifier, StringComparison.OrdinalIgnoreCase) },
{ "job", (value, character) => value == character.Info.Job.Prefab.Identifier },
{ "role", (value, character) => value.Equals(GameMain.Server.TraitorManager.GetTraitorRole(character), StringComparison.OrdinalIgnoreCase) }
};
@@ -180,12 +169,29 @@ namespace Barotrauma
itemCountFilters.Add((character) => filter(attribute.Value, character));
}
}
goal = new Traitor.GoalFindItem((character) => itemCountFilters.All(f => f(character)), Config.GetAttributeString("identifier", null), Config.GetAttributeBool("preferNew", true), Config.GetAttributeBool("allowNew", true), Config.GetAttributeBool("allowExisting", true), Config.GetAttributeFloat("percentage", -1f), Config.GetAttributeStringArray("allowedContainers", new string[] {"steelcabinet", "mediumsteelcabinet", "suppliescabinet"}));
goal = new Traitor.GoalFindItem((character) => itemCountFilters.All(f => f(character)),
Config.GetAttributeString("identifier",
null),
Config.GetAttributeBool("preferNew",
true),
Config.GetAttributeBool("allowNew",
true),
Config.GetAttributeBool("allowExisting",
true),
Config.GetAttributeFloat("percentage",
-1f),
Config.GetAttributeIdentifierArray("allowedContainers",
new string[]
{
"steelcabinet",
"mediumsteelcabinet",
"suppliescabinet"
}.ToIdentifiers()));
break;
case "replaceinventory":
checker.Required("containers", "replacements");
checker.Optional("percentage");
goal = new Traitor.GoalReplaceInventory(Config.GetAttributeStringArray("containers", new string[] { }), Config.GetAttributeStringArray("replacements", new string[] { }), Config.GetAttributeFloat("percentage", 100.0f) / 100.0f);
goal = new Traitor.GoalReplaceInventory(Config.GetAttributeIdentifierArray("containers", new Identifier[] { }), Config.GetAttributeIdentifierArray("replacements", new Identifier[] { }), Config.GetAttributeFloat("percentage", 100.0f) / 100.0f);
break;
case "reachdistancefromsub":
checker.Optional("distance");
@@ -221,7 +227,7 @@ namespace Barotrauma
break;
case "keeptransformedalive":
checker.Required("speciesname");
goal = new Traitor.GoalKeepTransformedAlive(Config.GetAttributeString("speciesname", null));
goal = new Traitor.GoalKeepTransformedAlive(Config.GetAttributeIdentifier("speciesname", Identifier.Empty));
break;
default:
GameServer.Log($"Unrecognized goal type \"{goalType}\".", ServerLog.MessageType.Error);
@@ -425,7 +431,7 @@ namespace Barotrauma
}
public readonly Dictionary<string, Role> Roles = new Dictionary<string, Role>();
public readonly string Identifier;
public readonly Identifier Identifier;
public readonly string StartText;
public readonly string EndMessageSuccessText;
public readonly string EndMessageSuccessDeadText;
@@ -575,14 +581,14 @@ namespace Barotrauma
if (jobs != null)
{
var jobsSet = new HashSet<string>(jobs.Select(job => job.ToLower(CultureInfo.InvariantCulture)));
filters.Add(character => character.Info?.Job != null && jobsSet.Contains(character.Info.Job.Name.ToLower(CultureInfo.InvariantCulture)));
filters.Add(character => character.Info?.Job != null && jobsSet.Contains(character.Info.Job.Name.ToLower().Value));
}
return new Role(filters);
}
public TraitorMissionPrefab(XElement missionRoot)
public TraitorMissionPrefab(ContentXElement missionRoot)
{
Identifier = missionRoot.GetAttributeString("identifier", null);
Identifier = missionRoot.GetAttributeIdentifier("identifier", Identifier.Empty);
foreach (var element in missionRoot.Elements())
{
using (var checker = new AttributeChecker(element))