(a00338777) v0.9.2.1

This commit is contained in:
Joonas Rikkonen
2019-08-26 19:58:19 +03:00
parent 0f63da27b2
commit 80698b58b0
311 changed files with 11763 additions and 4507 deletions
@@ -14,11 +14,6 @@ namespace Barotrauma
get;
private set;
}
/// <summary>
/// Use as a minimum or static sight range.
/// </summary>
public static float StaticSightRange = 3000;
private float soundRange;
private float sightRange;
@@ -75,7 +70,7 @@ namespace Barotrauma
public bool Enabled = true;
public float MinSoundRange, MinSightRange;
public float MaxSoundRange = float.MaxValue, MaxSightRange = float.MaxValue;
public float MaxSoundRange = 100000, MaxSightRange = 100000;
public TargetType Type { get; private set; }
@@ -128,8 +123,8 @@ namespace Barotrauma
{
SightRange = element.GetAttributeFloat("sightrange", 0.0f);
SoundRange = element.GetAttributeFloat("soundrange", 0.0f);
MinSightRange = element.GetAttributeFloat("minsightrange", SightRange);
MinSoundRange = element.GetAttributeFloat("minsoundrange", SoundRange);
MinSightRange = element.GetAttributeFloat("minsightrange", 0f);
MinSoundRange = element.GetAttributeFloat("minsoundrange", 0f);
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
@@ -142,15 +137,9 @@ namespace Barotrauma
}
}
public AITarget(Entity e, float sightRange = -1, float soundRange = 0)
public AITarget(Entity e)
{
Entity = e;
if (sightRange < 0)
{
sightRange = StaticSightRange;
}
SightRange = sightRange;
SoundRange = soundRange;
List.Add(this);
}
@@ -109,9 +109,9 @@ namespace Barotrauma
private Dictionary<AITarget, AITargetMemory> targetMemories;
//the eyesight of the NPC (0.0 = blind, 1.0 = sees every target within sightRange)
private float sight;
public float sight;
//how far the NPC can hear targets from (0.0 = deaf, 1.0 = hears every target within soundRange)
private float hearing;
public float hearing;
private float colliderSize;
@@ -270,11 +270,14 @@ namespace Barotrauma
return null;
}
public override void SelectTarget(AITarget target)
public override void SelectTarget(AITarget target) => SelectTarget(target, 100);
public void SelectTarget(AITarget target, float priority)
{
SelectedAiTarget = target;
selectedTargetMemory = GetTargetMemory(target);
targetValue = 100.0f;
selectedTargetMemory.Priority = priority;
targetValue = priority;
}
public override void Update(float deltaTime)
@@ -985,7 +988,7 @@ namespace Barotrauma
var aiTarget = wallTarget.Structure.AiTarget;
if (aiTarget != null && SelectedAiTarget != aiTarget)
{
SelectTarget(aiTarget);
SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget).Priority);
}
}
if (SelectedAiTarget.Entity is IDamageable damageTarget)
@@ -370,7 +370,7 @@ namespace Barotrauma
if (item.CurrentHull != hull) { continue; }
if (AIObjectiveRepairItems.IsValidTarget(item, Character))
{
if (item.Repairables.All(r => item.Condition > r.ShowRepairUIThreshold)) { continue; }
if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { continue; }
AddTargets<AIObjectiveRepairItems, Item>(Character, item);
if (newOrder == null)
{
@@ -640,7 +640,7 @@ namespace Barotrauma
if (item.CurrentHull != hull) { continue; }
if (AIObjectiveRepairItems.IsValidTarget(item, character))
{
if (item.Repairables.All(r => item.Condition > r.ShowRepairUIThreshold)) { continue; }
if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { continue; }
AddTargets<AIObjectiveRepairItems, Item>(character, item);
}
}
@@ -142,6 +142,9 @@ namespace Barotrauma
IsPathDirty = false;
}
public Func<PathNode, bool> startNodeFilter;
public Func<PathNode, bool> endNodeFilter;
protected override Vector2 DoSteeringSeek(Vector2 target, float weight)
{
bool needsNewPath = currentPath != null && currentPath.Unreachable || Vector2.DistanceSquared(target, currentTarget) > 1;
@@ -164,7 +167,7 @@ namespace Barotrauma
}
}
var newPath = pathFinder.FindPath(pos, target, character.Submarine, "(Character: " + character.Name + ")");
var newPath = pathFinder.FindPath(pos, target, character.Submarine, "(Character: " + character.Name + ")", startNodeFilter, endNodeFilter);
bool useNewPath = currentPath == null || needsNewPath;
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
{
@@ -14,7 +14,9 @@ namespace Barotrauma
private float waitUntilPathUnreachable;
private bool getDivingGearIfNeeded;
public Func<bool> customCondition;
public Func<bool> requiredCondition;
public Func<PathNode, bool> startNodeFilter;
public Func<PathNode, bool> endNodeFilter;
public bool followControlledCharacter;
public bool mimic;
@@ -137,7 +139,12 @@ namespace Barotrauma
currTargetSimPos -= diff;
}
}
character.AIController.SteeringManager.SteeringSeek(currTargetSimPos);
if (PathSteering != null)
{
PathSteering.startNodeFilter = startNodeFilter;
PathSteering.endNodeFilter = endNodeFilter;
}
SteeringManager.SteeringSeek(currTargetSimPos);
if (SteeringManager != PathSteering)
{
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 1, heading: VectorExtensions.Forward(character.AnimController.Collider.Rotation));
@@ -191,7 +198,7 @@ namespace Barotrauma
}
else if (closeEnough)
{
if (customCondition == null || customCondition())
if (requiredCondition == null || requiredCondition())
{
if (Target is Item item)
{
@@ -218,7 +225,7 @@ namespace Barotrauma
private void CalculateCloseEnough()
{
float interactionDistance = Target is Item i ? i.InteractDistance * 0.9f : 0;
float interactionDistance = Target is Item i ? i.InteractDistance + Math.Max(i.Rect.Width, i.Rect.Height) / 2 : 0;
CloseEnough = Math.Max(interactionDistance, CloseEnough);
}
@@ -149,7 +149,14 @@ namespace Barotrauma
character?.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f);
}
}
repairable.CurrentFixer = abandon && repairable.CurrentFixer == character ? null : character;
if (abandon)
{
repairable.StopRepairing(character);
}
else
{
repairable.StartRepairing(character, Repairable.FixActions.Repair);
}
break;
}
}
@@ -161,7 +168,11 @@ namespace Barotrauma
constructor: () =>
{
previousCondition = -1;
var objective = new AIObjectiveGoTo(Item, character, objectiveManager);
var objective = new AIObjectiveGoTo(Item, character, objectiveManager)
{
// Don't stop in ladders, because we can't interact with other items while holding the ladders.
endNodeFilter = node => node.Waypoint.Ladders == null
};
if (repairTool != null)
{
objective.CloseEnough = repairTool.Range * 0.75f;
@@ -43,7 +43,7 @@ namespace Barotrauma
if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c))) { return false; }
if (!Objectives.ContainsKey(item))
{
if (item.Repairables.All(r => item.Condition > r.ShowRepairUIThreshold)) { return false; }
if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { return false; }
}
if (RequireAdequateSkills)
{
@@ -157,12 +157,13 @@ namespace Barotrauma
}
}
public SteeringPath FindPath(Vector2 start, Vector2 end, Submarine hostSub = null, string errorMsgStr = null)
public SteeringPath FindPath(Vector2 start, Vector2 end, Submarine hostSub = null, string errorMsgStr = null, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null)
{
float closestDist = 0.0f;
PathNode startNode = null;
foreach (PathNode node in nodes)
{
if (startNodeFilter != null && !startNodeFilter(node)) { continue; }
Vector2 nodePos = node.Position;
if (hostSub != null)
{
@@ -219,6 +220,7 @@ namespace Barotrauma
PathNode endNode = null;
foreach (PathNode node in nodes)
{
if (endNodeFilter != null && !endNodeFilter(node)) { continue; }
Vector2 nodePos = node.Position;
if (hostSub != null)
{
@@ -96,8 +96,8 @@ namespace Barotrauma
public virtual AnimationType AnimationType { get; protected set; }
public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType.ToString()}";
public static string GetDefaultFile(string speciesName, AnimationType animType, ContentPackage contentPackage = null) =>
$"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName, animType)}.xml";
public static string GetDefaultFile(string speciesName, AnimationType animType, ContentPackage contentPackage = null)
=> Path.Combine(GetFolder(speciesName, contentPackage), $"{GetDefaultFileName(speciesName, animType)}.xml");
public static string GetFolder(string speciesName, ContentPackage contentPackage = null)
{
@@ -66,7 +66,8 @@ namespace Barotrauma
.Concat(Joints.Select(j => j as RagdollSubParams)));
public static string GetDefaultFileName(string speciesName) => $"{speciesName.CapitaliseFirstInvariant()}DefaultRagdoll";
public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null) => $"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName)}.xml";
public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null)
=> Path.Combine(GetFolder(speciesName, contentPackage), $"{GetDefaultFileName(speciesName)}.xml");
private static readonly object[] dummyParams = new object[]
{
@@ -1324,7 +1324,7 @@ namespace Barotrauma
private void CheckBodyInRest(float deltaTime)
{
if (Collider.LinearVelocity.LengthSquared() > 0.01f || character.SelectedBy != null || !character.IsDead)
if (InWater || Collider.LinearVelocity.LengthSquared() > 0.01f || character.SelectedBy != null || !character.IsDead)
{
bodyInRestTimer = 0.0f;
foreach (Limb limb in Limbs)
@@ -1383,10 +1383,12 @@ namespace Barotrauma
private bool CheckValidity(PhysicsBody body)
{
string errorMsg = null;
string bodyName = body.UserData is Limb ? "Limb" : "Collider";
string bodyName = body.UserData is Limb limb ?
"Limb (" + limb.type + ")" :
"Collider";
if (!MathUtils.IsValid(body.SimPosition) || Math.Abs(body.SimPosition.X) > 1e10f || Math.Abs(body.SimPosition.Y) > 1e10f)
{
errorMsg = bodyName+ " position invalid (" + body.SimPosition + ", character: " + character.Name + "), resetting the ragdoll.";
errorMsg = bodyName + " position invalid (" + body.SimPosition + ", character: " + character.Name + "), resetting the ragdoll.";
}
else if (!MathUtils.IsValid(body.LinearVelocity) || Math.Abs(body.LinearVelocity.X) > 1000f || Math.Abs(body.LinearVelocity.Y) > 1000f)
{
@@ -1426,10 +1428,10 @@ namespace Barotrauma
{
Collider.SetTransform(Vector2.Zero, 0.0f);
}
foreach (Limb limb in Limbs)
foreach (Limb otherLimb in Limbs)
{
limb.body.SetTransform(Collider.SimPosition, 0.0f);
limb.body.ResetDynamics();
otherLimb.body.SetTransform(Collider.SimPosition, 0.0f);
otherLimb.body.ResetDynamics();
}
SetInitialLimbPositions();
return false;
@@ -104,6 +104,9 @@ namespace Barotrauma
public readonly bool IsHumanoid;
public bool IsTraitor;
public string TraitorCurrentObjective = "";
//the name of the species (e.q. human)
public readonly string SpeciesName;
@@ -1515,12 +1518,12 @@ namespace Barotrauma
bool leftHand = Inventory.IsInLimbSlot(item, InvSlotType.LeftHand);
bool selected = false;
if (rightHand && SelectedItems[0] == null)
if (rightHand && (SelectedItems[0] == null || SelectedItems[0] == item))
{
selectedItems[0] = item;
selected = true;
}
if (leftHand && SelectedItems[1] == null)
if (leftHand && (SelectedItems[1] == null || SelectedItems[1] == item))
{
selectedItems[1] = item;
selected = true;
@@ -1839,7 +1842,7 @@ namespace Barotrauma
{
DeselectCharacter();
}
else if (focusedCharacter != null && IsKeyHit(InputType.Grab) && FocusedCharacter.CanInventoryBeAccessed)
else if (focusedCharacter != null && IsKeyHit(InputType.Grab) && FocusedCharacter.CanBeDragged)
{
SelectCharacter(focusedCharacter);
}
@@ -1954,6 +1957,10 @@ namespace Barotrauma
//disable AI characters that are far away from the sub and the controlled character
float distSqr = Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition);
if (Controlled != null)
{
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(Controlled.WorldPosition, c.WorldPosition));
}
else
{
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(GameMain.GameScreen.Cam.GetPosition(), c.WorldPosition));
}
@@ -2198,16 +2205,15 @@ namespace Barotrauma
private void UpdateSightRange()
{
if (aiTarget == null) { return; }
// TODO: the formula might need some tweaking
float range = (float)Math.Sqrt(Mass) * 1000.0f + AnimController.Collider.LinearVelocity.Length() * 500.0f;
aiTarget.SightRange = MathHelper.Clamp(range, 0, 15000.0f);
float range = (float)Math.Sqrt(Mass) * 250 + AnimController.Collider.LinearVelocity.Length() * 500;
aiTarget.SightRange = MathHelper.Clamp(range, 0, 10000);
}
private void UpdateSoundRange()
{
if (aiTarget == null) { return; }
float range = Mass / 5 * AnimController.TargetMovement.Length() * Noise;
aiTarget.SoundRange = MathHelper.Clamp(range, 0f, 5000f);
float range = ((float)Math.Sqrt(Mass) / 3) * (AnimController.TargetMovement.Length() * 2) * Noise;
aiTarget.SoundRange = MathHelper.Clamp(range, 0, 10000);
}
public void SetOrder(Order order, string orderOption, Character orderGiver, bool speak = true)
@@ -2598,6 +2604,7 @@ namespace Barotrauma
{
if (selectedItems[i] != null) selectedItems[i].Drop(this);
}
SelectedConstruction = null;
AnimController.ResetPullJoints();
@@ -21,7 +21,7 @@ namespace Barotrauma
/// <summary>
/// Probability for the affliction to be applied. Used by attacks.
/// </summary>
public float ApplyProbability;
public float ApplyProbability = 1.0f;
/// <summary>
/// Which character gave this affliction
@@ -9,6 +9,10 @@ namespace Barotrauma
{
private float invertControlsCooldown = 60.0f;
private float stunCoolDown = 60.0f;
private float invertControlsTimer;
private float invertControlsToggleTimer;
public AfflictionSpaceHerpes(AfflictionPrefab prefab, float strength) : base(prefab, strength)
{
}
@@ -25,10 +29,29 @@ namespace Barotrauma
//invert controls every 126-234 seconds when strength is close to 0
//every 56-104 seconds when strength is close to 100
invertControlsCooldown = (180.0f - Strength) * Rand.Range(0.7f, 1.3f);
var invertControlsAffliction = AfflictionPrefab.List.Find(ap => ap.Identifier == "invertcontrols");
float invertControlsDuration = MathHelper.Lerp(10.0f, 60.0f, Strength / 100.0f) * Rand.Range(0.7f, 1.3f);
characterHealth.ApplyAffliction(null, new Affliction(invertControlsAffliction, invertControlsDuration));
invertControlsTimer = MathHelper.Lerp(10.0f, 60.0f, Strength / 100.0f) * Rand.Range(0.7f, 1.3f);
}
else if (invertControlsTimer > 0.0f)
{
//randomly toggle inverted controls on/off every 5 seconds
invertControlsToggleTimer -= deltaTime;
if (invertControlsToggleTimer <= 0.0f)
{
invertControlsToggleTimer = 5.0f;
if (Rand.Range(0.0f, 1.0f) < 0.5f)
{
characterHealth.ReduceAffliction(null, "invertcontrols", 100);
}
else
{
var invertControlsAffliction = AfflictionPrefab.List.Find(ap => ap.Identifier == "invertcontrols");
characterHealth.ApplyAffliction(null, new Affliction(invertControlsAffliction, 5.0f));
}
}
invertControlsTimer -= deltaTime;
}
if (Strength > 50.0f)
{
@@ -1,9 +1,9 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -732,14 +732,14 @@ namespace Barotrauma
return allAfflictions;
}
public void ServerWrite(NetBuffer msg)
public void ServerWrite(IWriteMessage msg)
{
List<Affliction> activeAfflictions = afflictions.FindAll(a => a.Strength > 0.0f && a.Strength >= a.Prefab.ActivationThreshold);
msg.Write((byte)activeAfflictions.Count);
foreach (Affliction affliction in activeAfflictions)
{
msg.WriteRangedInteger(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(affliction.Prefab));
msg.WriteRangedIntegerDeprecated(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(affliction.Prefab));
msg.WriteRangedSingle(
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
0.0f, affliction.Prefab.MaxStrength, 8);
@@ -758,8 +758,8 @@ namespace Barotrauma
msg.Write((byte)limbAfflictions.Count);
foreach (var limbAffliction in limbAfflictions)
{
msg.WriteRangedInteger(0, limbHealths.Count - 1, limbHealths.IndexOf(limbAffliction.First));
msg.WriteRangedInteger(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(limbAffliction.Second.Prefab));
msg.WriteRangedIntegerDeprecated(0, limbHealths.Count - 1, limbHealths.IndexOf(limbAffliction.First));
msg.WriteRangedIntegerDeprecated(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(limbAffliction.Second.Prefab));
msg.WriteRangedSingle(
MathHelper.Clamp(limbAffliction.Second.Strength, 0.0f, limbAffliction.Second.Prefab.MaxStrength),
0.0f, limbAffliction.Second.Prefab.MaxStrength, 8);
@@ -144,6 +144,15 @@ namespace Barotrauma
#if SERVER
if (GameMain.Server != null && Entity.Spawner != null)
{
if (GameMain.Server.EntityEventManager.UniqueEvents.Any(ev => ev.Entity == item))
{
string errorMsg = $"Error while spawning job items. Item {item.Name} created network events before the spawn event had been created.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Job.InitializeJobItem:EventsBeforeSpawning", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
GameMain.Server.EntityEventManager.UniqueEvents.RemoveAll(ev => ev.Entity == item);
GameMain.Server.EntityEventManager.Events.RemoveAll(ev => ev.Entity == item);
}
Entity.Spawner.CreateNetworkEvent(item, false);
}
#endif
@@ -36,7 +36,8 @@ namespace Barotrauma
Afflictions,
Buffs,
Tutorials,
UIStyle
UIStyle,
TraitorMissions
}
public class ContentPackage
@@ -79,6 +80,7 @@ namespace Barotrauma
ContentType.LevelGenerationParameters,
ContentType.RandomEvents,
ContentType.Missions,
ContentType.TraitorMissions,
ContentType.BackgroundCreaturePrefabs,
ContentType.RuinConfig,
ContentType.NPCConversations,
@@ -96,7 +98,7 @@ namespace Barotrauma
public string Path
{
get;
private set;
set;
}
public string SteamWorkshopUrl;
@@ -419,7 +421,7 @@ namespace Barotrauma
switch (contentFile.Type)
{
case ContentType.Submarine:
return path == "Submarines";
return path == "Submarines" || path == "Mods";
default:
return path == "Mods";
}
@@ -460,8 +462,9 @@ namespace Barotrauma
return Files.Where(f => f.Type == type).Select(f => f.Path);
}
public static void LoadAll(string folder)
public static void LoadAll()
{
string folder = ContentPackage.Folder;
if (!Directory.Exists(folder))
{
try
@@ -475,14 +478,23 @@ namespace Barotrauma
}
}
string[] files = Directory.GetFiles(folder, "*.xml");
List.Clear();
string[] files = Directory.GetFiles(folder, "*.xml");
foreach (string filePath in files)
{
ContentPackage package = new ContentPackage(filePath);
List.Add(package);
List.Add(new ContentPackage(filePath));
}
string[] modDirectories = Directory.GetDirectories("Mods");
foreach (string modDirectory in modDirectories)
{
if (System.IO.Path.GetFileName(modDirectory.TrimEnd(System.IO.Path.DirectorySeparatorChar)) == "ExampleMod") { continue; }
string modFilePath = System.IO.Path.Combine(modDirectory, Steam.SteamManager.MetadataFileName);
if (File.Exists(modFilePath))
{
List.Add(new ContentPackage(modFilePath));
}
}
}
@@ -505,7 +517,7 @@ namespace Barotrauma
public class ContentFile
{
public readonly string Path;
public string Path;
public ContentType Type;
public Workshop.Item WorkShopItem;
@@ -142,6 +142,7 @@ namespace Barotrauma
}
catch (Exception e)
{
handle.Exception = e;
DebugConsole.ThrowError("Coroutine \"" + handle.Name + "\" has thrown an exception", e);
}
}
@@ -182,7 +183,7 @@ namespace Barotrauma
{
if (handle.Thread.ThreadState.HasFlag(ThreadState.Stopped))
{
if ((CoroutineStatus)handle.Coroutine.Current == CoroutineStatus.Failure)
if (handle.Exception!=null || (CoroutineStatus)handle.Coroutine.Current == CoroutineStatus.Failure)
{
DebugConsole.ThrowError("Coroutine \"" + handle.Name + "\" has failed");
}
@@ -438,7 +438,7 @@ namespace Barotrauma
});
}));
commands.Add(new Command("banip", "banip [ip]: Ban the IP address from the server.", null));
commands.Add(new Command("banendpoint|banip", "banendpoint [endpoint]: Ban the IP address/SteamID from the server.", null));
commands.Add(new Command("teleportcharacter|teleport", "teleport [character name]: Teleport the specified character to the position of the cursor. If the name parameter is omitted, the controlled character will be teleported.", (string[] args) =>
{
@@ -674,6 +674,11 @@ namespace Barotrauma
},null));
#if DEBUG
commands.Add(new Command("crash", "crash: Crashes the game.", (string[] args) =>
{
throw new Exception("crash command issued");
}));
commands.Add(new Command("teleportsub", "teleportsub [start/end]: Teleport the submarine to the start or end of the level. WARNING: does not take outposts into account, so often leads to physics glitches. Only use for debugging.", (string[] args) =>
{
if (Submarine.MainSub == null || Level.Loaded == null) return;
@@ -960,6 +965,7 @@ namespace Barotrauma
}));
#if DEBUG
/*TODO: reimplement
commands.Add(new Command("simulatedlatency", "simulatedlatency [minimumlatencyseconds] [randomlatencyseconds]: applies a simulated latency to network messages. Useful for simulating real network conditions when testing the multiplayer locally.", (string[] args) =>
{
if (args.Count() < 2 || (GameMain.NetworkMember == null)) return;
@@ -1029,7 +1035,7 @@ namespace Barotrauma
}
#endif
NewMessage("Set packet duplication to " + (int)(duplicates * 100) + "%.", Color.White);
}));
}));*/
#endif
//"dummy commands" that only exist so that the server can give clients permissions to use them
@@ -1502,8 +1508,9 @@ namespace Barotrauma
public static void NewMessage(string msg, Color color, bool isCommand = false)
{
if (string.IsNullOrEmpty((msg))) return;
var newMsg = new ColoredText(msg, color, isCommand);
lock (queuedMessages)
{
queuedMessages.Enqueue(new ColoredText(msg, color, isCommand));
@@ -36,6 +36,8 @@ namespace Barotrauma
private List<ScriptedEventSet> selectedEventSets;
private EventManagerSettings settings;
private readonly bool isClient;
public float CurrentIntensity
{
@@ -51,13 +53,15 @@ namespace Barotrauma
{
events = new List<ScriptedEvent>();
selectedEventSets = new List<ScriptedEventSet>();
isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
}
public bool Enabled = true;
public void StartRound(Level level)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
if (isClient) { return; }
var suitableSettings = EventManagerSettings.List.FindAll(s =>
level.Difficulty >= s.MinLevelDifficulty &&
@@ -193,13 +197,13 @@ namespace Barotrauma
public void Update(float deltaTime)
{
if (!Enabled) return;
if (!Enabled) { return; }
//clients only calculate the intensity but don't create any events
//(the intensity is used for controlling the background music)
CalculateCurrentIntensity(deltaTime);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
if (isClient) { return; }
roundDuration += deltaTime;
@@ -32,7 +32,7 @@ namespace Barotrauma
private set { failureMessage = value; }
}
private string description;
protected string description;
public virtual string Description
{
get { return description; }
@@ -155,6 +155,13 @@ namespace Barotrauma
return false;
}
protected void ShowMessage(int index)
{
ShowMessageProjSpecific(index);
}
partial void ShowMessageProjSpecific(int index);
/// <summary>
/// End the mission and give a reward if it was completed successfully
/// </summary>
@@ -5,7 +5,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
enum MissionType
public enum MissionType
{
Random,
None,
@@ -28,6 +28,9 @@ namespace Barotrauma
{
monsterFile = prefab.ConfigElement.GetAttributeString("monsterfile", "");
monsterCount = prefab.ConfigElement.GetAttributeInt("monstercount", 1);
description = description.Replace("[monster]",
TextManager.Get("character." + System.IO.Path.GetFileNameWithoutExtension(monsterFile)));
}
public override void Start(Level level)
@@ -66,9 +69,9 @@ namespace Barotrauma
if (activeMonsters.Any()) { return; }
#if CLIENT
ShowMessage(state);
#endif
state = 1;
break;
}
@@ -94,16 +94,15 @@ namespace Barotrauma
if (item.ParentInventory != null) item.body.FarseerBody.IsKinematic = false;
if (item.CurrentHull?.Submarine == null) return;
#if CLIENT
ShowMessage(state);
#endif
state = 1;
break;
case 1:
if (!Submarine.MainSub.AtEndPosition && !Submarine.MainSub.AtStartPosition) return;
#if CLIENT
ShowMessage(state);
#endif
state = 2;
break;
}
@@ -6,7 +6,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
abstract class CampaignMode : GameMode
abstract partial class CampaignMode : GameMode
{
public readonly CargoManager CargoManager;
@@ -111,9 +111,8 @@ namespace Barotrauma
base.Update(deltaTime);
if (!IsRunning) { return; }
#if CLIENT
if (GameMain.Client != null) { return; }
#endif
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (!watchmenSpawned)
{
if (Level.Loaded.StartOutpost != null) { startWatchman = SpawnWatchman(Level.Loaded.StartOutpost); }
@@ -128,7 +127,7 @@ namespace Barotrauma
foreach (Character character in Character.CharacterList)
{
#if SERVER
if (string.IsNullOrEmpty(character.OwnerClientIP)) { continue; }
if (string.IsNullOrEmpty(character.OwnerClientEndPoint)) { continue; }
#else
if (!CrewManager.GetCharacters().Contains(character)) { continue; }
#endif
@@ -13,7 +13,7 @@ namespace Barotrauma
public readonly string Name;
public string ClientIP
public string ClientEndPoint
{
get;
private set;
@@ -42,7 +42,7 @@ namespace Barotrauma
public CharacterCampaignData(XElement element)
{
Name = element.GetAttributeString("name", "Unnamed");
ClientIP = element.GetAttributeString("ip", "");
ClientEndPoint = element.GetAttributeString("endpoint", null) ?? element.GetAttributeString("ip", "");
string steamID = element.GetAttributeString("steamid", "");
if (!string.IsNullOrEmpty(steamID))
{
@@ -69,7 +69,7 @@ namespace Barotrauma
{
XElement element = new XElement("CharacterCampaignData",
new XAttribute("name", Name),
new XAttribute("ip", ClientIP),
new XAttribute("endpoint", ClientEndPoint),
new XAttribute("steamid", SteamID));
CharacterInfo?.Save(element);
@@ -64,7 +64,7 @@ namespace Barotrauma
isRunning = true;
}
public virtual void MsgBox() { }
public virtual void ShowStartMessage() { }
public virtual void AddToGUIUpdateList()
{
@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Xml.Linq;
using Lidgren.Network;
using System.Collections.Generic;
using System.IO;
@@ -98,14 +97,11 @@ namespace Barotrauma
GameMain.GameSession.EndRound("");
//client character has spawned this round -> remove old data (and replace with an up-to-date one if the client still has an alive character)
characterData.RemoveAll(cd => cd.HasSpawned);
foreach (Client c in GameMain.Server.ConnectedClients)
{
if (c.HasSpawned)
{
//client has spawned this round -> remove old data (and replace with new one if the client still has an alive character)
characterData.RemoveAll(cd => cd.MatchesClient(c));
}
if (c.Character?.Info != null && !c.Character.IsDead)
{
characterData.Add(new CharacterCampaignData(c));
@@ -260,7 +260,7 @@ namespace Barotrauma
if (GameMode != null)
{
GameMode.MsgBox();
GameMode.ShowStartMessage();
if (GameMode is MultiPlayerCampaign mpCampaign && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
@@ -46,6 +46,8 @@ namespace Barotrauma
public bool PauseOnFocusLost { get; set; }
public bool MuteOnFocusLost { get; set; }
public bool DynamicRangeCompressionEnabled { get; set; }
public bool VoipAttenuationEnabled { get; set; }
public bool UseDirectionalVoiceChat { get; set; }
public enum VoiceMode
@@ -176,9 +178,9 @@ namespace Barotrauma
#if CLIENT
if (GameMain.SoundManager != null)
{
GameMain.SoundManager.SetCategoryGainMultiplier("default", soundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("ui", soundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", soundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("default", soundVolume, 0);
GameMain.SoundManager.SetCategoryGainMultiplier("ui", soundVolume, 0);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", soundVolume, 0);
}
#endif
}
@@ -191,7 +193,7 @@ namespace Barotrauma
{
musicVolume = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
GameMain.SoundManager?.SetCategoryGainMultiplier("music", musicVolume);
GameMain.SoundManager?.SetCategoryGainMultiplier("music", musicVolume, 0);
#endif
}
}
@@ -203,7 +205,7 @@ namespace Barotrauma
{
voiceChatVolume = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
GameMain.SoundManager?.SetCategoryGainMultiplier("voip", voiceChatVolume * 20.0f);
GameMain.SoundManager?.SetCategoryGainMultiplier("voip", voiceChatVolume * 20.0f, 0);
#endif
}
}
@@ -286,7 +288,7 @@ namespace Barotrauma
public GameSettings()
{
ContentPackage.LoadAll(ContentPackage.Folder);
ContentPackage.LoadAll();
CompletedTutorialNames = new List<string>();
LoadDefaultConfig();
@@ -443,6 +445,11 @@ namespace Barotrauma
LoadAudioSettings(doc);
LoadControls(doc);
LoadContentPackages(doc);
#if DEBUG
WindowMode = WindowMode.Windowed;
#endif
UnsavedSettings = false;
}
@@ -797,6 +804,8 @@ namespace Barotrauma
new XAttribute("voicechatvolume", voiceChatVolume),
new XAttribute("microphonevolume", microphoneVolume),
new XAttribute("muteonfocuslost", MuteOnFocusLost),
new XAttribute("dynamicrangecompressionenabled", DynamicRangeCompressionEnabled),
new XAttribute("voipattenuationenabled", VoipAttenuationEnabled),
new XAttribute("usedirectionalvoicechat", UseDirectionalVoiceChat),
new XAttribute("voicesetting", VoiceSetting),
new XAttribute("voicecapturedevice", VoiceCaptureDevice ?? ""),
@@ -910,7 +919,7 @@ namespace Barotrauma
}
AutoCheckUpdates = doc.Root.GetAttributeBool("autocheckupdates", AutoCheckUpdates);
sendUserStatistics = doc.Root.GetAttributeBool("senduserstatistics", sendUserStatistics);
QuickStartSubmarineName = doc.Root.GetAttributeString("quickstartsubmarine", "");
QuickStartSubmarineName = doc.Root.GetAttributeString("quickstartsub", QuickStartSubmarineName);
useSteamMatchmaking = doc.Root.GetAttributeBool("usesteammatchmaking", useSteamMatchmaking);
requireSteamAuthentication = doc.Root.GetAttributeBool("requiresteamauthentication", requireSteamAuthentication);
EnableSplashScreen = doc.Root.GetAttributeBool("enablesplashscreen", EnableSplashScreen);
@@ -997,8 +1006,11 @@ namespace Barotrauma
{
SoundVolume = audioSettings.GetAttributeFloat("soundvolume", SoundVolume);
MusicVolume = audioSettings.GetAttributeFloat("musicvolume", MusicVolume);
DynamicRangeCompressionEnabled = audioSettings.GetAttributeBool("dynamicrangecompressionenabled", DynamicRangeCompressionEnabled);
VoipAttenuationEnabled = audioSettings.GetAttributeBool("voipattenuationenabled", VoipAttenuationEnabled);
VoiceChatVolume = audioSettings.GetAttributeFloat("voicechatvolume", VoiceChatVolume);
MuteOnFocusLost = audioSettings.GetAttributeBool("muteonfocuslost", MuteOnFocusLost);
UseDirectionalVoiceChat = audioSettings.GetAttributeBool("usedirectionalvoicechat", UseDirectionalVoiceChat);
VoiceCaptureDevice = audioSettings.GetAttributeString("voicecapturedevice", VoiceCaptureDevice);
NoiseGateThreshold = audioSettings.GetAttributeFloat("noisegatethreshold", NoiseGateThreshold);
@@ -1108,6 +1120,8 @@ namespace Barotrauma
ChatOpen = true;
soundVolume = 0.5f;
musicVolume = 0.3f;
DynamicRangeCompressionEnabled = true;
VoipAttenuationEnabled = true;
voiceChatVolume = 0.5f;
microphoneVolume = 1.0f;
AutoCheckUpdates = true;
@@ -82,7 +82,7 @@ namespace Barotrauma.Items.Components
}
}
}
public DockingPort(Item item, XElement element)
: base(item, element)
{
@@ -160,7 +160,7 @@ namespace Barotrauma.Items.Components
if (DockingTarget != null)
{
Undock();
}
}
if (target.item.Submarine == item.Submarine)
{
@@ -168,7 +168,7 @@ namespace Barotrauma.Items.Components
DockingTarget = null;
return;
}
target.InitializeLinks();
if (!item.linkedTo.Contains(target.item)) item.linkedTo.Add(target.item);
@@ -193,7 +193,7 @@ namespace Barotrauma.Items.Components
Math.Sign(DockingTarget.item.WorldPosition.X - item.WorldPosition.X) :
Math.Sign(DockingTarget.item.WorldPosition.Y - item.WorldPosition.Y);
DockingTarget.DockingDir = -DockingDir;
if (door != null && DockingTarget.door != null)
{
WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap);
@@ -205,7 +205,7 @@ namespace Barotrauma.Items.Components
targetWayPoint.linkedTo.Add(myWayPoint);
}
}
CreateJoint(false);
#if SERVER
@@ -246,7 +246,7 @@ namespace Barotrauma.Items.Components
else if (DockingTarget.item.Submarine.PhysicsBody.Mass < item.Submarine.PhysicsBody.Mass ||
item.Submarine.IsOutpost)
{
DockingTarget.item.Submarine.SubBody.SetPosition(item.Submarine.SubBody.Position - ConvertUnits.ToDisplayUnits(jointDiff));
DockingTarget.item.Submarine.SubBody.SetPosition(DockingTarget.item.Submarine.SubBody.Position - ConvertUnits.ToDisplayUnits(jointDiff));
}
ConnectWireBetweenPorts();
@@ -401,7 +401,7 @@ namespace Barotrauma.Items.Components
}
private void CreateHulls()
{
{
var hullRects = new Rectangle[] { item.WorldRect, DockingTarget.item.WorldRect };
var subs = new Submarine[] { item.Submarine, DockingTarget.item.Submarine };
@@ -416,18 +416,18 @@ namespace Barotrauma.Items.Components
{
DockingTarget.CreateDoorBody();
}
if (IsHorizontal)
{
if (hullRects[0].Center.X > hullRects[1].Center.X)
{
hullRects = new Rectangle[] { DockingTarget.item.WorldRect, item.WorldRect };
subs = new Submarine[] { DockingTarget.item.Submarine,item.Submarine };
subs = new Submarine[] { DockingTarget.item.Submarine, item.Submarine };
}
hullRects[0] = new Rectangle(hullRects[0].Center.X, hullRects[0].Y, ((int)DockedDistance / 2), hullRects[0].Height);
hullRects[1] = new Rectangle(hullRects[1].Center.X - ((int)DockedDistance / 2), hullRects[1].Y, ((int)DockedDistance / 2), hullRects[1].Height);
//expand hulls if needed, so there's no empty space between the sub's hulls and docking port hulls
int leftSubRightSide = int.MinValue, rightSubLeftSide = int.MaxValue;
foreach (Hull hull in Hull.hullList)
@@ -478,7 +478,7 @@ namespace Barotrauma.Items.Components
hullRects[1].Width += rightHullDiff;
}
}
for (int i = 0; i < 2; i++)
{
hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition));
@@ -503,7 +503,7 @@ namespace Barotrauma.Items.Components
hullRects = new Rectangle[] { DockingTarget.item.WorldRect, item.WorldRect };
subs = new Submarine[] { DockingTarget.item.Submarine, item.Submarine };
}
hullRects[0] = new Rectangle(hullRects[0].X, hullRects[0].Y + (int)(-hullRects[0].Height + DockedDistance) / 2, hullRects[0].Width, ((int)DockedDistance / 2));
hullRects[1] = new Rectangle(hullRects[1].X, hullRects[1].Y - hullRects[1].Height / 2, hullRects[1].Width, ((int)DockedDistance / 2));
@@ -954,7 +954,7 @@ namespace Barotrauma.Items.Components
#endif
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(docked);
@@ -965,7 +965,7 @@ namespace Barotrauma.Items.Components
}
}
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
bool isDocked = msg.ReadBoolean();
@@ -313,8 +313,7 @@ namespace Barotrauma.Items.Components
PredictedState = null;
}
}
LinkedGap.Open = openState;
LinkedGap.Open = isBroken ? 1.0f : openState;
}
if (isClosing)
@@ -371,7 +370,7 @@ namespace Barotrauma.Items.Components
{
LinkedGap.AutoOrient();
}
LinkedGap.Open = openState;
LinkedGap.Open = isBroken ? 1.0f : openState;
LinkedGap.PassAmbientLight = Window != Rectangle.Empty;
}
@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Xml.Linq;
@@ -286,14 +285,21 @@ namespace Barotrauma.Items.Components
item.SetTransform(rightHand.SimPosition, 0.0f);
}
bool alreadySelected = character.HasEquippedItem(item);
if (picker.TrySelectItem(item) || picker.HasEquippedItem(item))
bool alreadyEquipped = character.HasEquippedItem(item);
bool canSelect = picker.TrySelectItem(item);
if (canSelect || picker.HasEquippedItem(item))
{
if (!canSelect)
{
character.DeselectItem(item);
}
item.body.Enabled = true;
IsActive = true;
#if SERVER
if (!alreadySelected) GameServer.Log(character.LogName + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
if (!alreadyEquipped) GameServer.Log(character.LogName + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
}
}
@@ -557,7 +563,7 @@ namespace Barotrauma.Items.Components
DeattachFromWall();
}
}
public override XElement Save(XElement parentElement)
{
if (!attachable)
@@ -581,8 +587,8 @@ namespace Barotrauma.Items.Components
return saveElement;
}
public override void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public override void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
base.ServerWrite(msg, c, extraData);
if (!attachable || body == null) { return; }
@@ -592,11 +598,11 @@ namespace Barotrauma.Items.Components
msg.Write(body.SimPosition.Y);
}
public override void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
base.ClientRead(type, msg, sendingTime);
bool shouldBeAttached = msg.ReadBoolean();
Vector2 simPosition = new Vector2(msg.ReadFloat(), msg.ReadFloat());
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
if (!attachable)
{
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
@@ -116,7 +115,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(deattachTimer);
}
@@ -56,11 +56,9 @@ namespace Barotrauma.Items.Components
public MeleeWeapon(Item item, XElement element)
: base(item, element)
{
//throwForce = ToolBox.GetAttributeFloat(element, "throwforce", 1.0f);
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "attack") continue;
if (subElement.Name.ToString().ToLowerInvariant() != "attack") { continue; }
attack = new Attack(subElement, item.Name + ", MeleeWeapon");
}
item.IsShootable = true;
@@ -70,23 +68,22 @@ namespace Barotrauma.Items.Components
public override bool Use(float deltaTime, Character character = null)
{
if (character == null || reloadTimer > 0.0f) return false;
if (Item.RequireAimToUse && !character.IsKeyDown(InputType.Aim) || hitting) return false;
if (character == null || reloadTimer > 0.0f) { return false; }
if (Item.RequireAimToUse && !character.IsKeyDown(InputType.Aim) || hitting) { return false; }
//don't allow hitting if the character is already hitting with another weapon
for (int i = 0; i < 2; i++ )
{
if (character.SelectedItems[i] == null || character.SelectedItems[i] == Item) continue;
if (character.SelectedItems[i] == null || character.SelectedItems[i] == Item) { continue; }
var otherWeapon = character.SelectedItems[i].GetComponent<MeleeWeapon>();
if (otherWeapon == null) continue;
if (otherWeapon.hitting) return false;
if (otherWeapon == null) { continue; }
if (otherWeapon.hitting) { return false; }
}
SetUser(character);
if (hitPos < MathHelper.PiOver4) return false;
if (hitPos < MathHelper.PiOver4) { return false; }
reloadTimer = reload;
@@ -94,21 +91,20 @@ namespace Barotrauma.Items.Components
item.body.FarseerBody.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall;
item.body.FarseerBody.OnCollision += OnCollision;
foreach (Limb l in character.AnimController.Limbs)
if (!character.AnimController.InWater)
{
//item.body.FarseerBody.IgnoreCollisionWith(l.body.FarseerBody);
if (character.AnimController.InWater) continue;
if (l.type == LimbType.LeftFoot || l.type == LimbType.LeftThigh || l.type == LimbType.LeftLeg) continue;
if (l.type == LimbType.Head || l.type == LimbType.Torso)
foreach (Limb l in character.AnimController.Limbs)
{
l.body.ApplyLinearImpulse(new Vector2(character.AnimController.Dir * 7.0f, -4.0f));
if (l.type == LimbType.LeftFoot || l.type == LimbType.LeftThigh || l.type == LimbType.LeftLeg) { continue; }
if (l.type == LimbType.Head || l.type == LimbType.Torso)
{
l.body.ApplyLinearImpulse(new Vector2(character.AnimController.Dir * 7.0f, -4.0f));
}
else
{
l.body.ApplyLinearImpulse(new Vector2(character.AnimController.Dir * 5.0f, -2.0f));
}
}
else
{
l.body.ApplyLinearImpulse(new Vector2(character.AnimController.Dir * 5.0f, -2.0f));
}
}
hitting = true;
@@ -121,7 +117,6 @@ namespace Barotrauma.Items.Components
public override void Drop(Character dropper)
{
base.Drop(dropper);
hitting = false;
hitPos = 0.0f;
}
@@ -133,17 +128,17 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (!item.body.Enabled) return;
if (!picker.HasSelectedItem(item)) IsActive = false;
if (!item.body.Enabled) { return; }
if (!picker.HasSelectedItem(item)) { IsActive = false; }
reloadTimer -= deltaTime;
if (reloadTimer < 0) { reloadTimer = 0; }
if (!picker.IsKeyDown(InputType.Aim) && !hitting) hitPos = 0.0f;
if (!picker.IsKeyDown(InputType.Aim) && !hitting) { hitPos = 0.0f; }
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
if (item.body.Dir != picker.AnimController.Dir) Flip();
if (item.body.Dir != picker.AnimController.Dir) { Flip(); }
AnimController ac = picker.AnimController;
@@ -236,16 +231,16 @@ namespace Barotrauma.Items.Components
if (f2.Body.UserData is Limb)
{
targetLimb = (Limb)f2.Body.UserData;
if (targetLimb.IsSevered || targetLimb.character == null) return false;
if (targetLimb.IsSevered || targetLimb.character == null) { return false; }
targetCharacter = targetLimb.character;
if (targetCharacter == picker) return false;
if (targetCharacter == picker){ return false; }
if (AllowHitMultiple)
{
if (hitTargets.Contains(targetCharacter)) return false;
if (hitTargets.Contains(targetCharacter)) { return false; }
}
else
{
if (hitTargets.Any(t => t is Character)) return false;
if (hitTargets.Any(t => t is Character)) { return false; }
}
hitTargets.Add(targetCharacter);
}
@@ -256,11 +251,11 @@ namespace Barotrauma.Items.Components
targetLimb = targetCharacter.AnimController.GetLimb(LimbType.Torso); //Otherwise armor can be bypassed in strange ways
if (AllowHitMultiple)
{
if (hitTargets.Contains(targetCharacter)) return false;
if (hitTargets.Contains(targetCharacter)) { return false; }
}
else
{
if (hitTargets.Any(t => t is Character)) return false;
if (hitTargets.Any(t => t is Character)) { return false; }
}
hitTargets.Add(targetCharacter);
}
@@ -269,11 +264,11 @@ namespace Barotrauma.Items.Components
targetStructure = (Structure)f2.Body.UserData;
if (AllowHitMultiple)
{
if (hitTargets.Contains(targetStructure)) return true;
if (hitTargets.Contains(targetStructure)) { return true; }
}
else
{
if (hitTargets.Any(t => t is Structure)) return true;
if (hitTargets.Any(t => t is Structure)) { return true; }
}
hitTargets.Add(targetStructure);
}
@@ -303,8 +298,8 @@ namespace Barotrauma.Items.Components
return false;
}
}
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return true;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return true; }
#if SERVER
if (GameMain.Server != null && targetCharacter != null) //TODO: Log structure hits
@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Lidgren.Network;
namespace Barotrauma.Items.Components
{
@@ -233,12 +232,12 @@ namespace Barotrauma.Items.Components
}
}
public virtual void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public virtual void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(activePicker == null ? (ushort)0 : activePicker.ID);
}
public virtual void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
ushort pickerID = msg.ReadUInt16();
if (pickerID == 0)
@@ -54,6 +54,9 @@ namespace Barotrauma.Items.Components
[Serialize(false, false)]
public bool RepairMultiple { get; set; }
[Serialize(false, false)]
public bool RepairThroughHoles { get; set; }
[Serialize(0.0f, false)]
public float FireProbability { get; set; }
@@ -146,10 +149,24 @@ namespace Barotrauma.Items.Components
}
}
Vector2 targetPosition = item.WorldPosition;
targetPosition += new Vector2(
(float)Math.Cos(item.body.Rotation),
(float)Math.Sin(item.body.Rotation)) * Range * item.body.Dir;
Vector2 rayStart;
Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos;
Vector2 barrelPos = item.SimPosition + ConvertUnits.ToSimUnits(TransformedBarrelPos);
//make sure there's no obstacles between the base of the item (or the shoulder of the character) and the end of the barrel
if (Submarine.PickBody(sourcePos, barrelPos, collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
{
//no obstacles -> we start the raycast at the end of the barrel
rayStart = ConvertUnits.ToSimUnits(item.WorldPosition + TransformedBarrelPos);
}
else
{
rayStart = ConvertUnits.ToSimUnits(item.WorldPosition);
}
Vector2 rayEnd = rayStart +
ConvertUnits.ToSimUnits(new Vector2(
(float)Math.Cos(item.body.Rotation),
(float)Math.Sin(item.body.Rotation)) * Range * item.body.Dir);
List<Body> ignoredBodies = new List<Body>();
foreach (Limb limb in character.AnimController.Limbs)
@@ -161,11 +178,8 @@ namespace Barotrauma.Items.Components
IsActive = true;
activeTimer = 0.1f;
Vector2 rayStart = ConvertUnits.ToSimUnits(item.WorldPosition);
Vector2 rayEnd = ConvertUnits.ToSimUnits(targetPosition);
debugRayStartPos = item.WorldPosition;
debugRayStartPos = ConvertUnits.ToDisplayUnits(rayStart);
debugRayEndPos = ConvertUnits.ToDisplayUnits(rayEnd);
if (character.Submarine == null)
@@ -203,7 +217,7 @@ namespace Barotrauma.Items.Components
float lastPickedFraction = 0.0f;
if (RepairMultiple)
{
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true);
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: RepairThroughHoles, allowInsideFixture: true);
lastPickedFraction = Submarine.LastPickedFraction;
Type lastHitType = null;
hitCharacters.Clear();
@@ -243,7 +257,7 @@ namespace Barotrauma.Items.Components
{
FixBody(user, deltaTime, degreeOfSuccess,
Submarine.PickBody(rayStart, rayEnd,
ignoredBodies, collisionCategories, ignoreSensors: false,
ignoredBodies, collisionCategories, ignoreSensors: RepairThroughHoles,
customPredicate: (Fixture f) => { return f?.Body?.UserData != null; },
allowInsideFixture: true));
lastPickedFraction = Submarine.LastPickedFraction;
@@ -324,6 +338,7 @@ namespace Barotrauma.Items.Components
}
else if (targetBody.UserData is Character targetCharacter)
{
if (targetCharacter.Removed) { return false; }
targetCharacter.LastDamageSource = item;
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new List<ISerializableEntity>() { targetCharacter });
FixCharacterProjSpecific(user, deltaTime, targetCharacter);
@@ -331,6 +346,7 @@ namespace Barotrauma.Items.Components
}
else if (targetBody.UserData is Limb targetLimb)
{
if (targetLimb.character == null || targetLimb.character.Removed) { return false; }
targetLimb.character.LastDamageSource = item;
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new List<ISerializableEntity>() { targetLimb.character, targetLimb });
FixCharacterProjSpecific(user, deltaTime, targetLimb.character);
@@ -506,9 +522,9 @@ namespace Barotrauma.Items.Components
if (propertyName != "stuck") { continue; }
if (door.SerializableProperties == null || !door.SerializableProperties.TryGetValue(propertyName, out SerializableProperty property)) { continue; }
object value = property.GetValue(target);
if (value.GetType() == typeof(float))
if (door.Stuck > 0)
{
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, (float)value / 100, Color.DarkGray * 0.5f, Color.White);
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White);
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
}
}
@@ -6,12 +6,10 @@ namespace Barotrauma.Items.Components
{
class Throwable : Holdable
{
float throwForce;
private float throwForce, throwPos;
private bool throwing, throwDone;
float throwPos;
bool throwing;
bool throwDone;
private bool midAir;
[Serialize(1.0f, false)]
public float ThrowForce
@@ -57,7 +55,17 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (!item.body.Enabled) return;
if (!item.body.Enabled) { return; }
if (midAir)
{
if (item.body.LinearVelocity.LengthSquared() < 0.01f)
{
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform;
midAir = false;
}
return;
}
if (picker == null || picker.Removed || !picker.HasSelectedItem(item))
{
IsActive = false;
@@ -113,6 +121,10 @@ namespace Barotrauma.Items.Components
item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
//disable platform collisions until the item comes back to rest again
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
midAir = true;
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
ac.GetLimb(LimbType.Torso).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
@@ -82,13 +82,14 @@ namespace Barotrauma.Items.Components
}
}
#endif
if (AITarget != null) AITarget.Enabled = value;
isActive = value;
}
}
private bool drawable = true;
public List<PropertyConditional> IsActiveConditionals;
public bool Drawable
{
get { return drawable; }
@@ -208,11 +209,6 @@ namespace Barotrauma.Items.Components
set;
}
public AITarget AITarget
{
get;
private set;
}
/// <summary>
/// How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
@@ -267,6 +263,15 @@ namespace Barotrauma.Items.Components
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "activeconditional":
case "isactive":
IsActiveConditionals = IsActiveConditionals ?? new List<PropertyConditional>();
foreach (XAttribute attribute in subElement.Attributes())
{
if (attribute.Name.ToString().ToLowerInvariant() == "targetitemcomponent") { continue; }
IsActiveConditionals.Add(new PropertyConditional(attribute));
}
break;
case "requireditem":
case "requireditems":
RelatedItem ri = RelatedItem.Load(subElement, item.Name);
@@ -308,12 +313,6 @@ namespace Barotrauma.Items.Components
effectList.Add(statusEffect);
break;
case "aitarget":
AITarget = new AITarget(item, subElement)
{
Enabled = isActive
};
break;
default:
if (LoadElemProjSpecific(subElement)) break;
@@ -474,12 +473,6 @@ namespace Barotrauma.Items.Components
delayedCorrectionCoroutine = null;
}
if (AITarget != null)
{
AITarget.Remove();
AITarget = null;
}
RemoveComponentSpecific();
}
@@ -496,11 +489,6 @@ namespace Barotrauma.Items.Components
loopingSoundChannel = null;
}
#endif
if (AITarget != null)
{
AITarget.Remove();
AITarget = null;
}
ShallowRemoveComponentSpecific();
}
@@ -788,6 +776,7 @@ namespace Barotrauma.Items.Components
public virtual void Reset()
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, originalElement);
if (this is Pickable) { canBePicked = true; }
ParseMsg();
OverrideRequiredItems(originalElement);
}
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using System;
using System.Linq;
using System.Xml.Linq;
@@ -84,6 +83,8 @@ namespace Barotrauma.Items.Components
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
if (progressTimer > deconstructTime)
{
int emptySlots = outputContainer.Inventory.Items.Where(i => i == null).Count();
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
{
float percentageHealth = targetItem.Condition / targetItem.Prefab.Health;
@@ -100,13 +101,14 @@ namespace Barotrauma.Items.Components
itemPrefab.Health * deconstructProduct.OutCondition;
//container full, drop the items outside the deconstructor
if (outputContainer.Inventory.Items.All(i => i != null))
if (emptySlots <= 0)
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, item.Position, item.Submarine, condition);
}
else
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, outputContainer.Inventory, condition);
emptySlots--;
}
}
@@ -196,6 +198,7 @@ namespace Barotrauma.Items.Components
if (inputContainer.Inventory.Items.All(i => i == null)) { active = false; }
IsActive = active;
currPowerConsumption = IsActive ? powerConsumption : 0.0f;
#if SERVER
if (user != null)
@@ -3,7 +3,6 @@ using System;
using System.Globalization;
using System.Xml.Linq;
using Barotrauma.Networking;
using Lidgren.Network;
namespace Barotrauma.Items.Components
{
@@ -98,11 +97,17 @@ namespace Barotrauma.Items.Components
UpdatePropellerDamage(deltaTime);
if (item.AiTarget != null)
{
var aiTarget = item.AiTarget;
aiTarget.SoundRange = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, currForce.Length() / maxForce);
}
if (item.CurrentHull != null)
{
item.CurrentHull.AiTarget.SoundRange = Math.Max(currForce.Length(), item.CurrentHull.AiTarget.SoundRange);
var aiTarget = item.CurrentHull.AiTarget;
float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, currForce.Length() / maxForce);
aiTarget.SoundRange = Math.Max(noise, aiTarget.SoundRange);
}
#if CLIENT
for (int i = 0; i < 5; i++)
{
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -12,6 +12,8 @@ namespace Barotrauma.Items.Components
private float maxFlow;
private float? targetLevel;
private float controlLockTimer;
private bool hasPower;
@@ -57,18 +59,24 @@ namespace Barotrauma.Items.Components
currFlow = 0.0f;
hasPower = false;
controlLockTimer -= deltaTime;
if (targetLevel != null)
{
float hullPercentage = 0.0f;
if (item.CurrentHull != null) hullPercentage = (item.CurrentHull.WaterVolume / item.CurrentHull.Volume) * 100.0f;
if (item.CurrentHull != null) { hullPercentage = (item.CurrentHull.WaterVolume / item.CurrentHull.Volume) * 100.0f; }
FlowPercentage = ((float)targetLevel - hullPercentage) * 10.0f;
if (controlLockTimer <= 0.0f)
{
targetLevel = null;
}
}
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
//pumps consume more power when in a bad condition
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.MaxCondition);
if (voltage < minVoltage) return;
if (voltage < minVoltage) { return; }
UpdateProjSpecific(deltaTime);
@@ -109,6 +117,7 @@ namespace Barotrauma.Items.Components
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempSpeed))
{
flowPercentage = MathHelper.Clamp(tempSpeed, -100.0f, 100.0f);
controlLockTimer = 0.1f;
}
}
else if (connection.Name == "set_targetlevel")
@@ -116,6 +125,7 @@ namespace Barotrauma.Items.Components
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempTarget))
{
targetLevel = MathHelper.Clamp((tempTarget + 100.0f) / 2.0f, 0.0f, 100.0f);
controlLockTimer = 0.1f;
}
}
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -271,7 +270,7 @@ namespace Barotrauma.Items.Components
//calculate how much external power there is in the grid
//(power coming from somewhere else than this reactor, e.g. batteries)
float externalPower = CurrPowerConsumption - pt.CurrPowerConsumption;
float externalPower = Math.Max(CurrPowerConsumption - pt.CurrPowerConsumption, 0);
//reduce the external power from the load to prevent overloading the grid
load = Math.Max(load, pt.PowerLoad - externalPower);
}
@@ -288,10 +287,17 @@ namespace Barotrauma.Items.Components
if (item.CurrentHull != null)
{
//the sound can be heard from 20 000 display units away when running at full power
item.CurrentHull.SoundRange = Math.Max(
(-currPowerConsumption / MaxPowerOutput) * 20000.0f,
item.CurrentHull.AiTarget.SoundRange);
var aiTarget = item.CurrentHull.AiTarget;
float range = Math.Abs(currPowerConsumption) / MaxPowerOutput;
float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
aiTarget.SoundRange = Math.Max(aiTarget.SoundRange, noise);
}
if (item.AiTarget != null)
{
var aiTarget = item.AiTarget;
float range = Math.Abs(currPowerConsumption) / MaxPowerOutput;
aiTarget.SoundRange = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
}
}
@@ -439,6 +445,8 @@ namespace Barotrauma.Items.Components
{
base.UpdateBroken(deltaTime, cam);
item.SendSignal(0, ((int)(temperature * 100.0f)).ToString(), "temperature_out", null);
currPowerConsumption = 0.0f;
Temperature -= deltaTime * 1000.0f;
targetFissionRate = Math.Max(targetFissionRate - deltaTime * 10.0f, 0.0f);
@@ -114,9 +114,9 @@ namespace Barotrauma.Items.Components
if (value == Mode.Passive)
{
currentPingIndex = -1;
if (item.CurrentHull != null)
if (item.AiTarget != null)
{
item.CurrentHull.AiTarget.SectorDegrees = 360.0f;
item.AiTarget.SectorDegrees = 360.0f;
}
}
#if CLIENT
@@ -168,15 +168,10 @@ namespace Barotrauma.Items.Components
var activePing = activePings[currentPingIndex];
if (activePing.State > 1.0f)
{
if (item.CurrentHull != null)
{
item.CurrentHull.AiTarget.SoundRange = Math.Max(Range * activePing.State / zoom, item.CurrentHull.AiTarget.SoundRange);
item.CurrentHull.AiTarget.SectorDegrees = activePing.IsDirectional ? DirectionalPingSector : 360.0f;
item.CurrentHull.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
}
if (item.AiTarget != null)
{
item.AiTarget.SoundRange = Math.Max(Range * activePing.State / zoom, item.AiTarget.SoundRange);
float range = MathUtils.InverseLerp(item.AiTarget.MinSoundRange, item.AiTarget.MaxSoundRange, Range * activePing.State / zoom);
item.AiTarget.SoundRange = MathHelper.Lerp(item.AiTarget.MinSoundRange, item.AiTarget.MaxSoundRange, range);
item.AiTarget.SectorDegrees = activePing.IsDirectional ? DirectionalPingSector : 360.0f;
item.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
}
@@ -200,9 +195,9 @@ namespace Barotrauma.Items.Components
}
else
{
if (item.CurrentHull != null)
if (item.AiTarget != null)
{
item.CurrentHull.AiTarget.SectorDegrees = 360.0f;
item.AiTarget.SectorDegrees = 360.0f;
}
currentPingIndex = -1;
aiPingCheckPending = false;
@@ -345,7 +340,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Client c)
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
{
bool isActive = msg.ReadBoolean();
bool directionalPing = useDirectionalPing;
@@ -388,7 +383,7 @@ namespace Barotrauma.Items.Components
#endif
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(currentMode == Mode.Active);
if (currentMode == Mode.Active)
@@ -522,7 +522,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c)
public void ServerRead(ClientNetObject type, IReadMessage msg, Barotrauma.Networking.Client c)
{
bool autoPilot = msg.ReadBoolean();
bool dockingButtonClicked = msg.ReadBoolean();
@@ -537,8 +537,8 @@ namespace Barotrauma.Items.Components
if (maintainPos)
{
newPosToMaintain = new Vector2(
msg.ReadFloat(),
msg.ReadFloat());
msg.ReadSingle(),
msg.ReadSingle());
}
else
{
@@ -547,7 +547,7 @@ namespace Barotrauma.Items.Components
}
else
{
newSteeringInput = new Vector2(msg.ReadFloat(), msg.ReadFloat());
newSteeringInput = new Vector2(msg.ReadSingle(), msg.ReadSingle());
}
if (!item.CanClientAccess(c)) return;
@@ -587,7 +587,7 @@ namespace Barotrauma.Items.Components
unsentChanges = true;
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Barotrauma.Networking.Client c, object[] extraData = null)
{
msg.Write(autoPilot);
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -64,6 +64,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, false)]
public bool Overload
{
get;
set;
}
//can the component transfer power
private bool canTransfer;
public bool CanTransfer
@@ -115,6 +122,8 @@ namespace Barotrauma.Items.Components
{
base.UpdateBroken(deltaTime, cam);
Overload = false;
if (!isBroken)
{
powerLoad = 0.0f;
@@ -128,7 +137,8 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
RefreshConnections();
if (!CanTransfer) return;
if (!CanTransfer) { return; }
if (isBroken)
{
@@ -143,6 +153,8 @@ namespace Barotrauma.Items.Components
return;
}
Overload = false;
//reset and recalculate the power generated/consumed
//by the constructions connected to the grid
fullPower = 0.0f;
@@ -156,10 +168,11 @@ namespace Barotrauma.Items.Components
foreach (Powered p in connectedList)
{
PowerTransfer pt = p as PowerTransfer;
if (pt == null || pt.updateCount == 0) continue;
if (pt == null || pt.updateCount == 0) { continue; }
if (pt is RelayComponent != this is RelayComponent) continue;
if (pt is RelayComponent != this is RelayComponent) { continue; }
pt.Overload = false;
pt.powerLoad += (fullLoad - pt.powerLoad) / inertia;
pt.currPowerConsumption += (-fullPower - pt.currPowerConsumption) / inertia;
@@ -173,38 +186,38 @@ namespace Barotrauma.Items.Components
pt.Item.SendSignal(0, "", "power", null, voltage);
pt.Item.SendSignal(0, "", "power_out", null, voltage);
#if CLIENT
//damage the item if voltage is too high
//(except if running as a client)
if (GameMain.Client != null) continue;
#endif
//items in a bad condition are more sensitive to overvoltage
float maxOverVoltage = MathHelper.Lerp(OverloadVoltage * 0.75f, OverloadVoltage, item.Condition / item.MaxCondition);
float maxOverVoltage = MathHelper.Lerp(OverloadVoltage * 0.75f, OverloadVoltage, pt.item.Condition / pt.item.MaxCondition);
maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
//if the item can't be fixed, don't allow it to break
if (!item.Repairables.Any() || !CanBeOverloaded) continue;
if (!pt.item.Repairables.Any() || !pt.CanBeOverloaded) { continue; }
//relays don't blow up if the power is higher than load, only if the output is high enough
//(i.e. enough power passing through the relay)
if (this is RelayComponent) continue;
if (pt is RelayComponent) { continue; }
if (-pt.currPowerConsumption < Math.Max(pt.powerLoad, 200.0f) * maxOverVoltage) continue;
if (-pt.currPowerConsumption < Math.Max(pt.powerLoad, 200.0f) * maxOverVoltage) { continue; }
pt.Overload = true;
#if CLIENT
//damage the item if voltage is too high
//(except if running as a client)
if (GameMain.Client != null) { continue; }
#endif
float prevCondition = pt.item.Condition;
pt.item.Condition -= deltaTime * 10.0f;
if (pt.item.Condition <= 0.0f && prevCondition > 0.0f)
{
#if CLIENT
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: pt.item.CurrentHull);
Vector2 baseVel = Rand.Vector(300.0f);
for (int i = 0; i < 10; i++)
{
var particle = GameMain.ParticleManager.CreateParticle("spark", pt.item.WorldPosition,
baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull);
baseVel + Rand.Vector(100.0f), 0.0f, pt.item.CurrentHull);
if (particle != null) particle.Size *= Rand.Range(0.5f, 1.0f);
}
@@ -214,8 +227,8 @@ namespace Barotrauma.Items.Components
GameMain.GameSession.EventManager.CurrentIntensity : 0.5f;
//higher probability for fires if the current intensity is low
if (FireProbability > 0.0f &&
Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(FireProbability, FireProbability * 0.1f, currentIntensity))
if (pt.FireProbability > 0.0f &&
Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(pt.FireProbability, pt.FireProbability * 0.1f, currentIntensity))
{
new FireSource(pt.item.WorldPosition);
}
@@ -409,11 +409,11 @@ namespace Barotrauma.Items.Components
private bool OnProjectileCollision(Fixture target, Vector2 collisionNormal)
{
if (User != null && User.Removed) User = null;
if (User != null && User.Removed) { User = null; }
if (IgnoredBodies.Contains(target.Body)) return false;
if (IgnoredBodies.Contains(target.Body)) { return false; }
if (target.UserData is Item) return false;
if (target.UserData is Item) { return false; }
if (target.CollisionCategories == Physics.CollisionCharacter && !(target.Body.UserData is Limb))
{
@@ -447,10 +447,21 @@ namespace Barotrauma.Items.Components
if (attack != null) { attackResult = attack.DoDamage(User, structure, item.WorldPosition, 1.0f); }
}
if (character != null) character.LastDamageSource = item;
ApplyStatusEffects(ActionType.OnUse, 1.0f, character, target.Body.UserData as Limb, user: user);
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character, target.Body.UserData as Limb, user: user);
if (character != null) { character.LastDamageSource = item; }
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
ApplyStatusEffects(ActionType.OnUse, 1.0f, character, target.Body.UserData as Limb, user: user);
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character, target.Body.UserData as Limb, user: user);
#if SERVER
if (GameMain.NetworkMember.IsServer)
{
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse });
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact });
}
#endif
}
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
item.body.CollisionCategories = Physics.CollisionItem;
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
@@ -10,12 +9,15 @@ namespace Barotrauma.Items.Components
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
{
public static float SkillIncreasePerRepair = 5.0f;
public static float SkillIncreasePerSabotage = 3.0f;
private string header;
private float deteriorationTimer;
private float deteriorateAlwaysResetTimer;
bool wasBroken;
bool wasGoodCondition;
public float LastActiveTime;
@@ -40,14 +42,21 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(50.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The item won't deteriorate spontaneously if the condition is below this value. For example, if set to 10, the condition will spontaneously drop to 10 and then stop dropping (unless the item is damaged further by external factors).")]
[Serialize(50.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The item won't deteriorate spontaneously if the condition is below this value. For example, if set to 10, the condition will spontaneously drop to 10 and then stop dropping (unless the item is damaged further by external factors). Percentages of max condition.")]
public float MinDeteriorationCondition
{
get;
set;
}
[Serialize(80.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The condition of the item has to be below this before the repair UI becomes usable.")]
[Serialize(0f, true)]
public float MinSabotageCondition
{
get;
set;
}
[Serialize(80.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The condition of the item has to be below this before the repair UI becomes usable. Percentages of max condition.")]
public float ShowRepairUIThreshold
{
get;
@@ -76,16 +85,20 @@ namespace Barotrauma.Items.Components
set;
}
private Character currentFixer;
public Character CurrentFixer
public Character CurrentFixer { get; private set; }
public enum FixActions : int
{
get { return currentFixer; }
set
{
if (currentFixer == value || item.IsFullCondition) return;
if (currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None;
currentFixer = value;
}
None = 0,
Repair = 1,
Sabotage = 2
}
private FixActions currentFixerAction = FixActions.None;
public FixActions CurrentFixerAction
{
get => currentFixerAction;
private set { currentFixerAction = value; }
}
public Repairable(Item item, XElement element)
@@ -105,18 +118,41 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded()
{
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
#if SERVER
//let the clients know the initial deterioration delay
item.CreateServerEvent(this);
#endif
}
partial void InitProjSpecific(XElement element);
public void StartRepairing(Character character)
public bool StartRepairing(Character character, FixActions action)
{
CurrentFixer = character;
if (character == null || character.IsDead || action == FixActions.None)
{
DebugConsole.ThrowError("Invalid repair command!");
return false;
}
else
{
CurrentFixer = character;
CurrentFixerAction = action;
return true;
}
}
public bool StopRepairing(Character character)
{
if (CurrentFixer == character)
{
CurrentFixer.AnimController.Anim = AnimController.Animation.None;
CurrentFixer = null;
currentFixerAction = FixActions.None;
#if SERVER
item.CreateServerEvent(this);
#endif
return true;
}
else
{
return false;
}
}
public override void UpdateBroken(float deltaTime, Camera cam)
@@ -129,7 +165,7 @@ namespace Barotrauma.Items.Components
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
item.Condition = item.Prefab.Health;
#if SERVER
//let the clients know the initial deterioration delay
//let the clients know the deterioration delay
item.CreateServerEvent(this);
#endif
}
@@ -140,6 +176,18 @@ namespace Barotrauma.Items.Components
if (CurrentFixer == null)
{
if (deteriorateAlwaysResetTimer > 0.0f)
{
deteriorateAlwaysResetTimer -= deltaTime;
if (deteriorateAlwaysResetTimer <= 0.0f)
{
DeteriorateAlways = false;
#if SERVER
//let the clients know the deterioration delay
item.CreateServerEvent(this);
#endif
}
}
if (!ShouldDeteriorate()) { return; }
if (item.Condition > 0.0f)
{
@@ -155,7 +203,7 @@ namespace Barotrauma.Items.Components
return;
}
if (item.Condition > MinDeteriorationCondition)
if (item.ConditionPercentage > MinDeteriorationCondition)
{
item.Condition -= DeteriorationSpeed * deltaTime;
}
@@ -163,10 +211,9 @@ namespace Barotrauma.Items.Components
return;
}
if (Item.IsFullCondition || CurrentFixer.SelectedConstruction != item || !currentFixer.CanInteractWith(item))
if (CurrentFixer != null && (CurrentFixer.SelectedConstruction != item || !CurrentFixer.CanInteractWith(item) || CurrentFixer.IsDead))
{
currentFixer.AnimController.Anim = AnimController.Animation.None;
currentFixer = null;
StopRepairing(CurrentFixer);
return;
}
@@ -177,44 +224,89 @@ namespace Barotrauma.Items.Components
float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f;
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it
if (item.Condition < ShowRepairUIThreshold)
if (item.ConditionPercentage < ShowRepairUIThreshold)
{
wasBroken = true;
}
if (item.ConditionPercentage > MinSabotageCondition)
{
wasGoodCondition = true;
}
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f)
if (currentFixerAction == FixActions.Repair)
{
item.Condition = item.MaxCondition;
if (fixDuration <= 0.0f)
{
item.Condition = item.MaxCondition;
}
else
{
float conditionIncrease = deltaTime / (fixDuration / item.MaxCondition);
item.Condition += conditionIncrease;
#if SERVER
GameMain.Server.KarmaManager.OnItemRepaired(CurrentFixer, this, conditionIncrease);
#endif
}
if (item.IsFullCondition)
{
if (wasBroken)
{
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
}
SteamAchievementManager.OnItemRepaired(item, CurrentFixer);
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
wasBroken = false;
}
StopRepairing(CurrentFixer);
}
}
else if (currentFixerAction == FixActions.Sabotage)
{
if (fixDuration <= 0.0f)
{
item.Condition = item.MaxCondition * (MinSabotageCondition / 100);
}
else
{
float conditionDecrease = deltaTime / (fixDuration / item.MaxCondition);
item.Condition -= conditionDecrease;
}
if (item.ConditionPercentage <= MinSabotageCondition)
{
if (wasGoodCondition)
{
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreasePerSabotage / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
}
deteriorationTimer = 0.0f;
deteriorateAlwaysResetTimer = item.Condition / DeteriorationSpeed;
DeteriorateAlways = true;
item.Condition = item.MaxCondition * (MinSabotageCondition / 100);
wasGoodCondition = false;
}
StopRepairing(CurrentFixer);
}
}
else
{
float conditionIncrease = deltaTime / (fixDuration / item.MaxCondition);
item.Condition += conditionIncrease;
#if SERVER
GameMain.Server.KarmaManager.OnItemRepaired(CurrentFixer, this, conditionIncrease);
#endif
}
if (wasBroken && item.IsFullCondition)
{
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
}
SteamAchievementManager.OnItemRepaired(item, currentFixer);
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
wasBroken = false;
#if SERVER
item.CreateServerEvent(this);
#endif
throw new NotImplementedException(currentFixerAction.ToString());
}
}
partial void UpdateProjSpecific(float deltaTime);
private bool ShouldDeteriorate()
@@ -218,6 +218,16 @@ namespace Barotrauma.Items.Components
public void SetWire(int index, Wire wire)
{
Wire previousWire = wires[index];
if (wire != previousWire && previousWire != null)
{
var otherConnection = previousWire.OtherConnection(this);
if (otherConnection != null)
{
otherConnection.recipientsDirty = true;
}
}
wires[index] = wire;
recipientsDirty = true;
if (wire != null)
@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -19,7 +18,9 @@ namespace Barotrauma.Items.Components
/// Wires that have been disconnected from the panel, but not removed completely (visible at the bottom of the connection panel).
/// </summary>
public readonly HashSet<Wire> DisconnectedWires = new HashSet<Wire>();
private List<ushort> disconnectedWireIds;
[Serialize(false, true), Editable(ToolTip = "Locked connection panels cannot be rewired in-game.")]
public bool Locked
{
@@ -64,6 +65,19 @@ namespace Barotrauma.Items.Components
{
c.ConnectLinked();
}
if (disconnectedWireIds != null)
{
foreach (ushort disconnectedWireId in disconnectedWireIds)
{
if (!(Entity.FindEntityByID(disconnectedWireId) is Item wireItem)) { continue; }
Wire wire = wireItem.GetComponent<Wire>();
if (wire != null)
{
DisconnectedWires.Add(wire);
}
}
}
}
public override void OnItemLoaded()
@@ -193,6 +207,8 @@ namespace Barotrauma.Items.Components
{
loadedConnections[i].wireId.CopyTo(Connections[i].wireId, 0);
}
disconnectedWireIds = element.GetAttributeUshortArray("disconnectedwires", new ushort[0]).ToList();
}
public override XElement Save(XElement parentElement)
@@ -204,6 +220,11 @@ namespace Barotrauma.Items.Components
c.Save(componentElement);
}
if (DisconnectedWires.Count > 0)
{
componentElement.Add(new XAttribute("disconnectedwires", string.Join(",", DisconnectedWires.Select(w => w.Item.ID))));
}
return componentElement;
}
@@ -214,6 +235,14 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
foreach (Wire wire in DisconnectedWires.ToList())
{
if (wire.OtherConnection(null) == null) //wire not connected to anything else
{
wire.Item.Drop(null);
}
}
DisconnectedWires.Clear();
foreach (Connection c in Connections)
{
@@ -233,7 +262,7 @@ namespace Barotrauma.Items.Components
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
foreach (Connection connection in Connections)
{
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
@@ -2,7 +2,6 @@
using System;
using System.Xml.Linq;
using Barotrauma.Networking;
using Lidgren.Network;
#if CLIENT
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Lights;
@@ -251,10 +250,6 @@ namespace Barotrauma.Items.Components
light.Range = range;
#endif
}
if (AITarget != null)
{
UpdateAITarget(AITarget);
}
if (item.AiTarget != null)
{
UpdateAITarget(item.AiTarget);
@@ -267,6 +262,7 @@ namespace Barotrauma.Items.Components
public override void UpdateBroken(float deltaTime, Camera cam)
{
light.Color = Color.Transparent;
lightBrightness = 0.0f;
}
protected override void RemoveComponentSpecific()
@@ -298,7 +294,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(IsOn);
}
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
@@ -104,12 +103,12 @@ namespace Barotrauma.Items.Components
IsOn = on;
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(isOn);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
SetState(msg.ReadBoolean(), true);
}
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -282,7 +281,7 @@ namespace Barotrauma.Items.Components
Structure attachTarget = Structure.GetAttachTarget(item.WorldPosition);
canPlaceNode = attachTarget != null;
sub = attachTarget?.Submarine;
sub = sub ?? attachTarget?.Submarine;
newNodePos = sub == null ?
item.WorldPosition :
item.WorldPosition - sub.Position - sub.HiddenSubPosition;
@@ -333,7 +332,8 @@ namespace Barotrauma.Items.Components
}
else
{
newNodePos = RoundNode(item.Position, item.CurrentHull) - sub.HiddenSubPosition;
newNodePos = RoundNode(item.Position, item.CurrentHull);
if (sub != null) { newNodePos -= sub.HiddenSubPosition; }
canPlaceNode = true;
}
@@ -724,7 +724,7 @@ namespace Barotrauma.Items.Components
base.RemoveComponentSpecific();
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
int eventIndex = msg.ReadRangedInteger(0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent));
int nodeCount = msg.ReadRangedInteger(0, MaxNodesPerNetworkEvent);
@@ -738,7 +738,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < nodeCount; i++)
{
nodePositions[nodeStartIndex + i] = new Vector2(msg.ReadFloat(), msg.ReadFloat());
nodePositions[nodeStartIndex + i] = new Vector2(msg.ReadSingle(), msg.ReadSingle());
}
if (nodePositions.Any(n => !MathUtils.IsValid(n)))
@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -674,7 +673,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
Item item = (Item)extraData[2];
msg.Write(item.Removed ? (ushort)0 : item.ID);
@@ -1,6 +1,5 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -375,7 +374,7 @@ namespace Barotrauma
}
}
public void SharedWrite(NetBuffer msg, object[] extraData = null)
public void SharedWrite(IWriteMessage msg, object[] extraData = null)
{
for (int i = 0; i < capacity; i++)
{
@@ -3,7 +3,6 @@ using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -127,6 +126,24 @@ namespace Barotrauma
}
}
public delegate bool InventoryFilter(Inventory inventory);
public Inventory FindParentInventory(InventoryFilter filter)
{
if (parentInventory != null)
{
if (filter(parentInventory))
{
return parentInventory;
}
var owner = parentInventory.Owner as Item;
if (owner != null)
{
return owner.FindParentInventory(filter);
}
}
return null;
}
private Item container;
public Item Container
{
@@ -597,12 +614,15 @@ namespace Barotrauma
case "fabricate":
case "fabricable":
case "fabricableitem":
case "upgrade":
break;
case "staticbody":
StaticBodyConfig = subElement;
break;
case "aitarget":
aiTarget = new AITarget(this, subElement);
aiTarget.SoundRange = aiTarget.MinSoundRange;
aiTarget.SightRange = aiTarget.MinSightRange;
break;
default:
ItemComponent ic = ItemComponent.Load(subElement, this, itemPrefab.ConfigFile);
@@ -674,9 +694,6 @@ namespace Barotrauma
}
InitProjSpecific();
InsertToList();
ItemList.Add(this);
if (callOnItemLoaded)
{
@@ -686,6 +703,9 @@ namespace Barotrauma
}
}
InsertToList();
ItemList.Add(this);
DebugConsole.Log("Created " + Name + " (" + ID + ")");
}
@@ -988,7 +1008,24 @@ namespace Barotrauma
}
return false;
}
private bool ConditionalMatches(PropertyConditional conditional)
{
if (string.IsNullOrEmpty(conditional.TargetItemComponentName))
{
if (!conditional.Matches(this)) { return false; }
}
else
{
foreach (ItemComponent component in components)
{
if (component.Name != conditional.TargetItemComponentName) { continue; }
if (!conditional.Matches(component)) { return false; }
}
}
return true;
}
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null, bool isNetworkEvent = false)
{
if (!hasStatusEffectsOfType[(int)type]) { return; }
@@ -1103,6 +1140,7 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
//aitarget goes silent/invisible if the components don't keep it active
if (aiTarget != null)
{
@@ -1131,7 +1169,11 @@ namespace Barotrauma
foreach (ItemComponent ic in components)
{
if (ic.Parent != null) ic.IsActive = ic.Parent.IsActive;
if (ic.Parent != null) { ic.IsActive = ic.Parent.IsActive; }
if (ic.IsActiveConditionals != null)
{
ic.IsActive = ic.IsActiveConditionals.All(conditional => ConditionalMatches(conditional));
}
#if CLIENT
if (!ic.WasUsed)
@@ -1827,7 +1869,7 @@ namespace Barotrauma
return allProperties;
}
private void WritePropertyChange(NetBuffer msg, object[] extraData, bool inGameEditableOnly)
private void WritePropertyChange(IWriteMessage msg, object[] extraData, bool inGameEditableOnly)
{
var allProperties = inGameEditableOnly ? GetProperties<InGameEditable>() : GetProperties<Editable>();
SerializableProperty property = extraData[1] as SerializableProperty;
@@ -1836,7 +1878,7 @@ namespace Barotrauma
var propertyOwner = allProperties.Find(p => p.Second == property);
if (allProperties.Count > 1)
{
msg.WriteRangedInteger(0, allProperties.Count - 1, allProperties.FindIndex(p => p.Second == property));
msg.WriteRangedIntegerDeprecated(0, allProperties.Count - 1, allProperties.FindIndex(p => p.Second == property));
}
object value = property.GetValue(propertyOwner.First);
@@ -1908,7 +1950,7 @@ namespace Barotrauma
}
}
private void ReadPropertyChange(NetBuffer msg, bool inGameEditableOnly, Client sender = null)
private void ReadPropertyChange(IReadMessage msg, bool inGameEditableOnly, Client sender = null)
{
var allProperties = inGameEditableOnly ? GetProperties<InGameEditable>() : GetProperties<Editable>();
if (allProperties.Count == 0) { return; }
@@ -1940,7 +1982,7 @@ namespace Barotrauma
}
else if (type == typeof(float))
{
float val = msg.ReadFloat();
float val = msg.ReadSingle();
if (allowEditing) property.TrySetValue(parentObject, val);
}
else if (type == typeof(int))
@@ -1960,17 +2002,17 @@ namespace Barotrauma
}
else if (type == typeof(Vector2))
{
Vector2 val = new Vector2(msg.ReadFloat(), msg.ReadFloat());
Vector2 val = new Vector2(msg.ReadSingle(), msg.ReadSingle());
if (allowEditing) property.TrySetValue(parentObject, val);
}
else if (type == typeof(Vector3))
{
Vector3 val = new Vector3(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat());
Vector3 val = new Vector3(msg.ReadSingle(), msg.ReadSingle(), msg.ReadSingle());
if (allowEditing) property.TrySetValue(parentObject, val);
}
else if (type == typeof(Vector4))
{
Vector4 val = new Vector4(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat());
Vector4 val = new Vector4(msg.ReadSingle(), msg.ReadSingle(), msg.ReadSingle(), msg.ReadSingle());
if (allowEditing) property.TrySetValue(parentObject, val);
}
else if (type == typeof(Point))
@@ -2038,7 +2080,7 @@ namespace Barotrauma
{
return null;
}
Rectangle rect = element.GetAttributeRect("rect", Rectangle.Empty);
if (rect.Width == 0 && rect.Height == 0)
{
@@ -2090,7 +2132,7 @@ namespace Barotrauma
foreach (XElement subElement in element.Elements())
{
ItemComponent component = unloadedComponents.Find(x => x.Name == subElement.Name.ToString());
if (component == null) continue;
if (component == null) { continue; }
component.Load(subElement);
unloadedComponents.Remove(component);
@@ -2104,6 +2146,11 @@ namespace Barotrauma
item.SetActiveSprite();
if (submarine?.GameVersion != null)
{
SerializableProperty.UpgradeGameVersion(item, item.Prefab.ConfigElement, submarine.GameVersion);
}
foreach (ItemComponent component in item.components)
{
component.OnItemLoaded();
@@ -2164,6 +2211,7 @@ namespace Barotrauma
SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement);
Sprite.ReloadXML();
SpriteDepth = Sprite.Depth;
condition = Prefab.Health;
components.ForEach(c => c.Reset());
}
@@ -2249,4 +2297,4 @@ namespace Barotrauma
partial void RemoveProjSpecific();
}
}
}
@@ -1,7 +1,6 @@
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -229,7 +228,13 @@ namespace Barotrauma
surface = rect.Y - rect.Height;
aiTarget = new AITarget(this);
aiTarget = new AITarget(this)
{
MinSightRange = 2000,
MaxSightRange = 5000,
MaxSoundRange = 5000,
SoundRange = 0
};
hullList.Add(this);
@@ -418,13 +423,14 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
UpdateProjSpecific(deltaTime, cam);
Oxygen -= OxygenDeteriorationSpeed * deltaTime;
FireSource.UpdateAll(FireSources, deltaTime);
aiTarget.SightRange = Submarine == null ? 0.0f : Math.Max(Submarine.Velocity.Length() * 2000.0f, AITarget.StaticSightRange);
aiTarget.SightRange = Submarine == null ? aiTarget.MinSightRange : Submarine.Velocity.Length() / 2 * aiTarget.MaxSightRange;
aiTarget.SoundRange -= deltaTime * 1000.0f;
if (!update)
@@ -5,7 +5,6 @@ using Barotrauma.RuinGeneration;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -1673,7 +1672,7 @@ namespace Barotrauma
loaded = null;
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
foreach (LevelWall levelWall in extraWalls)
{
@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -122,7 +121,7 @@ namespace Barotrauma
return "LevelObject (" + ActivePrefab.Name + ")";
}
public void ServerWrite(NetBuffer msg, Client c)
public void ServerWrite(IWriteMessage msg, Client c)
{
for (int j = 0; j < Triggers.Count; j++)
{
@@ -3,7 +3,6 @@ using Barotrauma.Particles;
#endif
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -407,10 +406,10 @@ namespace Barotrauma
partial void RemoveProjSpecific();
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
LevelObject obj = extraData[0] as LevelObject;
msg.WriteRangedInteger(0, objects.Count, objects.IndexOf(obj));
msg.WriteRangedIntegerDeprecated(0, objects.Count, objects.IndexOf(obj));
obj.ServerWrite(msg, c);
}
}
@@ -2,7 +2,6 @@
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -598,7 +597,7 @@ namespace Barotrauma
return vel.ClampLength(ConvertUnits.ToDisplayUnits(ForceVelocityLimit)) * currentForceFluctuation;
}
public void ServerWrite(NetBuffer msg, Client c)
public void ServerWrite(IWriteMessage msg, Client c)
{
if (ForceFluctuationStrength > 0.0f)
{
@@ -339,6 +339,11 @@ namespace Barotrauma
hull.Update(deltaTime, cam);
}
foreach (Structure structure in Structure.WallList)
{
structure.Update(deltaTime, cam);
}
foreach (Gap gap in Gap.GapList)
{
gap.Update(deltaTime, cam);
@@ -40,6 +40,18 @@ namespace Barotrauma
get { return name; }
}
public string GetItemNameTextId()
{
var textId = $"entityname.{Identifier}";
return TextManager.ContainsTag(textId) ? textId : null;
}
public string GetHullNameTextId()
{
var textId = $"roomname.{Identifier}";
return TextManager.ContainsTag(textId) ? textId : null;
}
//Used to differentiate between items when saving/loading
//Allows changing the name of an item without breaking existing subs or having multiple items with the same name
public string Identifier
@@ -4,7 +4,6 @@ using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Factories;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -373,7 +372,12 @@ namespace Barotrauma
// Only add ai targets automatically to submarine/outpost walls
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null && !Prefab.NoAITarget)
{
aiTarget = new AITarget(this);
aiTarget = new AITarget(this)
{
MinSightRange = 2000,
MaxSightRange = 5000,
MaxSoundRange = 0
};
}
InsertToList();
@@ -460,15 +464,16 @@ namespace Barotrauma
{
if (IsHorizontal)
{
xsections = (int)Math.Ceiling((float)rect.Width / WallSectionSize);
//equivalent to (int)Math.Ceiling((double)rect.Width / WallSectionSize) without the potential for floating point indeterminism
xsections = (rect.Width + WallSectionSize - 1) / WallSectionSize;
Sections = new WallSection[xsections];
width = (int)WallSectionSize;
width = WallSectionSize;
}
else
{
ysections = (int)Math.Ceiling((float)rect.Height / WallSectionSize);
ysections = (rect.Height + WallSectionSize - 1) / WallSectionSize;
Sections = new WallSection[ysections];
height = (int)WallSectionSize;
height = WallSectionSize;
}
}
@@ -1184,21 +1189,32 @@ namespace Barotrauma
ID = (ushort)int.Parse(element.Attribute("ID").Value)
};
SerializableProperty.DeserializeProperties(s, element);
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString())
{
case "section":
int index = subElement.GetAttributeInt("i", -1);
if (index == -1) continue;
s.Sections[index].damage = subElement.GetAttributeFloat("damage", 0.0f);
if (index == -1) { continue; }
if (index < 0 || index >= s.SectionCount)
{
string errorMsg = $"Error while loading structure \"{s.Name}\". Section damage index out of bounds. Index: {index}, section count: {s.SectionCount}.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Structure.Load:SectionIndexOutOfBounds", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
else
{
s.Sections[index].damage = subElement.GetAttributeFloat("damage", 0.0f);
}
break;
}
}
if (element.GetAttributeBool("flippedx", false)) s.FlipX(false);
if (element.GetAttributeBool("flippedy", false)) s.FlipY(false);
SerializableProperty.DeserializeProperties(s, element);
//structures with a body drop a shadow by default
if (element.Attribute("usedropshadow") == null)
@@ -1277,5 +1293,14 @@ namespace Barotrauma
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement);
}
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
if (aiTarget != null)
{
aiTarget.SightRange = Submarine == null ? aiTarget.MinSightRange : Submarine.Velocity.Length() / 2 * aiTarget.MaxSightRange;
}
}
}
}
@@ -3,7 +3,6 @@ using Barotrauma.Networking;
using Barotrauma.RuinGeneration;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -1143,7 +1142,6 @@ namespace Barotrauma
savedSubmarines.Add(sub);
}
public static void RefreshSavedSub(string filePath)
{
string fullPath = Path.GetFullPath(filePath);
@@ -1154,12 +1152,15 @@ namespace Barotrauma
savedSubmarines[i].Dispose();
}
}
var sub = new Submarine(filePath);
if (!sub.IsFileCorrupted)
if (File.Exists(filePath))
{
savedSubmarines.Add(sub);
var sub = new Submarine(filePath);
if (!sub.IsFileCorrupted)
{
savedSubmarines.Add(sub);
}
savedSubmarines = savedSubmarines.OrderBy(s => s.filePath ?? "").ToList();
}
savedSubmarines = savedSubmarines.OrderBy(s => s.filePath ?? "").ToList();
}
public static void RefreshSavedSubs()
@@ -1210,6 +1211,15 @@ namespace Barotrauma
}
}
var contentPackageSubs = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.Submarine);
foreach (string subPath in contentPackageSubs)
{
if (!filePaths.Any(fp => Path.GetFullPath(fp) == Path.GetFullPath(subPath)))
{
filePaths.Add(subPath);
}
}
foreach (string path in filePaths)
{
var sub = new Submarine(path);
@@ -1,5 +1,4 @@
using Barotrauma.Items.Components;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
@@ -7,9 +6,9 @@ using System.Text;
namespace Barotrauma.Networking
{
enum ChatMessageType
public enum ChatMessageType
{
Default, Error, Dead, Server, Radio, Private, Console, MessageBox, Order, ServerLog
Default, Error, Dead, Server, Radio, Private, Console, MessageBox, Order, ServerLog, ServerMessageBox
}
partial class ChatMessage
@@ -1,4 +1,4 @@
using Lidgren.Network;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -12,7 +12,8 @@ namespace Barotrauma.Networking
public string Name;
public byte ID;
public UInt64 SteamID;
public Character.TeamType TeamID;
private Character character;
@@ -38,6 +39,12 @@ namespace Barotrauma.Networking
HasSpawned = true;
#if CLIENT
GameMain.GameSession?.CrewManager?.SetPlayerVoiceIconState(this, muted, mutedLocally);
if (character == GameMain.Client.Character && GameMain.Client.SpawnAsTraitor)
{
character.IsTraitor = true;
character.TraitorCurrentObjective = GameMain.Client.TraitorFirstObjective;
}
#endif
}
}
@@ -179,7 +186,7 @@ namespace Barotrauma.Networking
}
}
public void WritePermissions(NetBuffer msg)
public void WritePermissions(IWriteMessage msg)
{
msg.Write(ID);
msg.Write((UInt16)Permissions);
@@ -192,7 +199,7 @@ namespace Barotrauma.Networking
}
}
}
public static void ReadPermissions(NetBuffer inc, out ClientPermissions permissions, out List<DebugConsole.Command> permittedCommands)
public static void ReadPermissions(IReadMessage inc, out ClientPermissions permissions, out List<DebugConsole.Command> permittedCommands)
{
UInt16 permissionsInt = inc.ReadUInt16();
@@ -221,7 +228,7 @@ namespace Barotrauma.Networking
}
}
public void ReadPermissions(NetIncomingMessage inc)
public void ReadPermissions(IReadMessage inc)
{
ClientPermissions permissions = ClientPermissions.None;
List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
@@ -7,7 +7,7 @@ using System.Xml.Linq;
namespace Barotrauma.Networking
{
[Flags]
enum ClientPermissions
public enum ClientPermissions
{
None = 0x0,
ManageRound = 0x1,
@@ -1,6 +1,4 @@
using Lidgren.Network;
namespace Barotrauma.Networking
namespace Barotrauma.Networking
{
interface INetSerializable { }
@@ -10,10 +8,10 @@ namespace Barotrauma.Networking
interface IClientSerializable : INetSerializable
{
#if CLIENT
void ClientWrite(NetBuffer msg, object[] extraData = null);
void ClientWrite(IWriteMessage msg, object[] extraData = null);
#endif
#if SERVER
void ServerRead(ClientNetObject type, NetBuffer msg, Client c);
void ServerRead(ClientNetObject type, IReadMessage msg, Client c);
#endif
}
@@ -23,10 +21,10 @@ namespace Barotrauma.Networking
interface IServerSerializable : INetSerializable
{
#if SERVER
void ServerWrite(NetBuffer msg, Client c, object[] extraData = null);
void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null);
#endif
#if CLIENT
void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime);
void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime);
#endif
}
}
@@ -53,12 +53,12 @@ namespace Barotrauma
public float ExtinguishFireKarmaIncrease { get; set; }
private float allowedWireDisconnectionsPerMinute;
[Serialize(5.0f, true)]
public float AllowedWireDisconnectionsPerMinute
private int allowedWireDisconnectionsPerMinute;
[Serialize(5, true)]
public int AllowedWireDisconnectionsPerMinute
{
get { return allowedWireDisconnectionsPerMinute; }
set { allowedWireDisconnectionsPerMinute = Math.Max(0.0f, value); }
set { allowedWireDisconnectionsPerMinute = Math.Max(0, value); }
}
[Serialize(6.0f, true)]
@@ -76,6 +76,9 @@ namespace Barotrauma
[Serialize(1.0f, true)]
public float KickBanThreshold { get; set; }
[Serialize(0, true)]
public int KicksBeforeBan { get; set; }
[Serialize(10.0f, true)]
public float KarmaNotificationInterval { get; set; }
@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
namespace Barotrauma.Networking
{
@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
using System.Collections.Generic;
namespace Barotrauma.Networking
@@ -11,10 +10,10 @@ namespace Barotrauma.Networking
/// <summary>
/// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager
/// </summary>
protected void Write(NetOutgoingMessage msg, List<NetEntityEvent> eventsToSync, out List<NetEntityEvent> sentEvents, Client recipient = null)
protected void Write(IWriteMessage msg, List<NetEntityEvent> eventsToSync, out List<NetEntityEvent> sentEvents, Client recipient = null)
{
//write into a temporary buffer so we can write the number of events before the actual data
NetBuffer tempBuffer = new NetBuffer();
IWriteMessage tempBuffer = new WriteOnlyMessage();
sentEvents = new List<NetEntityEvent>();
@@ -22,7 +21,7 @@ namespace Barotrauma.Networking
foreach (NetEntityEvent e in eventsToSync)
{
//write into a temporary buffer so we can write the length before the actual data
NetBuffer tempEventBuffer = new NetBuffer();
IWriteMessage tempEventBuffer = new WriteOnlyMessage();
try
{
WriteEvent(tempEventBuffer, e, recipient);
@@ -67,7 +66,7 @@ namespace Barotrauma.Networking
tempBuffer.Write(e.EntityID);
tempBuffer.Write((byte)tempEventBuffer.LengthBytes);
tempBuffer.Write(tempEventBuffer);
tempBuffer.Write(tempEventBuffer.Buffer, 0, tempEventBuffer.LengthBytes);
tempBuffer.WritePadBits();
sentEvents.Add(e);
@@ -78,10 +77,10 @@ namespace Barotrauma.Networking
{
msg.Write(eventsToSync[0].ID);
msg.Write((byte)eventCount);
msg.Write(tempBuffer);
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
}
}
protected abstract void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null);
protected abstract void WriteEvent(IWriteMessage buffer, NetEntityEvent entityEvent, Client recipient = null);
}
}
@@ -1,5 +1,4 @@
using Barotrauma.Items.Components;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -9,9 +8,6 @@ namespace Barotrauma.Networking
{
enum ClientPacketHeader
{
REQUEST_AUTH, //ask the server if a password is needed, if so we'll get nonce for encryption
REQUEST_STEAMAUTH, //the same as REQUEST_AUTH, but in addition we want to authenticate the player's Steam ID
REQUEST_INIT, //ask the server to give you initialization
UPDATE_LOBBY, //update state in lobby
UPDATE_INGAME, //update state ingame
@@ -64,7 +60,9 @@ namespace Barotrauma.Networking
QUERY_STARTGAME, //ask the clients whether they're ready to start
STARTGAME, //start a new round
ENDGAME
ENDGAME,
TRAITOR_MESSAGE
}
enum ServerNetObject
{
@@ -78,6 +76,14 @@ namespace Barotrauma.Networking
ENTITY_EVENT_INITIAL,
}
enum TraitorMessageType
{
Server,
ServerMessageBox,
Objective,
Console
}
enum VoteType
{
Unknown,
@@ -94,6 +100,7 @@ namespace Barotrauma.Networking
Banned,
Kicked,
ServerShutdown,
ServerCrashed,
ServerFull,
AuthenticationRequired,
SteamAuthenticationRequired,
@@ -132,13 +139,7 @@ namespace Barotrauma.Networking
#if DEBUG
public Dictionary<string, long> messageCount = new Dictionary<string, long>();
#endif
public NetPeer NetPeer
{
get;
protected set;
}
protected string name;
protected ServerSettings serverSettings;
@@ -154,12 +155,6 @@ namespace Barotrauma.Networking
public bool ShowNetStats;
public int Port
{
get;
set;
}
public int TickRate
{
get { return serverSettings.TickRate; }
@@ -205,13 +200,7 @@ namespace Barotrauma.Networking
{
get { return serverSettings; }
}
public NetPeerConfiguration NetPeerConfiguration
{
get;
protected set;
}
public bool CanUseRadio(Character sender)
{
if (sender == null) return false;
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
namespace Barotrauma.Networking
{
@@ -0,0 +1,37 @@
using System;
namespace Barotrauma.Networking
{
public enum DeliveryMethod : byte
{
Unreliable = 0x0,
Reliable = 0x1,
ReliableOrdered = 0x2
}
public enum ConnectionInitialization : byte
{
//used by all peer implementations
SteamTicketAndVersion = 0x1,
Password = 0x2,
Success = 0x0,
//used only by SteamP2P implementations
ConnectionStarted = 0x3
}
[Flags]
public enum PacketHeader : byte
{
//used by all peer implementations
None = 0x0,
IsCompressed = 0x1,
IsConnectionInitializationStep = 0x2,
//used only by SteamP2P implementations
IsDisconnectMessage = 0x4,
IsServerMessage = 0x8,
IsHeartbeatMessage = 0x10
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma.Networking
{
public interface IReadMessage
{
bool ReadBoolean();
void ReadPadBits();
byte ReadByte();
UInt16 ReadUInt16();
Int16 ReadInt16();
UInt32 ReadUInt32();
Int32 ReadInt32();
UInt64 ReadUInt64();
Int64 ReadInt64();
Single ReadSingle();
Double ReadDouble();
UInt32 ReadVariableUInt32();
String ReadString();
int ReadRangedInteger(int min, int max);
Single ReadRangedSingle(Single min, Single max, int bitCount);
byte[] ReadBytes(int numberOfBytes);
int BitPosition { get; set; }
int BytePosition { get; }
byte[] Buffer { get; }
int LengthBits { get; set; }
int LengthBytes { get; }
NetworkConnection Sender { get; }
}
}
@@ -0,0 +1,33 @@
using System;
namespace Barotrauma.Networking
{
public interface IWriteMessage
{
void Write(bool val);
void WritePadBits();
void Write(byte val);
void Write(Int16 val);
void Write(UInt16 val);
void Write(Int32 val);
void Write(UInt32 val);
void Write(Int64 val);
void Write(UInt64 val);
void Write(Single val);
void Write(Double val);
void WriteVariableUInt32(UInt32 val);
void Write(string val);
void WriteRangedIntegerDeprecated(int min, int max, int val); //TODO: remove this, val should be first parameter >:(
void WriteRangedInteger(int val, int min, int max);
void WriteRangedSingle(Single val, Single min, Single max, int bitCount);
void Write(byte[] val, int startIndex, int length);
void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int outLength);
int BitPosition { get; set; }
int BytePosition { get; }
byte[] Buffer { get; }
int LengthBits { get; set; }
int LengthBytes { get; }
}
}
@@ -0,0 +1,955 @@
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Text;
namespace Barotrauma.Networking
{
public static class MsgConstants
{
public const int MTU = 1200;
public const int CompressionThreshold = 1000;
public const int InitialBufferSize = 256;
public const int BufferOverAllocateAmount = 4;
}
/// <summary>
/// Utility struct for writing Singles
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct SingleUIntUnion
{
/// <summary>
/// Value as a 32 bit float
/// </summary>
[FieldOffset(0)]
public float SingleValue;
/// <summary>
/// Value as an unsigned 32 bit integer
/// </summary>
[FieldOffset(0)]
public uint UIntValue;
}
internal static class MsgWriter
{
internal static void Write(ref byte[] buf, ref int bitPos, bool val)
{
#if DEBUG
int resetPos = bitPos;
#endif
EnsureBufferSize(ref buf, bitPos + 1);
int bytePos = bitPos / 8;
int bitOffset = bitPos % 8;
byte bitFlag = (byte)(1 << bitOffset);
byte bitMask = (byte)((~bitFlag) & 0xff);
buf[bytePos] &= bitMask;
if (val) buf[bytePos] |= bitFlag;
bitPos++;
#if DEBUG
bool testVal = MsgReader.ReadBoolean(buf, ref resetPos);
if (testVal != val || resetPos != bitPos)
{
DebugConsole.ThrowError("Boolean written incorrectly! " + testVal + ", " + val + "; " + resetPos + ", " + bitPos);
}
#endif
}
internal static void WritePadBits(ref byte[] buf, ref int bitPos)
{
int bitOffset = bitPos % 8;
bitPos += ((8 - bitOffset) % 8);
EnsureBufferSize(ref buf, bitPos);
}
internal static void Write(ref byte[] buf, ref int bitPos, byte val)
{
EnsureBufferSize(ref buf, bitPos + 8);
NetBitWriter.WriteByte(val, 8, buf, bitPos);
bitPos += 8;
}
internal static void Write(ref byte[] buf, ref int bitPos, UInt16 val)
{
EnsureBufferSize(ref buf, bitPos + 16);
NetBitWriter.WriteUInt16(val, 16, buf, bitPos);
bitPos += 16;
}
internal static void Write(ref byte[] buf, ref int bitPos, Int16 val)
{
EnsureBufferSize(ref buf, bitPos + 16);
NetBitWriter.WriteUInt16((UInt16)val, 16, buf, bitPos);
bitPos += 16;
}
internal static void Write(ref byte[] buf, ref int bitPos, UInt32 val)
{
EnsureBufferSize(ref buf, bitPos + 32);
NetBitWriter.WriteUInt32(val, 32, buf, bitPos);
bitPos += 32;
}
internal static void Write(ref byte[] buf, ref int bitPos, Int32 val)
{
EnsureBufferSize(ref buf, bitPos + 32);
NetBitWriter.WriteUInt32((UInt32)val, 32, buf, bitPos);
bitPos += 32;
}
internal static void Write(ref byte[] buf, ref int bitPos, UInt64 val)
{
EnsureBufferSize(ref buf, bitPos + 64);
NetBitWriter.WriteUInt64(val, 64, buf, bitPos);
bitPos += 64;
}
internal static void Write(ref byte[] buf, ref int bitPos, Int64 val)
{
EnsureBufferSize(ref buf, bitPos + 64);
NetBitWriter.WriteUInt64((UInt64)val, 64, buf, bitPos);
bitPos += 64;
}
internal static void Write(ref byte[] buf, ref int bitPos, Single val)
{
// Use union to avoid BitConverter.GetBytes() which allocates memory on the heap
SingleUIntUnion su;
su.UIntValue = 0; // must initialize every member of the union to avoid warning
su.SingleValue = val;
EnsureBufferSize(ref buf, bitPos + 32);
NetBitWriter.WriteUInt32(su.UIntValue, 32, buf, bitPos);
bitPos += 32;
}
internal static void Write(ref byte[] buf, ref int bitPos, Double val)
{
EnsureBufferSize(ref buf, bitPos + 64);
byte[] bytes = BitConverter.GetBytes(val);
WriteBytes(ref buf, ref bitPos, bytes, 0, bytes.Length);
bitPos += 64;
}
internal static void Write(ref byte[] buf, ref int bitPos, string val)
{
if (string.IsNullOrEmpty(val))
{
WriteVariableUInt32(ref buf, ref bitPos, (uint)0);
return;
}
byte[] bytes = Encoding.UTF8.GetBytes(val);
WriteVariableUInt32(ref buf, ref bitPos, (uint)bytes.Length);
WriteBytes(ref buf, ref bitPos, bytes, 0, bytes.Length);
}
internal static int WriteVariableUInt32(ref byte[] buf, ref int bitPos, uint value)
{
int retval = 1;
uint num1 = (uint)value;
while (num1 >= 0x80)
{
Write(ref buf, ref bitPos, (byte)(num1 | 0x80));
num1 = num1 >> 7;
retval++;
}
Write(ref buf, ref bitPos, (byte)num1);
return retval;
}
internal static void WriteRangedInteger(ref byte[] buf, ref int bitPos, int val, int min, int max)
{
uint range = (uint)(max - min);
int numberOfBits = NetUtility.BitsToHoldUInt(range);
EnsureBufferSize(ref buf, bitPos + numberOfBits);
uint rvalue = (uint)(val - min);
NetBitWriter.WriteUInt32(rvalue, numberOfBits, buf, bitPos);
bitPos += numberOfBits;
}
internal static void WriteRangedSingle(ref byte[] buf, ref int bitPos, Single val, Single min, Single max, int numberOfBits)
{
float range = max - min;
float unit = ((val - min) / range);
int maxVal = (1 << numberOfBits) - 1;
EnsureBufferSize(ref buf, bitPos + numberOfBits);
NetBitWriter.WriteUInt32((UInt32)((float)maxVal * unit), numberOfBits, buf, bitPos);
bitPos += numberOfBits;
}
internal static void WriteBytes(ref byte[] buf, ref int bitPos, byte[] val, int pos, int length)
{
EnsureBufferSize(ref buf, bitPos + length * 8);
NetBitWriter.WriteBytes(val, pos, length, buf, bitPos);
bitPos += length * 8;
}
internal static void EnsureBufferSize(ref byte[] buf, int numberOfBits)
{
int byteLen = ((numberOfBits + 7) >> 3);
if (buf == null)
{
buf = new byte[byteLen + MsgConstants.BufferOverAllocateAmount];
return;
}
if (buf.Length < byteLen)
{
Array.Resize<byte>(ref buf, byteLen + MsgConstants.BufferOverAllocateAmount);
}
}
}
internal static class MsgReader
{
internal static bool ReadBoolean(byte[] buf, ref int bitPos)
{
byte retval = NetBitWriter.ReadByte(buf, 1, bitPos);
bitPos++;
return (retval > 0 ? true : false);
}
internal static void ReadPadBits(byte[] buf, ref int bitPos)
{
int bitOffset = bitPos % 8;
bitPos += (8 - bitOffset) % 8;
}
internal static byte ReadByte(byte[] buf, ref int bitPos)
{
byte retval = NetBitWriter.ReadByte(buf, 8, bitPos);
bitPos += 8;
return retval;
}
internal static UInt16 ReadUInt16(byte[] buf, ref int bitPos)
{
uint retval = NetBitWriter.ReadUInt16(buf, 16, bitPos);
bitPos += 16;
return (ushort)retval;
}
internal static Int16 ReadInt16(byte[] buf, ref int bitPos)
{
return (Int16)ReadUInt16(buf, ref bitPos);
}
internal static UInt32 ReadUInt32(byte[] buf, ref int bitPos)
{
uint retval = NetBitWriter.ReadUInt32(buf, 32, bitPos);
bitPos += 32;
return retval;
}
internal static Int32 ReadInt32(byte[] buf, ref int bitPos)
{
return (Int32)ReadUInt32(buf, ref bitPos);
}
internal static UInt64 ReadUInt64(byte[] buf, ref int bitPos)
{
ulong low = NetBitWriter.ReadUInt32(buf, 32, bitPos);
bitPos += 32;
ulong high = NetBitWriter.ReadUInt32(buf, 32, bitPos);
ulong retval = low + (high << 32);
bitPos += 32;
return retval;
}
internal static Int64 ReadInt64(byte[] buf, ref int bitPos)
{
return (Int64)ReadUInt64(buf, ref bitPos);
}
internal static Single ReadSingle(byte[] buf, ref int bitPos)
{
if ((bitPos & 7) == 0) // read directly
{
float retval = BitConverter.ToSingle(buf, bitPos >> 3);
bitPos += 32;
return retval;
}
byte[] bytes = ReadBytes(buf, ref bitPos, 4);
return BitConverter.ToSingle(bytes, 0);
}
internal static Double ReadDouble(byte[] buf, ref int bitPos)
{
if ((bitPos & 7) == 0) // read directly
{
// read directly
double retval = BitConverter.ToDouble(buf, bitPos >> 3);
bitPos += 64;
return retval;
}
byte[] bytes = ReadBytes(buf, ref bitPos, 8);
return BitConverter.ToDouble(bytes, 0);
}
internal static UInt32 ReadVariableUInt32(byte[] buf, ref int bitPos)
{
int bitLength = buf.Length * 8;
int num1 = 0;
int num2 = 0;
while (bitLength - bitPos >= 8)
{
byte num3 = ReadByte(buf, ref bitPos);
num1 |= (num3 & 0x7f) << num2;
num2 += 7;
if ((num3 & 0x80) == 0)
return (uint)num1;
}
// ouch; failed to find enough bytes; malformed variable length number?
return (uint)num1;
}
internal static String ReadString(byte[] buf, ref int bitPos)
{
int bitLength = buf.Length * 8;
int byteLen = (int)ReadVariableUInt32(buf, ref bitPos);
if (byteLen <= 0) { return String.Empty; }
if ((ulong)(bitLength - bitPos) < ((ulong)byteLen * 8))
{
// not enough data
return null;
}
if ((bitPos & 7) == 0)
{
// read directly
string retval = System.Text.Encoding.UTF8.GetString(buf, bitPos >> 3, byteLen);
bitPos += (8 * byteLen);
return retval;
}
byte[] bytes = ReadBytes(buf, ref bitPos, byteLen);
return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
internal static int ReadRangedInteger(byte[] buf, ref int bitPos, int min, int max)
{
uint range = (uint)(max - min);
int numBits = NetUtility.BitsToHoldUInt(range);
uint rvalue = NetBitWriter.ReadUInt32(buf, numBits, bitPos);
bitPos += numBits;
return (int)(min + rvalue);
}
internal static Single ReadRangedSingle(byte[] buf, ref int bitPos, Single min, Single max, int bitCount)
{
int maxInt = (1 << bitCount) - 1;
int intVal = ReadRangedInteger(buf, ref bitPos, 0, maxInt);
Single range = max - min;
return min + (range * ((Single)intVal) / ((Single)maxInt));
}
internal static byte[] ReadBytes(byte[] buf, ref int bitPos, int numberOfBytes)
{
byte[] retval = new byte[numberOfBytes];
NetBitWriter.ReadBytes(buf, numberOfBytes, bitPos, retval, 0);
bitPos += (8 * numberOfBytes);
return retval;
}
}
public class WriteOnlyMessage : IWriteMessage
{
private byte[] buf = new byte[MsgConstants.InitialBufferSize];
private int seekPos = 0;
private int lengthBits = 0;
public int BitPosition
{
get
{
return seekPos;
}
set
{
seekPos = value;
}
}
public int BytePosition
{
get
{
return seekPos / 8;
}
}
public byte[] Buffer
{
get
{
return buf;
}
}
public int LengthBits
{
get
{
lengthBits = seekPos > lengthBits ? seekPos : lengthBits;
return lengthBits;
}
set
{
lengthBits = value;
seekPos = seekPos > lengthBits ? lengthBits : seekPos;
}
}
public int LengthBytes
{
get
{
return (LengthBits + ((8 - (LengthBits % 8)) % 8)) / 8;
}
}
public void Write(bool val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WritePadBits()
{
MsgWriter.WritePadBits(ref buf, ref seekPos);
}
public void Write(byte val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt16 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int16 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt32 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int32 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt64 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int64 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Single val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Double val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WriteVariableUInt32(UInt32 val)
{
MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, val);
}
public void Write(String val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WriteRangedIntegerDeprecated(int min, int max, int val)
{
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
}
public void WriteRangedInteger(int val, int min, int max)
{
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
}
public void WriteRangedSingle(Single val, Single min, Single max, int bitCount)
{
MsgWriter.WriteRangedSingle(ref buf, ref seekPos, val, min, max, bitCount);
}
public void Write(byte[] val, int startPos, int length)
{
MsgWriter.WriteBytes(ref buf, ref seekPos, val, startPos, length);
}
public void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int length)
{
if (LengthBytes <= MsgConstants.CompressionThreshold)
{
isCompressed = false;
if (LengthBytes > outBuf.Length) { Array.Resize(ref outBuf, LengthBytes); }
Array.Copy(buf, outBuf, LengthBytes);
length = LengthBytes;
}
else
{
using (MemoryStream output = new MemoryStream())
{
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Fastest))
{
dstream.Write(buf, 0, LengthBytes);
}
byte[] compressedBuf = output.ToArray();
//don't send the data as compressed if the data takes up more space after compression
//(which may happen when sending a sub/save file that's already been compressed with a better compression ratio)
if (compressedBuf.Length >= outBuf.Length)
{
isCompressed = false;
if (LengthBytes > outBuf.Length) { Array.Resize(ref outBuf, LengthBytes); }
Array.Copy(buf, outBuf, LengthBytes);
length = LengthBytes;
}
else
{
isCompressed = true;
if (compressedBuf.Length > outBuf.Length) { Array.Resize(ref outBuf, compressedBuf.Length); }
Array.Copy(compressedBuf, outBuf, compressedBuf.Length);
length = compressedBuf.Length;
DebugConsole.NewMessage("Compressed message: " + LengthBytes + " to " + length);
}
}
}
}
}
public class ReadOnlyMessage : IReadMessage
{
private byte[] buf;
private int seekPos = 0;
private int lengthBits = 0;
public int BitPosition
{
get
{
return seekPos;
}
set
{
seekPos = value;
}
}
public int BytePosition
{
get
{
return seekPos / 8;
}
}
public byte[] Buffer
{
get
{
return buf;
}
}
public int LengthBits
{
get
{
lengthBits = seekPos > lengthBits ? seekPos : lengthBits;
return lengthBits;
}
set
{
lengthBits = value;
seekPos = seekPos > lengthBits ? lengthBits : seekPos;
}
}
public int LengthBytes
{
get
{
return lengthBits / 8;
}
}
public NetworkConnection Sender { get; private set; }
public ReadOnlyMessage(byte[] inBuf, bool isCompressed, int startPos, int inLength, NetworkConnection sender)
{
Sender = sender;
if (isCompressed)
{
byte[] decompressedData;
using (MemoryStream input = new MemoryStream(inBuf, startPos, inLength))
{
using (MemoryStream output = new MemoryStream())
{
using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress))
{
dstream.CopyTo(output);
}
decompressedData = output.ToArray();
}
}
buf = new byte[decompressedData.Length];
Array.Copy(decompressedData, 0, buf, 0, decompressedData.Length);
lengthBits = decompressedData.Length * 8;
DebugConsole.NewMessage("Decompressing message: " + inLength + " to " + LengthBytes);
}
else
{
buf = new byte[inBuf.Length];
Array.Copy(inBuf, startPos, buf, 0, inLength);
lengthBits = inLength * 8;
}
seekPos = 0;
}
public bool ReadBoolean()
{
return MsgReader.ReadBoolean(buf, ref seekPos);
}
public void ReadPadBits()
{
MsgReader.ReadPadBits(buf, ref seekPos);
}
public byte ReadByte()
{
return MsgReader.ReadByte(buf, ref seekPos);
}
public UInt16 ReadUInt16()
{
return MsgReader.ReadUInt16(buf, ref seekPos);
}
public Int16 ReadInt16()
{
return MsgReader.ReadInt16(buf, ref seekPos);
}
public UInt32 ReadUInt32()
{
return MsgReader.ReadUInt32(buf, ref seekPos);
}
public Int32 ReadInt32()
{
return MsgReader.ReadInt32(buf, ref seekPos);
}
public UInt64 ReadUInt64()
{
return MsgReader.ReadUInt64(buf, ref seekPos);
}
public Int64 ReadInt64()
{
return MsgReader.ReadInt64(buf, ref seekPos);
}
public Single ReadSingle()
{
return MsgReader.ReadSingle(buf, ref seekPos);
}
public Double ReadDouble()
{
return MsgReader.ReadDouble(buf, ref seekPos);
}
public UInt32 ReadVariableUInt32()
{
return MsgReader.ReadVariableUInt32(buf, ref seekPos);
}
public String ReadString()
{
return MsgReader.ReadString(buf, ref seekPos);
}
public int ReadRangedInteger(int min, int max)
{
return MsgReader.ReadRangedInteger(buf, ref seekPos, min, max);
}
public Single ReadRangedSingle(Single min, Single max, int bitCount)
{
return MsgReader.ReadRangedSingle(buf, ref seekPos, min, max, bitCount);
}
public byte[] ReadBytes(int numberOfBytes)
{
return MsgReader.ReadBytes(buf, ref seekPos, numberOfBytes);
}
}
public class ReadWriteMessage : IWriteMessage, IReadMessage
{
private byte[] buf = new byte[MsgConstants.InitialBufferSize];
private int seekPos = 0;
private int lengthBits = 0;
public int BitPosition
{
get
{
return seekPos;
}
set
{
seekPos = value;
}
}
public int BytePosition
{
get
{
return seekPos / 8;
}
}
public byte[] Buffer
{
get
{
return buf;
}
}
public int LengthBits
{
get
{
lengthBits = seekPos > lengthBits ? seekPos : lengthBits;
return lengthBits;
}
set
{
lengthBits = value;
seekPos = seekPos > lengthBits ? lengthBits : seekPos;
}
}
public int LengthBytes
{
get
{
return (LengthBits + ((8 - (LengthBits % 8)) % 8)) / 8;
}
}
public NetworkConnection Sender { get { return null; } }
public void Write(bool val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WritePadBits()
{
MsgWriter.WritePadBits(ref buf, ref seekPos);
}
public void Write(byte val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt16 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int16 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt32 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int32 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt64 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int64 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Single val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Double val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WriteVariableUInt32(UInt32 val)
{
MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, val);
}
public void Write(String val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WriteRangedIntegerDeprecated(int min, int max, int val)
{
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
}
public void WriteRangedInteger(int val, int min, int max)
{
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
}
public void WriteRangedSingle(Single val, Single min, Single max, int bitCount)
{
MsgWriter.WriteRangedSingle(ref buf, ref seekPos, val, min, max, bitCount);
}
public void Write(byte[] val, int startPos, int length)
{
MsgWriter.WriteBytes(ref buf, ref seekPos, val, startPos, length);
}
public bool ReadBoolean()
{
return MsgReader.ReadBoolean(buf, ref seekPos);
}
public void ReadPadBits()
{
MsgReader.ReadPadBits(buf, ref seekPos);
}
public byte ReadByte()
{
return MsgReader.ReadByte(buf, ref seekPos);
}
public UInt16 ReadUInt16()
{
return MsgReader.ReadUInt16(buf, ref seekPos);
}
public Int16 ReadInt16()
{
return MsgReader.ReadInt16(buf, ref seekPos);
}
public UInt32 ReadUInt32()
{
return MsgReader.ReadUInt32(buf, ref seekPos);
}
public Int32 ReadInt32()
{
return MsgReader.ReadInt32(buf, ref seekPos);
}
public UInt64 ReadUInt64()
{
return MsgReader.ReadUInt64(buf, ref seekPos);
}
public Int64 ReadInt64()
{
return MsgReader.ReadInt64(buf, ref seekPos);
}
public Single ReadSingle()
{
return MsgReader.ReadSingle(buf, ref seekPos);
}
public Double ReadDouble()
{
return MsgReader.ReadDouble(buf, ref seekPos);
}
public UInt32 ReadVariableUInt32()
{
return MsgReader.ReadVariableUInt32(buf, ref seekPos);
}
public String ReadString()
{
return MsgReader.ReadString(buf, ref seekPos);
}
public int ReadRangedInteger(int min, int max)
{
return MsgReader.ReadRangedInteger(buf, ref seekPos, min, max);
}
public Single ReadRangedSingle(Single min, Single max, int bitCount)
{
return MsgReader.ReadRangedSingle(buf, ref seekPos, min, max, bitCount);
}
public byte[] ReadBytes(int numberOfBytes)
{
return MsgReader.ReadBytes(buf, ref seekPos, numberOfBytes);
}
public void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int outLength)
{
throw new InvalidOperationException("ReadWriteMessages are not to be sent");
}
}
}
@@ -0,0 +1,37 @@
using System;
using System.Net;
using Lidgren.Network;
namespace Barotrauma.Networking
{
public class LidgrenConnection : NetworkConnection
{
public NetConnection NetConnection { get; private set; }
public IPEndPoint IPEndPoint => NetConnection.RemoteEndPoint;
public string IPString
{
get
{
return IPEndPoint.Address.IsIPv4MappedToIPv6 ? IPEndPoint.Address.MapToIPv4().ToString() : IPEndPoint.Address.ToString();
}
}
public UInt16 Port
{
get
{
return (UInt16)IPEndPoint.Port;
}
}
public LidgrenConnection(string name, NetConnection netConnection, UInt64 steamId)
{
Name = name;
NetConnection = netConnection;
SteamID = steamId;
EndPointString = IPString;
}
}
}
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
namespace Barotrauma.Networking
{
public enum NetworkConnectionStatus
{
Connected = 0x1,
Disconnected = 0x2
}
public abstract class NetworkConnection
{
public string Name;
public UInt64 SteamID
{
get;
protected set;
}
public string EndPointString
{
get;
protected set;
}
public NetworkConnectionStatus Status = NetworkConnectionStatus.Disconnected;
}
}
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma.Networking
{
public class SteamP2PConnection : NetworkConnection
{
public double Timeout = 0.0;
public SteamP2PConnection(string name, UInt64 steamId)
{
SteamID = steamId;
EndPointString = SteamID.ToString();
Name = name;
Heartbeat();
}
public void Decay(float deltaTime)
{
Timeout -= deltaTime;
}
public void Heartbeat()
{
Timeout = 20.0;
}
}
}
@@ -282,7 +282,7 @@ namespace Barotrauma.Networking
{
RespawnCharactersProjSpecific();
}
public Vector2 FindSpawnPos()
{
if (Level.Loaded == null || Submarine.MainSub == null) { return Vector2.Zero; }
@@ -310,12 +310,11 @@ namespace Barotrauma.Networking
//make sure there aren't any walls too close
var tooCloseCells = Level.Loaded.GetTooCloseCells(potentialSpawnPos.Position.ToVector2(), Math.Max(minWidth, minHeight));
if (tooCloseCells.Any()) { continue; }
//make sure the spawnpoint is far enough from other subs
foreach (Submarine sub in Submarine.Loaded)
{
if (sub == RespawnShuttle || RespawnShuttle.DockedTo.Contains(sub)) { continue; }
float minDist = Math.Max(Math.Max(minWidth, minHeight) + Math.Max(sub.Borders.Width, sub.Borders.Height), 10000.0f);
if (Vector2.DistanceSquared(sub.WorldPosition, potentialSpawnPos.Position.ToVector2()) < minDist * minDist)
{
@@ -6,7 +6,7 @@ using System.Linq;
namespace Barotrauma.Networking
{
partial class ServerLog
public partial class ServerLog
{
private struct LogMessage
{
@@ -1,5 +1,4 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -14,21 +13,21 @@ using System.Xml.Linq;
namespace Barotrauma.Networking
{
enum SelectionMode
public enum SelectionMode
{
Manual = 0, Random = 1, Vote = 2
}
enum YesNoMaybe
public enum YesNoMaybe
{
No = 0, Maybe = 1, Yes = 2
}
enum BotSpawnMode
public enum BotSpawnMode
{
Normal, Fill
}
partial class ServerSettings : ISerializableEntity
{
public const string SettingsFile = "serversettings.xml";
@@ -57,25 +56,17 @@ namespace Barotrauma.Networking
public class SavedClientPermission
{
public readonly string IP;
public readonly string EndPoint;
public readonly ulong SteamID;
public readonly string Name;
public List<DebugConsole.Command> PermittedCommands;
public ClientPermissions Permissions;
public SavedClientPermission(string name, IPAddress ip, ClientPermissions permissions, List<DebugConsole.Command> permittedCommands)
public SavedClientPermission(string name, string endpoint, ClientPermissions permissions, List<DebugConsole.Command> permittedCommands)
{
this.Name = name;
this.IP = ip.IsIPv4MappedToIPv6 ? ip.MapToIPv4().ToString() : ip.ToString();
this.Permissions = permissions;
this.PermittedCommands = permittedCommands;
}
public SavedClientPermission(string name, string ip, ClientPermissions permissions, List<DebugConsole.Command> permittedCommands)
{
this.Name = name;
this.IP = ip;
this.EndPoint = endpoint;
this.Permissions = permissions;
this.PermittedCommands = permittedCommands;
}
@@ -139,9 +130,9 @@ namespace Barotrauma.Networking
}
}
public void Read(NetBuffer msg)
public void Read(IReadMessage msg)
{
long oldPos = msg.Position;
int oldPos = msg.BitPosition;
UInt32 size = msg.ReadVariableUInt32();
float x; float y; float z; float w;
@@ -152,7 +143,7 @@ namespace Barotrauma.Networking
{
case "float":
if (size != 4) break;
property.SetValue(parentObject, msg.ReadFloat());
property.SetValue(parentObject, msg.ReadSingle());
return;
case "int":
if (size != 4) break;
@@ -160,23 +151,23 @@ namespace Barotrauma.Networking
return;
case "vector2":
if (size != 8) break;
x = msg.ReadFloat();
y = msg.ReadFloat();
x = msg.ReadSingle();
y = msg.ReadSingle();
property.SetValue(parentObject, new Vector2(x, y));
return;
case "vector3":
if (size != 12) break;
x = msg.ReadFloat();
y = msg.ReadFloat();
z = msg.ReadFloat();
x = msg.ReadSingle();
y = msg.ReadSingle();
z = msg.ReadSingle();
property.SetValue(parentObject, new Vector3(x, y, z));
return;
case "vector4":
if (size != 16) break;
x = msg.ReadFloat();
y = msg.ReadFloat();
z = msg.ReadFloat();
w = msg.ReadFloat();
x = msg.ReadSingle();
y = msg.ReadSingle();
z = msg.ReadSingle();
w = msg.ReadSingle();
property.SetValue(parentObject, new Vector4(x, y, z, w));
return;
case "color":
@@ -196,17 +187,17 @@ namespace Barotrauma.Networking
property.SetValue(parentObject, new Rectangle(ix, iy, width, height));
return;
default:
msg.Position = oldPos; //reset position to properly read the string
msg.BitPosition = oldPos; //reset position to properly read the string
string incVal = msg.ReadString();
property.TrySetValue(parentObject, incVal);
return;
}
//size didn't match: skip this
msg.Position += 8 * size;
msg.BitPosition += (int)(8 * size);
}
public void Write(NetBuffer msg, object overrideValue = null)
public void Write(IWriteMessage msg, object overrideValue = null)
{
if (overrideValue == null) overrideValue = property.GetValue(parentObject);
switch (typeString)
@@ -287,7 +278,7 @@ namespace Barotrauma.Networking
ServerName = serverName;
Port = port;
QueryPort = queryPort;
//EnableUPnP = enableUPnP;
EnableUPnP = enableUPnP;
this.maxPlayers = maxPlayers;
this.isPublic = isPublic;
@@ -328,7 +319,7 @@ namespace Barotrauma.Networking
}
}
}
public string ServerName;
private string serverMessageText;
@@ -346,7 +337,9 @@ namespace Barotrauma.Networking
public int Port;
public int QueryPort;
public bool EnableUPnP;
public ServerLog ServerLog;
public Voting Voting;
@@ -354,10 +347,10 @@ namespace Barotrauma.Networking
public Dictionary<string, bool> MonsterEnabled { get; private set; }
public Dictionary<ItemPrefab, int> ExtraCargo { get; private set; }
private TimeSpan sparseUpdateInterval = new TimeSpan(0, 0, 0, 3);
private float selectedLevelDifficulty;
private string password;
private byte[] password;
public float AutoRestartTimer;
@@ -370,7 +363,7 @@ namespace Barotrauma.Networking
public List<SavedClientPermission> ClientPermissions { get; private set; } = new List<SavedClientPermission>();
public WhiteList Whitelist { get; private set; }
[Serialize(20, true)]
public int TickRate
{
@@ -446,7 +439,7 @@ namespace Barotrauma.Networking
ServerDetailsChanged = true;
}
}
[Serialize(true, true)]
public bool EndRoundAtLevelEnd
{
@@ -514,9 +507,15 @@ namespace Barotrauma.Networking
public bool HasPassword
{
get { return !string.IsNullOrEmpty(password); }
get { return password != null; }
#if CLIENT
set
{
password = value ? (password ?? new byte[1]) : null;
}
#endif
}
[Serialize(true, true)]
public bool AllowVoteKick
{
@@ -555,7 +554,7 @@ namespace Barotrauma.Networking
ServerDetailsChanged = true;
}
}
[Serialize(0, true)]
public int BotCount
{
@@ -569,7 +568,8 @@ namespace Barotrauma.Networking
get;
set;
}
[Serialize(BotSpawnMode.Normal, true)]
public BotSpawnMode BotSpawnMode
{
get;
@@ -588,7 +588,7 @@ namespace Barotrauma.Networking
get;
set;
}
[Serialize(true, true)]
public bool AllowRewiring
{
@@ -604,6 +604,7 @@ namespace Barotrauma.Networking
}
private YesNoMaybe traitorsEnabled;
[Serialize(YesNoMaybe.No, true)]
public YesNoMaybe TraitorsEnabled
{
get { return traitorsEnabled; }
@@ -615,6 +616,13 @@ namespace Barotrauma.Networking
}
}
[Serialize(defaultValue: 1, isSaveable: true)]
public int TraitorsMinPlayerCount
{
get;
set;
}
private SelectionMode subSelectionMode;
[Serialize(SelectionMode.Manual, true)]
public SelectionMode SubSelectionMode
@@ -642,7 +650,7 @@ namespace Barotrauma.Networking
}
public BanList BanList { get; private set; }
[Serialize(0.6f, true)]
public float EndVoteRequiredRatio
{
@@ -663,7 +671,7 @@ namespace Barotrauma.Networking
get;
private set;
}
[Serialize(120.0f, true)]
public float KickAFKTime
{
@@ -671,20 +679,6 @@ namespace Barotrauma.Networking
private set;
}
[Serialize(true, true)]
public bool TraitorUseRatio
{
get;
private set;
}
[Serialize(0.2f, true)]
public float TraitorRatio
{
get;
private set;
}
private bool karmaEnabled;
[Serialize(false, true)]
public bool KarmaEnabled
@@ -719,7 +713,7 @@ namespace Barotrauma.Networking
get;
set;
}
public int MaxPlayers
{
get { return maxPlayers; }
@@ -745,26 +739,42 @@ namespace Barotrauma.Networking
get;
private set;
}
public void SetPassword(string password)
{
if (string.IsNullOrEmpty(password))
{
this.password = "";
this.password = null;
}
else
{
this.password = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password)));
this.password = Lidgren.Network.NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password));
}
}
public bool IsPasswordCorrect(string input, int nonce)
public static byte[] SaltPassword(byte[] password, int salt)
{
byte[] saltedPw = new byte[password.Length*2];
for (int i = 0; i < password.Length; i++)
{
saltedPw[(i * 2)] = password[i];
saltedPw[(i * 2) + 1] = (byte)((salt >> (8 * (i % 4))) & 0xff);
}
saltedPw = Lidgren.Network.NetUtility.ComputeSHAHash(saltedPw);
return saltedPw;
}
public bool IsPasswordCorrect(byte[] input, int salt)
{
if (!HasPassword) return true;
string saltedPw = password;
saltedPw = saltedPw + Convert.ToString(nonce);
saltedPw = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(saltedPw)));
return input == saltedPw;
byte[] saltedPw = SaltPassword(password, salt);
DebugConsole.NewMessage(ToolBox.ByteArrayToString(input)+" "+ToolBox.ByteArrayToString(saltedPw));
if (input.Length != saltedPw.Length) return false;
for (int i=0;i<input.Length;i++)
{
if (input[i] != saltedPw[i]) return false;
}
return true;
}
/// <summary>
@@ -795,7 +805,7 @@ namespace Barotrauma.Networking
}
}
public void ReadMonsterEnabled(NetBuffer inc)
public void ReadMonsterEnabled(IReadMessage inc)
{
InitMonstersEnabled();
List<string> monsterNames = MonsterEnabled.Keys.ToList();
@@ -806,7 +816,7 @@ namespace Barotrauma.Networking
inc.ReadPadBits();
}
public void WriteMonsterEnabled(NetBuffer msg, Dictionary<string, bool> monsterEnabled = null)
public void WriteMonsterEnabled(IWriteMessage msg, Dictionary<string, bool> monsterEnabled = null)
{
//monster spawn settings
if (monsterEnabled == null) monsterEnabled = MonsterEnabled;
@@ -819,7 +829,7 @@ namespace Barotrauma.Networking
msg.WritePadBits();
}
public bool ReadExtraCargo(NetBuffer msg)
public bool ReadExtraCargo(IReadMessage msg)
{
bool changed = false;
UInt32 count = msg.ReadUInt32();
@@ -844,7 +854,7 @@ namespace Barotrauma.Networking
return changed;
}
public void WriteExtraCargo(NetBuffer msg)
public void WriteExtraCargo(IWriteMessage msg)
{
if (ExtraCargo == null)
{
@@ -9,10 +9,20 @@ namespace Barotrauma.Steam
{
public const bool USE_STEAM = true;
public const int STEAMP2P_OWNER_PORT = 30000;
public const uint AppID = 602960;
private Facepunch.Steamworks.Client client;
private Server server;
private Facepunch.Steamworks.Server server;
private static List<string> initializationErrors = new List<string>();
public static IEnumerable<string> InitializationErrors
{
get { return initializationErrors; }
}
public const string MetadataFileName = "filelist.xml";
private Dictionary<string, int> tagCommonness = new Dictionary<string, int>()
{
@@ -63,6 +73,34 @@ namespace Barotrauma.Steam
instance = new SteamManager();
}
public static ulong GetSteamID()
{
if (instance == null || !instance.isInitialized)
{
return 0;
}
if (instance.client != null)
{
return instance.client.SteamId;
}
else if (instance.server != null)
{
return instance.server.SteamId;
}
return 0;
}
public static string GetUsername()
{
if (instance == null || !instance.isInitialized || instance.client == null)
{
return "";
}
return instance.client.Username;
}
public static void OverlayCustomURL(string url)
{
if (instance == null || !instance.isInitialized || instance.client == null)
@@ -146,5 +184,33 @@ namespace Barotrauma.Steam
instance.server = null;
instance = null;
}
public static UInt64 SteamIDStringToUInt64(string str)
{
if (string.IsNullOrWhiteSpace(str)) { return 0; }
UInt64 retVal;
if (UInt64.TryParse(str, out retVal) && retVal >(1<<52)) { return retVal; }
if (str.ToUpper().IndexOf("STEAM_") != 0) { return 0; }
string[] split = str.Substring(6).Split(':');
if (split.Length != 3) { return 0; }
UInt64 universe = 0; UInt64 y = 0; UInt64 accountNumber = 0;
if (!UInt64.TryParse(split[0], out universe)) { return 0; }
if (!UInt64.TryParse(split[1], out y)) { return 0; }
if (!UInt64.TryParse(split[2], out accountNumber)) { return 0; }
UInt64 accountInstance = 1; UInt64 accountType = 1;
return (universe << 56) | (accountType << 52) | (accountInstance << 32) | (accountNumber << 1) | y;
}
public static string SteamIDUInt64ToString(UInt64 uint64)
{
UInt64 y = uint64 & 0x1;
UInt64 accountNumber = (uint64 >> 1) & 0x7fffffff;
UInt64 universe = (uint64 >> 56) & 0xff;
return "STEAM_" + universe.ToString() + ":" + y.ToString() + ":" + accountNumber.ToString();
}
}
}
@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
using System.Collections.Generic;
using System.Text;
@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
@@ -113,7 +112,7 @@ namespace Barotrauma.Networking
outBuf = null;
}
public virtual void Write(NetBuffer msg)
public virtual void Write(IWriteMessage msg)
{
if (!CanSend) throw new Exception("Called Write on a VoipQueue not set up for sending");
@@ -127,7 +126,7 @@ namespace Barotrauma.Networking
}
}
public virtual bool Read(NetBuffer msg)
public virtual bool Read(IReadMessage msg)
{
if (!CanReceive) throw new Exception("Called Read on a VoipQueue not set up for receiving");
@@ -138,7 +137,7 @@ namespace Barotrauma.Networking
for (int i = 0; i < BUFFER_COUNT; i++)
{
bufferLengths[i] = msg.ReadByte();
msg.ReadBytes(buffers[i], 0, bufferLengths[i]);
buffers[i] = msg.ReadBytes(bufferLengths[i]);
}
newestBufferInd = BUFFER_COUNT - 1;
LatestBufferID = incLatestBufferID;
@@ -150,7 +149,7 @@ namespace Barotrauma.Networking
for (int i = 0; i < BUFFER_COUNT; i++)
{
byte len = msg.ReadByte();
msg.Position += len * 8;
msg.BitPosition += len * 8;
}
return false;
}
@@ -2,7 +2,6 @@
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -12,7 +12,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
[AttributeUsage(AttributeTargets.Property)]
public class Editable : Attribute
class Editable : Attribute
{
public int MaxLength;
public int DecimalCount = 1;
@@ -45,7 +45,7 @@ namespace Barotrauma
}
[AttributeUsage(AttributeTargets.Property)]
public class InGameEditable : Editable
class InGameEditable : Editable
{
}
@@ -166,7 +166,6 @@ namespace Barotrauma
try
{
switch (typeName)
{
case "bool":
@@ -175,20 +174,26 @@ namespace Barotrauma
propertyInfo.SetValue(parentObject, boolValue, null);
break;
case "int":
int intVal;
if (int.TryParse(value, out intVal))
if (int.TryParse(value, out int intVal))
{
if (TrySetValueWithoutReflection(parentObject, intVal)) { return true; }
propertyInfo.SetValue(parentObject, intVal, null);
}
else
{
return false;
}
break;
case "float":
float floatVal;
if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatVal))
if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out float floatVal))
{
if (TrySetValueWithoutReflection(parentObject, floatVal)) { return true; }
propertyInfo.SetValue(parentObject, floatVal, null);
}
else
{
return false;
}
break;
case "string":
propertyInfo.SetValue(parentObject, value, null);
@@ -420,6 +425,9 @@ namespace Barotrauma
case "Charge":
if (parentObject is PowerContainer powerContainer) { return powerContainer.Charge; }
break;
case "Overload":
if (parentObject is PowerTransfer powerTransfer) { return powerTransfer.Overload; }
break;
case "AvailableFuel":
{ if (parentObject is Reactor reactor) { return reactor.AvailableFuel; } }
break;
@@ -662,5 +670,33 @@ namespace Barotrauma
element.SetAttributeValue(property.NameToLowerInvariant, stringValue);
}
}
/// <summary>
/// Upgrade the properties of an entity saved with an older version of the game. Properties that should be upgraded are defined using "Upgrade" elements in the config file.
/// for example, <Upgrade gameversion="0.9.2.0" scale="0.5"/> would force the scale of the entity to 0.5 if it was saved with a version prior to 0.9.2.0.
/// </summary>
/// <param name="entity">The entity to upgrade</param>
/// <param name="configElement">The XML element to get the upgrade instructions from (e.g. the config of an item prefab)</param>
/// <param name="savedVersion">The game version the entity was saved with</param>
public static void UpgradeGameVersion(ISerializableEntity entity, XElement configElement, Version savedVersion)
{
foreach (XElement subElement in configElement.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "upgrade") { continue; }
var upgradeVersion = new Version(subElement.GetAttributeString("gameversion", "0.0.0.0"));
if (savedVersion < upgradeVersion)
{
foreach (XAttribute attribute in subElement.Attributes())
{
string attributeName = attribute.Name.ToString().ToLowerInvariant();
if (attributeName == "gameversion") { continue; }
if (entity.SerializableProperties.TryGetValue(attributeName, out SerializableProperty property))
{
property.TrySetValue(entity, attribute.Value);
}
}
}
}
}
}
}
@@ -289,6 +289,30 @@ namespace Barotrauma
return intValue;
}
public static ushort[] GetAttributeUshortArray(this XElement element, string name, ushort[] defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
string stringValue = element.Attribute(name).Value;
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
string[] splitValue = stringValue.Split(',');
ushort[] ushortValue = new ushort[splitValue.Length];
for (int i = 0; i < splitValue.Length; i++)
{
try
{
ushort val = ushort.Parse(splitValue[i]);
ushortValue[i] = val;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
}
return ushortValue;
}
public static bool GetAttributeBool(this XElement element, string name, bool defaultValue)
{
@@ -259,23 +259,22 @@ namespace Barotrauma
#if SERVER
if (GameMain.Server?.TraitorManager != null)
{
foreach (Traitor traitor in GameMain.Server.TraitorManager.TraitorList)
if (GameMain.Server.TraitorManager.IsTraitor(character))
{
if (traitor.TargetCharacter == character)
{
//killed the target as a traitor
UnlockAchievement(traitor.Character, "traitorwin");
}
else if (traitor.Character == character)
{
//someone killed a traitor
UnlockAchievement(causeOfDeath.Killer, "killtraitor");
}
UnlockAchievement(causeOfDeath.Killer, "killtraitor");
}
}
#endif
}
public static void OnTraitorWin(Character character)
{
#if CLIENT
if (GameMain.Client != null || GameMain.GameSession == null) return;
#endif
UnlockAchievement(character, "traitorwin");
}
public static void OnRoundEnded(GameSession gameSession)
{
//made it to the destination
@@ -303,13 +302,11 @@ namespace Barotrauma
}
}
#if CLIENT
if (GameMain.Client != null) { return; }
#endif
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (gameSession.Mission != null)
{
if (gameSession.Mission is CombatMission combatMission)
if (gameSession.Mission is CombatMission combatMission && GameMain.GameSession.WinningTeam.HasValue)
{
//all characters that are alive and in the winning team get an achievement
UnlockAchievement(gameSession.Mission.Prefab.AchievementIdentifier + (int)GameMain.GameSession.WinningTeam, true,
+126 -19
View File
@@ -69,7 +69,7 @@ namespace Barotrauma
}
return language;
}
public static void LoadTextPacks(IEnumerable<ContentPackage> selectedContentPackages)
{
availableLanguages.Clear();
@@ -311,10 +311,83 @@ namespace Barotrauma
}
}
return string.Format(text, args);
return string.Format(text, args);
}
public static string FormatServerMessage(string textId)
{
return $"{textId}~";
}
public static string FormatServerMessage(string message, IEnumerable<string> keys, IEnumerable<string> values)
{
if (keys == null || values == null || !keys.Any() || !values.Any())
{
return FormatServerMessage(message);
}
var startIndex = message.LastIndexOf('/') + 1;
var endIndex = message.IndexOf('~', startIndex);
if (endIndex == -1)
{
endIndex = message.Length - 1;
}
var textId = message.Substring(startIndex, endIndex - startIndex + 1);
var keysWithValues = keys.Zip(values, (key, value) => new { Key = key, Value = value });
var prefixEntries = keysWithValues.Select((kv, index) =>
{
if (kv.Value.IndexOfAny(new char[] { '~', '/' }) != -1)
{
var kvStartIndex = kv.Value.LastIndexOf('/') + 1;
return kv.Value.Substring(0, kvStartIndex) + $"[{textId}.{index}]={kv.Value.Substring(kvStartIndex)}";
}
else
{
return null;
}
}).Where(e => e != null).ToArray();
return string.Join("",
(prefixEntries.Length > 0 ? string.Join("/", prefixEntries) + "/" : ""),
message,
string.Join("", keysWithValues.Select((kv, index) => kv.Value.IndexOfAny(new char[] { '~', '/' }) != -1 ? $"~{kv.Key}=[{textId}.{index}]" : $"~{kv.Key}={kv.Value}").ToArray())
);
}
static readonly string[] genderPronounVariables = new string[] {
"[genderpronoun]",
"[genderpronounpossessive]",
"[genderpronounreflexive]",
"[Genderpronoun]",
"[Genderpronounpossessive]",
"[Genderpronounreflexive]"
};
static readonly string[] genderPronounMaleValues = new string[] {
"PronounMaleLowercase",
"PronounPossessiveMaleLowercase",
"PronounReflexiveMaleLowercase",
"PronounMale",
"PronounPossessiveMale",
"PronounReflexiveMale"
};
static readonly string[] genderPronounFemaleValues = new string[] {
"PronounFemaleLowercase",
"PronounPossessiveFemaleLowercase",
"PronounReflexiveFemaleLowercase",
"PronounMale",
"PronounPossessiveFemale",
"PronounReflexiveFemale"
};
public static string FormatServerMessageWithGenderPronouns(Gender gender, string message, IEnumerable<string> keys, IEnumerable<string> values)
{
return FormatServerMessage(message, keys.Concat(genderPronounVariables), values.Concat(gender == Gender.Male ? genderPronounMaleValues : genderPronounFemaleValues));
}
static readonly Regex reReplacedMessage = new Regex(@"^(?<variable>[\[\].A-Za-z0-9_]+?)=(?<message>.*)$", RegexOptions.Compiled);
// Format: ServerMessage.Identifier1/ServerMessage.Indentifier2~[variable1]=value~[variable2]=value
// Also: replacement=ServerMessage.Identifier1~[variable1]=value/ServerMessage.Identifier2~[variable2]=replacement
public static string GetServerMessage(string serverMessage)
{
if (!textPacks.ContainsKey(Language))
@@ -328,6 +401,7 @@ namespace Barotrauma
}
string[] messages = serverMessage.Split('/');
var replacedMessages = new Dictionary<string, string>();
bool translationsFound = false;
@@ -335,8 +409,17 @@ namespace Barotrauma
{
for (int i = 0; i < messages.Length; i++)
{
if (!IsServerMessageWithVariables(messages[i])) // No variables, try to translate
if (messages[i].EndsWith("~", StringComparison.Ordinal))
{
messages[i] = messages[i].Substring(0, messages[i].Length - 1);
}
if (!IsServerMessageWithVariables(messages[i]) && !messages[i].Contains('=')) // No variables, try to translate
{
foreach (var replacedMessage in replacedMessages)
{
messages[i] = messages[i].Replace(replacedMessage.Key, replacedMessage.Value);
}
if (messages[i].Contains(" ")) continue; // Spaces found, do not translate
string msg = Get(messages[i], true);
if (msg != null) // If a translation was found, otherwise use the original
@@ -347,7 +430,22 @@ namespace Barotrauma
}
else
{
var match = reReplacedMessage.Match(messages[i]);
string messageVariable = null;
if (match.Success)
{
messageVariable = match.Groups["variable"].ToString();
messages[i] = match.Groups["message"].ToString();
}
foreach (var replacedMessage in replacedMessages)
{
messages[i] = messages[i].Replace(replacedMessage.Key, replacedMessage.Value);
}
string[] messageWithVariables = messages[i].Split('~');
string msg = Get(messageWithVariables[0], true);
if (msg != null) // If a translation was found, otherwise use the original
@@ -355,7 +453,7 @@ namespace Barotrauma
messages[i] = msg;
translationsFound = true;
}
else
else if (messageVariable == null)
{
continue; // No translation found, probably caused by player input -> skip variable handling
}
@@ -366,6 +464,12 @@ namespace Barotrauma
string[] variableAndValue = messageWithVariables[j].Split('=');
messages[i] = messages[i].Replace(variableAndValue[0], variableAndValue[1]);
}
if (messageVariable != null)
{
replacedMessages[messageVariable] = messages[i];
messages[i] = null;
}
}
}
@@ -374,7 +478,10 @@ namespace Barotrauma
string translatedServerMessage = string.Empty;
for (int i = 0; i < messages.Length; i++)
{
translatedServerMessage += messages[i];
if (messages[i] != null)
{
translatedServerMessage += messages[i];
}
}
return translatedServerMessage;
}
@@ -427,7 +534,7 @@ namespace Barotrauma
}
return string.Join(separator, texts);
}
public static string EnsureUTF8(string text)
{
byte[] bytes = Encoding.Default.GetBytes(text);
@@ -494,24 +601,24 @@ namespace Barotrauma
{
if (gender == Gender.Male)
{
return text.Replace("[genderpronoun]", Get("PronounMale").ToLower())
.Replace("[genderpronounpossessive]", Get("PronounPossessiveMale").ToLower())
.Replace("[genderpronounreflexive]", Get("PronounReflexiveMale").ToLower())
.Replace("[Genderpronoun]", Capitalize(Get("PronounMale")))
.Replace("[Genderpronounpossessive]", Capitalize(Get("PronounPossessiveMale")))
.Replace("[Genderpronounreflexive]", Capitalize(Get("PronounReflexiveMale")));
return text.Replace("[genderpronoun]", Get("PronounMaleLowercase"))
.Replace("[genderpronounpossessive]", Get("PronounPossessiveMaleLowercase"))
.Replace("[genderpronounreflexive]", Get("PronounReflexiveMaleLowercase"))
.Replace("[Genderpronoun]", Get("PronounMale"))
.Replace("[Genderpronounpossessive]", Get("PronounPossessiveMale"))
.Replace("[Genderpronounreflexive]", Get("PronounReflexiveMale"));
}
else
{
return text.Replace("[genderpronoun]", Get("PronounFemale").ToLower())
.Replace("[genderpronounpossessive]", Get("PronounPossessiveFemale").ToLower())
.Replace("[genderpronounreflexive]", Get("PronounReflexiveFemale").ToLower())
.Replace("[Genderpronoun]", Capitalize(Get("PronounFemale")))
.Replace("[Genderpronounpossessive]", Capitalize(Get("PronounPossessiveFemale")))
.Replace("[Genderpronounreflexive]", Capitalize(Get("PronounReflexiveFemale")));
return text.Replace("[genderpronoun]", Get("PronounFemaleLowercase"))
.Replace("[genderpronounpossessive]", Get("PronounPossessiveFemaleLowercase"))
.Replace("[genderpronounreflexive]", Get("PronounReflexiveFemaleLowerCase"))
.Replace("[Genderpronoun]", Get("PronounFemale"))
.Replace("[Genderpronounpossessive]", Get("PronounPossessiveFemale"))
.Replace("[Genderpronounreflexive]", Get("PronounReflexiveFemale"));
}
}
static Regex isCJK = new Regex(
@"\p{IsHangulJamo}|" +
@"\p{IsCJKRadicalsSupplement}|" +
@@ -50,6 +50,10 @@ namespace Barotrauma
public string Get(string textTag)
{
if (string.IsNullOrEmpty(textTag))
{
return null;
}
if (!texts.TryGetValue(textTag.ToLowerInvariant(), out List<string> textList) || !textList.Any())
{
return null;
@@ -404,7 +404,7 @@ namespace Barotrauma
}
}
public static void CopyFolder(string sourceDirName, string destDirName, bool copySubDirs)
public static void CopyFolder(string sourceDirName, string destDirName, bool copySubDirs, bool overwriteExisting = false)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
@@ -428,6 +428,10 @@ namespace Barotrauma
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
if (overwriteExisting && File.Exists(temppath))
{
File.Delete(temppath);
}
file.CopyTo(temppath, false);
}
@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -7,6 +6,7 @@ using System.Security.Cryptography;
using System.Reflection;
using System.Text;
using Microsoft.Xna.Framework;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -313,13 +313,16 @@ namespace Barotrauma
/// <summary>
/// Reads a number of bits from the buffer and inserts them to a new NetBuffer instance
/// </summary>
public static NetBuffer ExtractBits(this NetBuffer originalBuffer, int numberOfBits)
public static IReadMessage ExtractBits(this IReadMessage originalBuffer, int numberOfBits)
{
var buffer = new NetBuffer();
byte[] data = new byte[(int)Math.Ceiling(numberOfBits / (double)8)];
originalBuffer.ReadBits(data, 0, numberOfBits);
buffer.Write(data);
var buffer = new ReadWriteMessage();
for (int i=0;i<numberOfBits;i++)
{
bool bit = originalBuffer.ReadBoolean();
buffer.Write(bit);
}
buffer.BitPosition = 0;
return buffer;
}
@@ -407,5 +410,13 @@ namespace Barotrauma
//}
return destination;
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
}
}