diff --git a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs index b55a2d5c1..485c6381a 100644 --- a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs +++ b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.9.2.1")] -[assembly: AssemblyFileVersion("0.9.2.1")] +[assembly: AssemblyVersion("0.9.3.2")] +[assembly: AssemblyFileVersion("0.9.3.2")] diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs index a4927a4b7..522ed8426 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs @@ -105,15 +105,15 @@ namespace Barotrauma switch ((NetEntityEvent.Type)extraData[0]) { case NetEntityEvent.Type.InventoryState: - msg.WriteRangedIntegerDeprecated(0, 3, 0); + msg.WriteRangedInteger(0, 0, 3); Inventory.ClientWrite(msg, extraData); break; case NetEntityEvent.Type.Treatment: - msg.WriteRangedIntegerDeprecated(0, 3, 1); + msg.WriteRangedInteger(1, 0, 3); msg.Write(AnimController.Anim == AnimController.Animation.CPR); break; case NetEntityEvent.Type.Status: - msg.WriteRangedIntegerDeprecated(0, 3, 2); + msg.WriteRangedInteger(2, 0, 3); break; } } @@ -131,7 +131,7 @@ namespace Barotrauma msg.Write(inputCount); for (int i = 0; i < inputCount; i++) { - msg.WriteRangedIntegerDeprecated(0, (int)InputNetFlags.MaxVal, (int)memInput[i].states); + msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal); msg.Write(memInput[i].intAim); if (memInput[i].states.HasFlag(InputNetFlags.Select) || memInput[i].states.HasFlag(InputNetFlags.Deselect) || @@ -283,9 +283,16 @@ namespace Barotrauma case 0: if (Inventory == null) { - string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ")"; + string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")"; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + + //read anyway to prevent messing up reading the rest of the message + byte itemCount = msg.ReadByte(); + for (int i = 0; i < itemCount; i++) + { + msg.ReadUInt16(); + } } else { @@ -337,7 +344,7 @@ namespace Barotrauma public static Character ReadSpawnData(IReadMessage inc, bool spawn = true) { - DebugConsole.NewMessage("READING CHARACTER SPAWN DATA", Color.Cyan); + DebugConsole.Log("Reading character spawn data"); if (GameMain.Client == null) return null; @@ -433,7 +440,16 @@ namespace Barotrauma if (causeOfDeathType == CauseOfDeathType.Affliction) { int afflictionIndex = msg.ReadRangedInteger(0, AfflictionPrefab.List.Count - 1); - causeOfDeathAffliction = AfflictionPrefab.List[afflictionIndex]; + if (afflictionIndex < 0 || afflictionIndex >= AfflictionPrefab.List.Count) + { + string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction index out of bounds (index: {afflictionIndex}, affliction count: {AfflictionPrefab.List.Count})"; + causeOfDeathType = CauseOfDeathType.Unknown; + GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + } + else + { + causeOfDeathAffliction = AfflictionPrefab.List[afflictionIndex]; + } } byte severedLimbCount = msg.ReadByte(); @@ -452,13 +468,20 @@ namespace Barotrauma for (int i = 0; i < severedLimbCount; i++) { int severedJointIndex = msg.ReadByte(); - AnimController.SeverLimbJoint(AnimController.LimbJoints[severedJointIndex]); + if (severedJointIndex < 0 || severedJointIndex >= AnimController.LimbJoints.Length) + { + string errorMsg = $"Error in CharacterNetworking.ReadStatus: severed joint index out of bounds (index: {severedJointIndex}, joint count: {AnimController.LimbJoints.Length})"; + GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:JointIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + } + else + { + AnimController.SeverLimbJoint(AnimController.LimbJoints[severedJointIndex]); + } } } else { - if (IsDead) Revive(); - + if (IsDead) { Revive(); } CharacterHealth.ClientRead(msg); } } diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index bf118207c..2d6c93afe 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -1359,7 +1359,7 @@ namespace Barotrauma } ShowQuestionPrompt("Permission to grant to client #" + id + "?", (perm) => { - GameMain.Client.SendConsoleCommand("giveperm " + id + " " + perm); + GameMain.Client?.SendConsoleCommand("giveperm " + id + " " + perm); }); } ); @@ -1384,7 +1384,7 @@ namespace Barotrauma ShowQuestionPrompt("Permission to revoke from client #" + id + "?", (perm) => { - GameMain.Client.SendConsoleCommand("revokeperm " + id + " " + perm); + GameMain.Client?.SendConsoleCommand("revokeperm " + id + " " + perm); }); } ); @@ -1408,7 +1408,7 @@ namespace Barotrauma } ShowQuestionPrompt("Rank to grant to client #" + id + "?", (rank) => { - GameMain.Client.SendConsoleCommand("giverank " + id + " " + rank); + GameMain.Client?.SendConsoleCommand("giverank " + id + " " + rank); }); } ); @@ -1427,7 +1427,7 @@ namespace Barotrauma ShowQuestionPrompt("Console command permissions to grant to client #" + id + "? You may enter multiple commands separated with a space.", (commandNames) => { - GameMain.Client.SendConsoleCommand("givecommandperm " + id + " " + commandNames); + GameMain.Client?.SendConsoleCommand("givecommandperm " + id + " " + commandNames); }); } ); @@ -1446,7 +1446,7 @@ namespace Barotrauma ShowQuestionPrompt("Console command permissions to revoke from client #" + id + "? You may enter multiple commands separated with a space.", (commandNames) => { - GameMain.Client.SendConsoleCommand("revokecommandperm " + id + " " + commandNames); + GameMain.Client?.SendConsoleCommand("revokecommandperm " + id + " " + commandNames); }); } ); @@ -1487,7 +1487,7 @@ namespace Barotrauma banDuration = parsedBanDuration; } - GameMain.Client.SendConsoleCommand( + GameMain.Client?.SendConsoleCommand( "banendpoint " + args[0] + " " + (banDuration.HasValue ? banDuration.Value.TotalSeconds.ToString() : "0") + " " + @@ -1538,7 +1538,7 @@ namespace Barotrauma NewMessage("Index out of bounds!", Color.Red); return; } - GameMain.Client.SendConsoleCommand("campaigndestination " + destinationIndex); + GameMain.Client?.SendConsoleCommand("campaigndestination " + destinationIndex); }); } else diff --git a/Barotrauma/BarotraumaClient/Source/Fonts/ScalableFont.cs b/Barotrauma/BarotraumaClient/Source/Fonts/ScalableFont.cs index 7b35c576b..3ecda8c76 100644 --- a/Barotrauma/BarotraumaClient/Source/Fonts/ScalableFont.cs +++ b/Barotrauma/BarotraumaClient/Source/Fonts/ScalableFont.cs @@ -32,6 +32,12 @@ namespace Barotrauma private set; } + public bool IsCJK + { + get; + private set; + } + public uint Size { get @@ -58,11 +64,16 @@ namespace Barotrauma } public ScalableFont(XElement element, GraphicsDevice gd = null) - : this (element.GetAttributeString("file", ""), (uint)element.GetAttributeInt("size", 14), gd, element.GetAttributeBool("dynamicloading", false)) - { + : this( + element.GetAttributeString("file", ""), + (uint)element.GetAttributeInt("size", 14), + gd, + element.GetAttributeBool("dynamicloading", false), + element.GetAttributeBool("iscjk", false)) + { } - public ScalableFont(string filename, uint size, GraphicsDevice gd = null, bool dynamicLoading = false) + public ScalableFont(string filename, uint size, GraphicsDevice gd = null, bool dynamicLoading = false, bool isCJK = false) { lock (mutex) { @@ -85,6 +96,7 @@ namespace Barotrauma this.textures = new List(); this.texCoords = new Dictionary(); this.DynamicLoading = dynamicLoading; + this.IsCJK = isCJK; this.graphicsDevice = gd; if (gd != null && !dynamicLoading) diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs index b9a37894c..00cc87815 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs @@ -83,6 +83,7 @@ namespace Barotrauma public static ScalableFont VideoTitleFont => Style?.VideoTitleFont; public static ScalableFont ObjectiveTitleFont => Style?.ObjectiveTitleFont; public static ScalableFont ObjectiveNameFont => Style?.ObjectiveNameFont; + public static ScalableFont CJKFont { get; private set; } public static UISprite UIGlow => Style.UIGlow; @@ -154,6 +155,7 @@ namespace Barotrauma { GUI.graphicsDevice = graphicsDevice; var uiStyles = ContentPackage.GetFilesOfType(selectedContentPackages, ContentType.UIStyle).ToList(); + if (uiStyles.Count == 0) { DebugConsole.ThrowError("No UI styles defined in the selected content package!"); @@ -165,6 +167,11 @@ namespace Barotrauma } Style = new GUIStyle(uiStyles[0], graphicsDevice); + if (CJKFont == null) + { + CJKFont = new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf", + Font.Size, graphicsDevice, dynamicLoading: true, isCJK: true); + } } public static void LoadContent(bool loadSounds = true) @@ -408,7 +415,7 @@ namespace Barotrauma if (Character.Controlled?.Inventory != null) { - if (!Character.Controlled.LockHands && Character.Controlled.Stun >= -0.1f && !Character.Controlled.IsDead) + if (!Character.Controlled.LockHands && Character.Controlled.Stun < 0.1f && !Character.Controlled.IsDead) { Inventory.DrawFront(spriteBatch); } @@ -1563,9 +1570,10 @@ namespace Barotrauma button.OnClicked += (btn, userData) => { var quitButton = button; - if (GameMain.GameSession != null) + if (GameMain.GameSession != null || (Screen.Selected is CharacterEditorScreen charEditScreen || Screen.Selected is SubEditorScreen subEditScreen)) { - var msgBox = new GUIMessageBox("", TextManager.Get("PauseMenuQuitVerification"), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") }) + string text = GameMain.GameSession == null ? "PauseMenuQuitVerificationEditor" : "PauseMenuQuitVerification"; + var msgBox = new GUIMessageBox("", TextManager.Get(text), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") }) { UserData = "verificationprompt" }; diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIButton.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIButton.cs index 05e991ab8..0ea3828e8 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIButton.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIButton.cs @@ -188,7 +188,7 @@ namespace Barotrauma { if (!Visible) return; base.Update(deltaTime); - if (Rect.Contains(PlayerInput.MousePosition) && CanBeSelected && Enabled && GUI.IsMouseOn(this)) + if (Rect.Contains(PlayerInput.MousePosition) && CanBeSelected && CanBeFocused && Enabled && GUI.IsMouseOn(this)) { state = ComponentState.Hover; if (PlayerInput.LeftButtonDown()) diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIListBox.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIListBox.cs index 13066a72e..ed822aa59 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIListBox.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIListBox.cs @@ -22,10 +22,9 @@ namespace Barotrauma public GUIScrollBar ScrollBar { get; private set; } public GUIFrame Content { get; private set; } + private Dictionary childVisible = new Dictionary(); + private int totalSize; - - private int spacing; - private bool childrenNeedsRecalculation; private bool scrollBarNeedsRecalculation; @@ -95,11 +94,7 @@ namespace Barotrauma get { return totalSize; } } - public int Spacing - { - get { return spacing; } - set { spacing = value; } - } + public int Spacing { get; set; } public override Color Color { @@ -244,37 +239,37 @@ namespace Barotrauma { if (ScrollBar.IsHorizontal) { - if (y + child.Rect.Height + spacing > Content.Rect.Height) + if (y + child.Rect.Height + Spacing > Content.Rect.Height) { y = 0; - x += child.Rect.Width + spacing; + x += child.Rect.Width + Spacing; if (child.RectTransform.AbsoluteOffset.X != x || child.RectTransform.AbsoluteOffset.Y != y) { child.RectTransform.AbsoluteOffset = new Point(x, y); } - y += child.Rect.Height + spacing; + y += child.Rect.Height + Spacing; } else { - y += child.Rect.Height + spacing; + y += child.Rect.Height + Spacing; } } else { - if (x + child.Rect.Width + spacing > Content.Rect.Width) + if (x + child.Rect.Width + Spacing > Content.Rect.Width) { x = 0; - y += child.Rect.Height + spacing; + y += child.Rect.Height + Spacing; if (child.RectTransform.AbsoluteOffset.X != x || child.RectTransform.AbsoluteOffset.Y != y) { child.RectTransform.AbsoluteOffset = new Point(x, y); } - x += child.Rect.Width + spacing; + x += child.Rect.Width + Spacing; } else { - x += child.Rect.Width + spacing; + x += child.Rect.Width + Spacing; } } } @@ -282,11 +277,11 @@ namespace Barotrauma { if (ScrollBar.IsHorizontal) { - x += child.Rect.Width + spacing; + x += child.Rect.Width + Spacing; } else { - y += child.Rect.Height + spacing; + y += child.Rect.Height + Spacing; } } } @@ -327,10 +322,23 @@ namespace Barotrauma public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0) { if (!Visible) { return; } + + foreach (GUIComponent child in Content.Children) + { + if (!childVisible.ContainsKey(child)) { childVisible[child] = child.Visible; } + if (childVisible[child] != child.Visible) + { + childVisible[child] = child.Visible; + childrenNeedsRecalculation = true; + scrollBarNeedsRecalculation = true; + break; + } + } if (childrenNeedsRecalculation) { RecalculateChildren(); + childVisible.Clear(); } UpdateOrder = order; @@ -511,30 +519,30 @@ namespace Barotrauma { if (ScrollBar.IsHorizontal) { - if (pos + child.Rect.Height + spacing > Content.Rect.Height) + if (pos + child.Rect.Height + Spacing > Content.Rect.Height) { pos = 0; - totalSize += child.Rect.Width + spacing; + totalSize += child.Rect.Width + Spacing; } - pos += child.Rect.Height + spacing; + pos += child.Rect.Height + Spacing; if (child == children.Last()) { - totalSize += child.Rect.Width + spacing; + totalSize += child.Rect.Width + Spacing; } } else { - if (pos + child.Rect.Width + spacing > Content.Rect.Width) + if (pos + child.Rect.Width + Spacing > Content.Rect.Width) { pos = 0; - totalSize += child.Rect.Height + spacing; + totalSize += child.Rect.Height + Spacing; } - pos += child.Rect.Width + spacing; + pos += child.Rect.Width + Spacing; if (child == children.Last()) { - totalSize += child.Rect.Height + spacing; + totalSize += child.Rect.Height + Spacing; } } } @@ -545,7 +553,7 @@ namespace Barotrauma { totalSize += (ScrollBar.IsHorizontal) ? child.Rect.Width : child.Rect.Height; } - totalSize += Content.CountChildren * spacing; + totalSize += Content.CountChildren * Spacing; } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIProgressBar.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIProgressBar.cs index eb784d6a7..f982c05f3 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIProgressBar.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIProgressBar.cs @@ -56,7 +56,21 @@ namespace Barotrauma { if (!Visible) return; - if (ProgressGetter != null) BarSize = ProgressGetter(); + if (ProgressGetter != null) + { + float newSize = MathHelper.Clamp(ProgressGetter(), 0.0f, 1.0f); + if (!MathUtils.IsValid(newSize)) + { + GameAnalyticsManager.AddErrorEventOnce( + "GUIProgressBar.Draw:GetProgress", + GameAnalyticsSDK.Net.EGAErrorSeverity.Error, + "ProgressGetter of a GUIProgressBar (" + ProgressGetter.Target.ToString() + " - " + ProgressGetter.Method.ToString() + ") returned an invalid value (" + newSize + ")\n" + Environment.StackTrace); + } + else + { + BarSize = newSize; + } + } Rectangle sliderRect = new Rectangle( frame.Rect.X, diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs index 26fe1031b..24ebb083a 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs @@ -155,7 +155,8 @@ namespace Barotrauma string file = GetFontFilePath(element); uint size = GetFontSize(element); bool dynamicLoading = GetFontDynamicLoading(element); - return new ScalableFont(file, size, graphicsDevice, dynamicLoading); + bool isCJK = GetIsCJK(element); + return new ScalableFont(file, size, graphicsDevice, dynamicLoading, isCJK); } private uint GetFontSize(XElement element) @@ -200,6 +201,20 @@ namespace Barotrauma return element.GetAttributeBool("dynamicloading", false); } + private bool GetIsCJK(XElement element) + { + foreach (XElement subElement in element.Elements()) + { + if (subElement.Name.ToString().ToLowerInvariant() != "override") { continue; } + string language = subElement.GetAttributeString("language", "").ToLowerInvariant(); + if (GameMain.Config.Language.ToLowerInvariant() == language) + { + return subElement.GetAttributeBool("iscjk", false); + } + } + return element.GetAttributeBool("iscjk", false); + } + public GUIComponentStyle GetComponentStyle(string name) { componentStyles.TryGetValue(name.ToLowerInvariant(), out GUIComponentStyle style); diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs index 07d3459e3..811e8f4ea 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs @@ -204,8 +204,13 @@ namespace Barotrauma if (textColor.HasValue) { this.textColor = textColor.Value; - } - this.Font = font ?? GUI.Font; + } + + //if the text is in chinese/korean/japanese and we're not using a CJK-compatible font, + //use the default CJK font as a fallback + var selectedFont = font ?? GUI.Font; + if (TextManager.IsCJK(text) && !selectedFont.IsCJK) { selectedFont = GUI.CJKFont; } + this.Font = selectedFont; this.textAlignment = textAlignment; this.Wrap = wrap; this.Text = text ?? ""; diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index ab0b3e4a2..5e8c4aa94 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -190,7 +190,8 @@ namespace Barotrauma { if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; } SetCharacterOrder(null, order, null, Character.Controlled); - foreach (var hull in Character.Controlled.GetVisibleHulls()) + var visibleHulls = new List(Character.Controlled.GetVisibleHulls()); + foreach (var hull in visibleHulls) { HumanAIController.PropagateHullSafety(Character.Controlled, hull); HumanAIController.RefreshTargets(Character.Controlled, order, hull); diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/CaptainTutorial.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/CaptainTutorial.cs index e8829789b..fd2bf7ea2 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/CaptainTutorial.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/CaptainTutorial.cs @@ -199,6 +199,7 @@ namespace Barotrauma.Tutorials SetHighlight(captain_navConsole.Item, true); SetHighlight(captain_sonar.Item, true); SetHighlight(captain_statusMonitor, true); + captain_navConsole.UseAutoDocking = false; do { //captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f); @@ -221,6 +222,7 @@ namespace Barotrauma.Tutorials } while (captain_sonar.CurrentMode != Sonar.Mode.Active); do { yield return null; } while (Vector2.Distance(Submarine.MainSub.WorldPosition, Level.Loaded.EndPosition) > 4000f); RemoveCompletedObjective(segments[5]); + captain_navConsole.UseAutoDocking = true; yield return new WaitForSeconds(4f, false); TriggerTutorialSegment(6); // Docking do diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/DoctorTutorial.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/DoctorTutorial.cs index 186656d22..37a99239c 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/DoctorTutorial.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/DoctorTutorial.cs @@ -256,6 +256,7 @@ namespace Barotrauma.Tutorials GameMain.GameSession.CrewManager.AddCharacter(doctor); GameMain.GameSession.CrewManager.AddCharacter(patient1); GameMain.GameSession.CrewManager.ToggleCrewAreaOpen = true; + patient1.CharacterHealth.UseHealthWindow = false; yield return new WaitForSeconds(3.0f, false); patient1.AIController.Enabled = true; @@ -277,6 +278,7 @@ namespace Barotrauma.Tutorials RemoveCompletedObjective(segments[3]); SetHighlight(doctor_medBayCabinet.Item, true); SetDoorAccess(doctor_thirdDoor, doctor_thirdDoorLight, true); + patient1.CharacterHealth.UseHealthWindow = true; yield return new WaitForSeconds(2.0f, false); diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/EngineerTutorial.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/EngineerTutorial.cs index 95dfd8b4b..1824f4b0f 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/EngineerTutorial.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/EngineerTutorial.cs @@ -169,7 +169,9 @@ namespace Barotrauma.Tutorials } engineer_wire_1 = Item.ItemList.Find(i => i.HasTag("engineer_wire_1")); + engineer_wire_1.SpriteColor = Color.Transparent; engineer_wire_2 = Item.ItemList.Find(i => i.HasTag("engineer_wire_2")); + engineer_wire_2.SpriteColor = Color.Transparent; engineer_lamp_1 = Item.ItemList.Find(i => i.HasTag("engineer_lamp_1")).GetComponent(); engineer_lamp_2 = Item.ItemList.Find(i => i.HasTag("engineer_lamp_2")).GetComponent(); engineer_fourthDoor = Item.ItemList.Find(i => i.HasTag("engineer_fourthdoor")).GetComponent(); @@ -416,6 +418,8 @@ namespace Barotrauma.Tutorials do { CheckJunctionBoxHighlights(); yield return null; } while (!engineer_submarineJunctionBox_1.IsFullCondition || !engineer_submarineJunctionBox_2.IsFullCondition || !engineer_submarineJunctionBox_3.IsFullCondition); CheckJunctionBoxHighlights(); RemoveCompletedObjective(segments[4]); + yield return new WaitForSeconds(2f, false); + TriggerTutorialSegment(5); // Powerup reactor SetHighlight(engineer_submarineReactor.Item, true); engineer.AddActiveObjectiveEntity(engineer_submarineReactor.Item, engineer_reactorIcon, engineer_reactorIconColor); @@ -425,6 +429,8 @@ namespace Barotrauma.Tutorials RemoveCompletedObjective(segments[5]); GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Complete"), ChatMessageType.Radio, null); + yield return new WaitForSeconds(4f, false); + CoroutineManager.StartCoroutine(TutorialCompleted()); } @@ -472,17 +478,31 @@ namespace Barotrauma.Tutorials private void CheckGhostWires() { - if (engineer_wire_1 != null && engineer_lamp_1.Voltage > engineer_lamp_1.MinVoltage) + Color wireColor = + Color.Orange * + MathHelper.Lerp(0.25f, 0.75f, (float)(Math.Sin((Timing.TotalTime * 4.0f)) + 1.0f) / 2.0f); + + if (engineer_wire_1 != null) { - engineer_wire_1.Remove(); - engineer_wire_1 = null; + engineer_wire_1.SpriteColor = wireColor; + if (engineer_lamp_1.Voltage > engineer_lamp_1.MinVoltage) + { + engineer_wire_1.Remove(); + engineer_wire_1 = null; + } } - if (engineer_wire_2 != null && engineer_lamp_2.Voltage > engineer_lamp_2.MinVoltage) + + if (engineer_wire_2 != null) { - engineer_wire_2.Remove(); - engineer_wire_2 = null; + engineer_wire_2.SpriteColor = wireColor; + if (engineer_lamp_2.Voltage > engineer_lamp_2.MinVoltage) + { + engineer_wire_2.Remove(); + engineer_wire_2 = null; + } } + } private void HandleJunctionBoxWiringHighlights() diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/MechanicTutorial.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/MechanicTutorial.cs index cd20b3d14..7f2144286 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/MechanicTutorial.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/MechanicTutorial.cs @@ -526,6 +526,8 @@ namespace Barotrauma.Tutorials mechanic.AddActiveObjectiveEntity(mechanic_brokenWall_2, mechanic_weldIcon, mechanic_repairIconColor); do { yield return null; } while (WallHasDamagedSections(mechanic_brokenWall_2)); mechanic.RemoveActiveObjectiveEntity(mechanic_brokenWall_2); + yield return new WaitForSeconds(2f, false); + TriggerTutorialSegment(9, GameMain.Config.KeyBind(InputType.Use)); // Repairing machinery (pump) SetHighlight(mechanic_brokenPump.Item, true); mechanic_brokenPump.CanBeSelected = true; diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/OfficerTutorial.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/OfficerTutorial.cs index ce0cb9f88..5d8e66fec 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/OfficerTutorial.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/OfficerTutorial.cs @@ -373,7 +373,7 @@ namespace Barotrauma.Tutorials HighlightInventorySlot(officer.Inventory, "harpoongun", highlightColor, 0.5f, 0.5f, 0f); } yield return null; - } while (!harpoonGunChamber.Inventory.IsFull()); // Wait until all five harpons loaded + } while (!harpoonGunChamber.Inventory.IsFull()); // Wait until all six harpoons loaded RemoveCompletedObjective(segments[5]); SetHighlight(officer_rangedWeaponCabinet.Item, false); SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true); diff --git a/Barotrauma/BarotraumaClient/Source/GameSettings.cs b/Barotrauma/BarotraumaClient/Source/GameSettings.cs index e2515f27d..a7ecbd3c3 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSettings.cs @@ -113,8 +113,8 @@ namespace Barotrauma var tickBox = new GUITickBox(new RectTransform(tickBoxScale, contentPackageList.Content.RectTransform, scaleBasis: ScaleBasis.BothHeight), contentPackage.Name) { UserData = contentPackage, - OnSelected = SelectContentPackage, - Selected = SelectedContentPackages.Contains(contentPackage) + Selected = SelectedContentPackages.Contains(contentPackage), + OnSelected = SelectContentPackage }; if (contentPackage.CorePackage) { @@ -153,11 +153,6 @@ namespace Barotrauma UnsavedSettings = true; var msgBox = new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage")); - //change fonts to the default font of the new language to make sure - //they can be displayed when for example changing from English to Chinese - var defaultFont = GUI.Style.LoadCurrentDefaultFont(); - msgBox.Header.Font = defaultFont; - msgBox.Text.Font = defaultFont; return true; }; @@ -445,17 +440,11 @@ namespace Barotrauma }; voiceChatScrollBar.OnMoved(voiceChatScrollBar, voiceChatScrollBar.BarScroll); - var leftTickBoxes = new GUILayoutGroup(new RectTransform(new Vector2(0.28f, 0.06f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopLeft) - { RelativeOffset = new Vector2(0.02f, 0.32f) }) - { RelativeSpacing = 0.01f }; - var centerTickBoxes = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.06f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopLeft) - { RelativeOffset = new Vector2(0.3f, 0.32f) }) - { RelativeSpacing = 0.01f }; - var rightTickBoxes = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.06f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopRight) + var tickBoxes = new GUILayoutGroup(new RectTransform(new Vector2(0.28f, 0.15f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopLeft) { RelativeOffset = new Vector2(0.02f, 0.32f) }) { RelativeSpacing = 0.01f }; - GUITickBox muteOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale / 0.06f, leftTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("MuteOnFocusLost")); + GUITickBox muteOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, tickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("MuteOnFocusLost")); muteOnFocusLostBox.Selected = MuteOnFocusLost; muteOnFocusLostBox.ToolTip = TextManager.Get("MuteOnFocusLostToolTip"); muteOnFocusLostBox.OnSelected = (tickBox) => @@ -465,7 +454,7 @@ namespace Barotrauma return true; }; - GUITickBox dynamicRangeCompressionTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.06f, centerTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("DynamicRangeCompression")); + GUITickBox dynamicRangeCompressionTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, tickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("DynamicRangeCompression")); dynamicRangeCompressionTickBox.Selected = DynamicRangeCompressionEnabled; dynamicRangeCompressionTickBox.ToolTip = TextManager.Get("DynamicRangeCompressionToolTip"); dynamicRangeCompressionTickBox.OnSelected = (tickBox) => @@ -475,7 +464,7 @@ namespace Barotrauma return true; }; - GUITickBox voipAttenuationTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.06f, rightTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("VoipAttenuation")); + GUITickBox voipAttenuationTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, tickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), TextManager.Get("VoipAttenuation")); voipAttenuationTickBox.Selected = VoipAttenuationEnabled; voipAttenuationTickBox.ToolTip = TextManager.Get("VoipAttenuationToolTip"); voipAttenuationTickBox.OnSelected = (tickBox) => @@ -485,8 +474,8 @@ namespace Barotrauma return true; }; - var voipSettings = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.4f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopCenter) - { RelativeOffset = new Vector2(0.0f, 0.38f) }) + var voipSettings = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.35f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopCenter) + { RelativeOffset = new Vector2(0.0f, 0.47f) }) { RelativeSpacing = 0.01f }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), voipSettings.RectTransform), TextManager.Get("VoiceChat")); @@ -631,18 +620,21 @@ namespace Barotrauma var dbMeter = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.25f), voiceActivityGroup.RectTransform), 0.0f, Color.Lime); dbMeter.ProgressGetter = () => { - if (VoipCapture.Instance == null) return 0.0f; + if (VoipCapture.Instance == null) { return 0.0f; } dbMeter.Color = VoipCapture.Instance.LastdB > NoiseGateThreshold ? Color.Lime : Color.Orange; //TODO: i'm a filthy hack - return ((float)VoipCapture.Instance.LastdB + 100.0f) / 100.0f; + + float scrollVal = double.IsNegativeInfinity(VoipCapture.Instance.LastdB) ? 0.0f : ((float)VoipCapture.Instance.LastdB + 100.0f) / 100.0f; + return scrollVal * scrollVal; }; var noiseGateSlider = new GUIScrollBar(new RectTransform(Vector2.One, dbMeter.RectTransform, Anchor.Center), color: Color.White, barSize: 0.03f); noiseGateSlider.Frame.Visible = false; noiseGateSlider.Step = 0.01f; noiseGateSlider.Range = new Vector2(-100.0f, 0.0f); - noiseGateSlider.BarScrollValue = NoiseGateThreshold; + noiseGateSlider.BarScroll = MathUtils.InverseLerp(-1.0f, 0.0f, NoiseGateThreshold); + noiseGateSlider.BarScroll *= noiseGateSlider.BarScroll; noiseGateSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) => { - NoiseGateThreshold = scrollBar.BarScrollValue; + NoiseGateThreshold = MathHelper.Lerp(-100.0f, 0.0f, (float)Math.Sqrt(scrollBar.BarScroll)); UnsavedSettings = true; return true; }; @@ -1018,6 +1010,7 @@ namespace Barotrauma SelectedContentPackages.Remove(contentPackage); } } + if (contentPackage.GetFilesOfType(ContentType.Submarine).Any()) { Submarine.RefreshSavedSubs(); } UnsavedSettings = true; return true; } diff --git a/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs index 37d8138eb..800766b64 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs @@ -126,6 +126,7 @@ namespace Barotrauma public override void RemoveItem(Item item) { + if (!Items.Contains(item)) { return; } base.RemoveItem(item); CreateSlots(); } @@ -175,7 +176,12 @@ namespace Barotrauma AssignQuickUseNumKeys(); - highlightedSubInventorySlots.Clear(); + highlightedSubInventorySlots.RemoveWhere(s => s.Inventory.OpenState <= 0.0f); + foreach (var subSlot in highlightedSubInventorySlots) + { + subSlot.Slot = slots[subSlot.SlotIndex]; + } + //highlightedSubInventorySlots.Clear(); screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); CalculateBackgroundFrame(); @@ -469,6 +475,7 @@ namespace Barotrauma } List hideSubInventories = new List(); + highlightedSubInventorySlots.RemoveWhere(s => s.SlotIndex < 0 || s.SlotIndex >= Items.Length || Items[s.SlotIndex] == null); foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots) { if (highlightedSubInventorySlot.ParentInventory == this) @@ -646,7 +653,10 @@ namespace Barotrauma } } - slotRef.Inventory.OpenState = isEquippedSubInventory ? 1f : 0f; // Reset animation when initially equipped + if (isEquippedSubInventory) + { + slotRef.Inventory.OpenState = 1.0f; // Reset animation when initially equipped + } highlightedSubInventorySlots.Add(slotRef); slotRef.Inventory.HideTimer = 1f; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemContainer.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemContainer.cs index aaa67e887..afcfb1212 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemContainer.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemContainer.cs @@ -146,14 +146,24 @@ namespace Barotrauma.Items.Components if (item.body == null) { - transformedItemPos = new Vector2(item.Rect.X, item.Rect.Y); - if (item.Submarine != null) transformedItemPos += item.Submarine.DrawPosition; - transformedItemPos = transformedItemPos + ItemPos * item.Scale; + if (item.FlippedX) + { + transformedItemPos.X = -transformedItemPos.X; + transformedItemPos.X += item.Rect.Width; + transformedItemInterval.X = -transformedItemInterval.X; + } + if (item.FlippedY) + { + transformedItemPos.Y = -transformedItemPos.Y; + transformedItemPos.Y -= item.Rect.Height; + transformedItemInterval.Y = -transformedItemInterval.Y; + } + transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y); + if (item.Submarine != null) { transformedItemPos += item.Submarine.DrawPosition; } } else { Matrix transform = Matrix.CreateRotationZ(item.body.Rotation); - if (item.body.Dir == -1.0f) { transformedItemPos.X = -transformedItemPos.X; @@ -169,6 +179,10 @@ namespace Barotrauma.Items.Components Vector2 currentItemPos = transformedItemPos; + SpriteEffects spriteEffects = SpriteEffects.None; + if ((item.body != null && item.body.Dir == -1) || item.FlippedX) { spriteEffects |= SpriteEffects.FlipHorizontally; } + if (item.FlippedY) { spriteEffects |= SpriteEffects.FlipVertically; } + int i = 0; foreach (Item containedItem in Inventory.Items) { @@ -192,7 +206,7 @@ namespace Barotrauma.Items.Components containedItem.GetSpriteColor(), -currentRotation, containedItem.Scale, - (item.body != null && item.body.Dir == -1) ? SpriteEffects.FlipHorizontally : SpriteEffects.None, + spriteEffects, depth: ContainedSpriteDepth < 0.0f ? containedItem.Sprite.Depth : ContainedSpriteDepth); foreach (ItemContainer ic in containedItem.GetComponents()) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Controller.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Controller.cs index 61b1a9619..e4547a6c2 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Controller.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Controller.cs @@ -1,4 +1,5 @@ -using Microsoft.Xna.Framework.Graphics; +using Barotrauma.Networking; +using Microsoft.Xna.Framework.Graphics; namespace Barotrauma.Items.Components { @@ -72,5 +73,10 @@ namespace Barotrauma.Items.Components GameMain.Client.ChatBox.ToggleOpen = value; } } + + public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) + { + state = msg.ReadBoolean(); + } } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Engine.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Engine.cs index 07974570d..81c00d2ab 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Engine.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Engine.cs @@ -136,7 +136,7 @@ namespace Barotrauma.Items.Components public void ClientWrite(IWriteMessage msg, object[] extraData = null) { //targetForce can only be adjusted at 10% intervals -> no need for more accuracy than this - msg.WriteRangedIntegerDeprecated(-10, 10, (int)(targetForce / 10.0f)); + msg.WriteRangedInteger((int)(targetForce / 10.0f), -10, 10); } public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs index ef5f05314..bd7bb363a 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs @@ -44,6 +44,16 @@ namespace Barotrauma.Items.Components Stretch = true, RelativeSpacing = 0.02f }; + + itemList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), paddedFrame.RectTransform)) + { + OnSelected = (GUIComponent component, object userdata) => + { + selectedItem = userdata as FabricationRecipe; + if (selectedItem != null) { SelectItem(Character.Controlled, selectedItem); } + return true; + } + }; var filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.06f), paddedFrame.RectTransform), isHorizontal: true) { @@ -58,16 +68,6 @@ namespace Barotrauma.Items.Components OnClicked = (btn, userdata) => { ClearFilter(); itemFilterBox.Flash(Color.White); return true; } }; - itemList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), paddedFrame.RectTransform)) - { - OnSelected = (GUIComponent component, object userdata) => - { - selectedItem = userdata as FabricationRecipe; - if (selectedItem != null) { SelectItem(Character.Controlled, selectedItem); } - return true; - } - }; - inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.15f), paddedFrame.RectTransform), style: null); inputInventoryOverlay = new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawInputOverLay, null) { @@ -462,7 +462,7 @@ namespace Barotrauma.Items.Components public void ClientWrite(IWriteMessage msg, object[] extraData = null) { int itemIndex = pendingFabricatedItem == null ? -1 : fabricationRecipes.IndexOf(pendingFabricatedItem); - msg.WriteRangedIntegerDeprecated(-1, fabricationRecipes.Count - 1, itemIndex); + msg.WriteRangedInteger(itemIndex, -1, fabricationRecipes.Count - 1); } public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Pump.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Pump.cs index 9ab572571..59be09f8d 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Pump.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Pump.cs @@ -179,7 +179,7 @@ namespace Barotrauma.Items.Components public void ClientWrite(IWriteMessage msg, object[] extraData = null) { //flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this - msg.WriteRangedIntegerDeprecated(-10, 10, (int)(flowPercentage / 10.0f)); + msg.WriteRangedInteger((int)(flowPercentage / 10.0f), -10, 10); msg.Write(IsActive); } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs index d45da755e..146c3cc52 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs @@ -451,7 +451,10 @@ namespace Barotrauma.Items.Components if (PlayerInput.KeyDown(InputType.Right)) input.X += 1.0f; if (PlayerInput.KeyDown(InputType.Up)) input.Y += 1.0f; if (PlayerInput.KeyDown(InputType.Down)) input.Y += -1.0f; - if (PlayerInput.KeyDown(InputType.Run)) rate = 200.0f; + if (PlayerInput.KeyDown(InputType.Run)) + rate = 200.0f; + else if (PlayerInput.KeyDown(InputType.Crouch)) + rate = 20.0f; rate *= deltaTime; input.X *= rate; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs index b569893e4..f783f5b3c 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs @@ -73,11 +73,18 @@ namespace Barotrauma.Items.Components set { maintainPosTickBox.Selected = value; } } + private bool dockingModeEnabled; public bool DockingModeEnabled + { + get { return UseAutoDocking && dockingModeEnabled; } + set { dockingModeEnabled = value; } + } + + public bool UseAutoDocking { get; set; - } + } = true; public List DockingSources = new List(); public DockingPort ActiveDockingSource, DockingTarget; @@ -726,6 +733,8 @@ namespace Barotrauma.Items.Components inputCumulation = 0; keyboardInput = Vector2.Zero; } + + if (!UseAutoDocking) { return; } float closestDist = DockingAssistThreshold * DockingAssistThreshold; DockingModeEnabled = false; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Power/PowerContainer.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Power/PowerContainer.cs index 9da57eddc..143bbe182 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Power/PowerContainer.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Power/PowerContainer.cs @@ -129,7 +129,7 @@ namespace Barotrauma.Items.Components public void ClientWrite(IWriteMessage msg, object[] extraData) { - msg.WriteRangedIntegerDeprecated(0, 10, (int)(rechargeSpeed / MaxRechargeSpeed * 10)); + msg.WriteRangedInteger((int)(rechargeSpeed / MaxRechargeSpeed * 10), 0, 10); } public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) @@ -140,7 +140,11 @@ namespace Barotrauma.Items.Components return; } - RechargeSpeed = msg.ReadRangedInteger(0, 10) / 10.0f * maxRechargeSpeed; + float rechargeRate = msg.ReadRangedInteger(0, 10) / 10.0f; + RechargeSpeed = rechargeRate * MaxRechargeSpeed; +#if CLIENT + rechargeSpeedSlider.BarScroll = rechargeRate; +#endif Charge = msg.ReadRangedSingle(0.0f, 1.0f, 8) * capacity; } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/RepairTool.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/RepairTool.cs index 4f609260e..842ec0e92 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/RepairTool.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/RepairTool.cs @@ -65,13 +65,13 @@ namespace Barotrauma.Items.Components } - partial void UseProjSpecific(float deltaTime) + partial void UseProjSpecific(float deltaTime, Vector2 raystart) { if (ParticleEmitter != null) { float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi); ParticleEmitter.Emit( - deltaTime, item.WorldPosition + TransformedBarrelPos, + deltaTime, ConvertUnits.ToDisplayUnits(raystart), item.CurrentHull, particleAngle, ParticleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0); } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs index ff2511560..d47986d28 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs @@ -1,5 +1,6 @@ using Barotrauma.Networking; using Barotrauma.Particles; +using Barotrauma.Sounds; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections.Generic; @@ -25,6 +26,8 @@ namespace Barotrauma.Items.Components //the corresponding particle emitter is active when the condition is within this range private List particleEmitterConditionRanges = new List(); + private SoundChannel repairSoundChannel; + private string repairButtonText, repairingText; private string sabotageButtonText, sabotagingText; @@ -140,6 +143,19 @@ namespace Barotrauma.Items.Components particleEmitters[i].Emit(deltaTime, item.WorldPosition, item.CurrentHull); } } + + if (CurrentFixer != null && CurrentFixer.SelectedConstruction == item) + { + if (repairSoundChannel == null || !repairSoundChannel.IsPlaying) + { + repairSoundChannel = SoundPlayer.PlaySound("repair", item.WorldPosition, hullGuess: item.CurrentHull); + } + } + else + { + repairSoundChannel?.FadeOutAndDispose(); + repairSoundChannel = null; + } } public override void DrawHUD(SpriteBatch spriteBatch, Character character) @@ -177,20 +193,6 @@ namespace Barotrauma.Items.Components } } - public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) - { - deteriorationTimer = msg.ReadSingle(); - deteriorateAlwaysResetTimer = msg.ReadSingle(); - DeteriorateAlways = msg.ReadBoolean(); - CurrentFixer = msg.ReadBoolean() ? Character.Controlled : null; - currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2); - } - - public void ClientWrite(IWriteMessage msg, object[] extraData = null) - { - msg.WriteRangedInteger((int)requestStartFixAction, 0, 2); - } - public void Draw(SpriteBatch spriteBatch, bool editing) { if (GameMain.DebugDraw && Character.Controlled?.FocusedItem == item) @@ -213,5 +215,25 @@ namespace Barotrauma.Items.Components Color.Orange); } } + + protected override void RemoveComponentSpecific() + { + repairSoundChannel?.FadeOutAndDispose(); + repairSoundChannel = null; + } + + public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) + { + deteriorationTimer = msg.ReadSingle(); + deteriorateAlwaysResetTimer = msg.ReadSingle(); + DeteriorateAlways = msg.ReadBoolean(); + CurrentFixer = msg.ReadBoolean() ? Character.Controlled : null; + currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2); + } + + public void ClientWrite(IWriteMessage msg, object[] extraData = null) + { + msg.WriteRangedInteger((int)requestStartFixAction, 0, 2); + } } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/ConnectionPanel.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/ConnectionPanel.cs index 9425e5a48..d66e716d3 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/ConnectionPanel.cs @@ -1,4 +1,5 @@ using Barotrauma.Networking; +using Barotrauma.Sounds; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; @@ -12,6 +13,8 @@ namespace Barotrauma.Items.Components { public static Wire HighlightedWire; + private SoundChannel rewireSoundChannel; + partial void InitProjSpecific(XElement element) { if (GuiFrame == null) return; @@ -20,7 +23,37 @@ namespace Barotrauma.Items.Components UserData = this }; } - + + partial void UpdateProjSpecific(float deltaTime) + { + foreach (Wire wire in DisconnectedWires) + { + if (Rand.Range(0.0f, 500.0f) < 1.0f) + { + SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull); + Vector2 baseVel = new Vector2(0.0f, -100.0f); + for (int i = 0; i < 5; i++) + { + var particle = GameMain.ParticleManager.CreateParticle("spark", item.WorldPosition, + baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull); + if (particle != null) { particle.Size *= Rand.Range(0.5f, 1.0f); } + } + } + } + if (user != null && user.SelectedConstruction == item && HasRequiredItems(user, addMessage: false)) + { + if (rewireSoundChannel == null || !rewireSoundChannel.IsPlaying) + { + rewireSoundChannel = SoundPlayer.PlaySound("rewire", item.WorldPosition, hullGuess: item.CurrentHull); + } + } + else + { + rewireSoundChannel?.FadeOutAndDispose(); + rewireSoundChannel = null; + } + } + public override void Move(Vector2 amount) { if (item.Submarine == null || item.Submarine.Loading || Screen.Selected != GameMain.SubEditorScreen) return; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs b/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs index 12070f929..f12656960 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs @@ -155,8 +155,8 @@ namespace Barotrauma public class SlotReference { public readonly Inventory ParentInventory; - public readonly InventorySlot Slot; public readonly int SlotIndex; + public InventorySlot Slot; public Inventory Inventory; @@ -741,17 +741,37 @@ namespace Barotrauma { Inventory selectedInventory = selectedSlot.ParentInventory; int slotIndex = selectedSlot.SlotIndex; - if (selectedInventory.TryPutItem(draggingItem, slotIndex, true, true, Character.Controlled)) + + //if attempting to drop into an invalid slot in the same inventory, try to move to the correct slot + if (selectedInventory.Items[slotIndex] == null && + selectedInventory == Character.Controlled.Inventory && + !draggingItem.AllowedSlots.Any(a => a.HasFlag(Character.Controlled.Inventory.SlotTypes[slotIndex])) && + selectedInventory.TryPutItem(draggingItem, Character.Controlled, draggingItem.AllowedSlots)) { - if (selectedInventory.slots != null) selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f); + if (selectedInventory.slots != null) + { + for (int i = 0; i < selectedInventory.slots.Length; i++) + { + if (selectedInventory.Items[i] == draggingItem) + { + selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f); + } + } + selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.Red, 0.1f, 0.9f); + } + GUI.PlayUISound(GUISoundType.PickItem); + } + else if (selectedInventory.TryPutItem(draggingItem, slotIndex, true, true, Character.Controlled)) + { + if (selectedInventory.slots != null) { selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f); } GUI.PlayUISound(GUISoundType.PickItem); } else { - if (selectedInventory.slots != null) selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.Red, 0.1f, 0.9f); + if (selectedInventory.slots != null){ selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.Red, 0.1f, 0.9f); } GUI.PlayUISound(GUISoundType.PickItemFail); } - selectedInventory.HideTimer = 1.0f; + selectedInventory.HideTimer = 2.0f; if (selectedSlot.ParentInventory?.Owner is Item parentItem && parentItem.ParentInventory != null) { for (int i = 0; i < parentItem.ParentInventory.capacity; i++) @@ -960,7 +980,7 @@ namespace Barotrauma { GUI.DrawRectangle(spriteBatch, new Rectangle(rect.X, rect.Bottom - 8, rect.Width, 8), Color.Black * 0.8f, true); GUI.DrawRectangle(spriteBatch, - new Rectangle(rect.X, rect.Bottom - 8, (int)(rect.Width * item.Condition / item.MaxCondition), 8), + new Rectangle(rect.X, rect.Bottom - 8, (int)(rect.Width * (item.Condition / item.MaxCondition)), 8), Color.Lerp(Color.Red, Color.Green, item.Condition / item.MaxCondition) * 0.8f, true); } @@ -1080,9 +1100,9 @@ namespace Barotrauma public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { - receivedItemIDs = new ushort[capacity]; - - for (int i = 0; i < capacity; i++) + byte itemCount = msg.ReadByte(); + receivedItemIDs = new ushort[itemCount]; + for (int i = 0; i < itemCount; i++) { receivedItemIDs[i] = msg.ReadUInt16(); } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Item.cs b/Barotrauma/BarotraumaClient/Source/Items/Item.cs index ac592b1a5..2b4cceb29 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Item.cs @@ -930,17 +930,17 @@ namespace Barotrauma } NetEntityEvent.Type eventType = (NetEntityEvent.Type)extraData[0]; - msg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)eventType); + msg.WriteRangedInteger((int)eventType, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1); switch (eventType) { case NetEntityEvent.Type.ComponentState: int componentIndex = (int)extraData[1]; - msg.WriteRangedIntegerDeprecated(0, components.Count - 1, componentIndex); + msg.WriteRangedInteger(componentIndex, 0, components.Count - 1); (components[componentIndex] as IClientSerializable).ClientWrite(msg, extraData); break; case NetEntityEvent.Type.InventoryState: int containerIndex = (int)extraData[1]; - msg.WriteRangedIntegerDeprecated(0, components.Count - 1, containerIndex); + msg.WriteRangedInteger(containerIndex, 0, components.Count - 1); (components[containerIndex] as ItemContainer).Inventory.ClientWrite(msg, extraData); break; case NetEntityEvent.Type.Treatment: @@ -1114,7 +1114,8 @@ namespace Barotrauma MapEntityPrefab.Find(itemName, itemIdentifier, showErrorMessages: false) as ItemPrefab; if (itemPrefab == null) { - string errorMsg = "Failed to spawn item (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null"); + string errorMsg = "Failed to spawn item, prefab not found (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null") + ")"; + errorMsg += "\n" + string.Join(", ", GameMain.Config.SelectedContentPackages.Select(cp => cp.Name)); GameAnalyticsManager.AddErrorEventOnce("Item.ReadSpawnData:PrefabNotFound" + (itemName ?? "null") + (itemIdentifier ?? "null"), GameAnalyticsSDK.Net.EGAErrorSeverity.Critical, errorMsg); diff --git a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs index c3c89572e..77e7596a0 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs @@ -534,7 +534,7 @@ namespace Barotrauma msg.Write(FireSources.Count > 0); if (FireSources.Count > 0) { - msg.WriteRangedIntegerDeprecated(0, 16, Math.Min(FireSources.Count, 16)); + msg.WriteRangedInteger(Math.Min(FireSources.Count, 16), 0, 16); for (int i = 0; i < Math.Min(FireSources.Count, 16); i++) { var fireSource = FireSources[i]; diff --git a/Barotrauma/BarotraumaClient/Source/Map/Submarine.cs b/Barotrauma/BarotraumaClient/Source/Map/Submarine.cs index f5f226950..80f6ee878 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Submarine.cs @@ -45,7 +45,9 @@ namespace Barotrauma if (string.IsNullOrEmpty(filename)) { - DebugConsole.ThrowError("Error when loading round sound (" + element + ") - file path not set"); + string errorMsg = "Error when loading round sound (" + element + ") - file path not set"; + DebugConsole.ThrowError(errorMsg); + GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FilePathEmpty" + element.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace); return null; } @@ -69,7 +71,9 @@ namespace Barotrauma } catch (FileNotFoundException e) { - DebugConsole.ThrowError("Failed to load sound file \"" + filename + "\".", e); + string errorMsg = "Failed to load sound file \"" + filename + "\"."; + DebugConsole.ThrowError(errorMsg, e); + GameAnalyticsManager.AddErrorEventOnce("Submarine.LoadRoundSound:FileNotFound" + filename, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace); return null; } } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/BanList.cs b/Barotrauma/BarotraumaClient/Source/Networking/BanList.cs index 179732bbd..54e2d9418 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/BanList.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/BanList.cs @@ -84,14 +84,12 @@ namespace Barotrauma.Networking font: GUI.SmallFont); var reasonText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 0.0f), paddedPlayerFrame.RectTransform), - TextManager.Get("BanReason") + + TextManager.Get("BanReason") + " " + (string.IsNullOrEmpty(bannedPlayer.Reason) ? TextManager.Get("None") : ToolBox.LimitString(bannedPlayer.Reason, GUI.SmallFont, 170)), font: GUI.SmallFont, wrap: true) { ToolTip = bannedPlayer.Reason }; - - } return banFrame; @@ -100,34 +98,29 @@ namespace Barotrauma.Networking private bool RemoveBan(GUIButton button, object obj) { BannedPlayer banned = obj as BannedPlayer; - if (banned == null) return false; + if (banned == null) { return false; } localRemovedBans.Add(banned.UniqueIdentifier); - RecreateBanFrame(); + GameMain.Client?.ServerSettings?.ClientAdminWrite(ServerSettings.NetFlags.Properties); + return true; } private bool RangeBan(GUIButton button, object obj) { BannedPlayer banned = obj as BannedPlayer; - if (banned == null) return false; + if (banned == null) { return false; } localRangeBans.Add(banned.UniqueIdentifier); - RecreateBanFrame(); - return true; - } - - private bool CloseFrame(GUIButton button, object obj) - { - banFrame = null; + GameMain.Client?.ServerSettings?.ClientAdminWrite(ServerSettings.NetFlags.Properties); return true; } - + public void ClientAdminRead(IReadMessage incMsg) { bool hasPermission = incMsg.ReadBoolean(); diff --git a/Barotrauma/BarotraumaClient/Source/Networking/FileTransfer/FileReceiver.cs b/Barotrauma/BarotraumaClient/Source/Networking/FileTransfer/FileReceiver.cs index 5126e3de6..662795f19 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/FileTransfer/FileReceiver.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/FileTransfer/FileReceiver.cs @@ -460,13 +460,13 @@ namespace Barotrauma.Networking } catch (Exception e) { - ErrorMessage = "Loading received submarine ''" + fileTransfer.FileName + "'' failed! {" + e.Message + "}"; + ErrorMessage = "Loading received submarine \"" + fileTransfer.FileName + "\" failed! {" + e.Message + "}"; return false; } if (stream == null) { - ErrorMessage = "Decompressing received submarine file''" + fileTransfer.FilePath + "'' failed!"; + ErrorMessage = "Decompressing received submarine file \"" + fileTransfer.FilePath + "\" failed!"; return false; } @@ -490,7 +490,7 @@ namespace Barotrauma.Networking stream.Close(); stream.Dispose(); - ErrorMessage = "Parsing file ''" + fileTransfer.FilePath + "'' failed! The file may not be a valid submarine file."; + ErrorMessage = "Parsing file \"" + fileTransfer.FilePath + "\" failed! The file may not be a valid submarine file."; return false; } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs index 1de69fbd4..7dca12e5e 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs @@ -396,15 +396,13 @@ namespace Barotrauma.Networking private bool connectCancelled; private void CancelConnect() { - if (!(GameMain.ServerChildProcess?.HasExited??true)) + if (!(GameMain.ServerChildProcess?.HasExited ?? true)) { GameMain.ServerChildProcess.Kill(); GameMain.ServerChildProcess = null; } - connectCancelled = true; - clientPeer?.Close(); - clientPeer = null; + Disconnect(); } // Before main looping starts, we loop here and wait for approval message @@ -554,14 +552,20 @@ namespace Barotrauma.Networking UpdateHUD(deltaTime); base.Update(deltaTime); - + try { clientPeer?.Update(deltaTime); } catch (Exception e) { - string errorMsg = "Error while reading a message from server. {" + e + "}\n" + e.StackTrace; + string errorMsg = "Error while reading a message from server. {" + e + "}. "; + if (GameMain.Client == null) { errorMsg += "Client disposed."; } + errorMsg += "\n" + e.StackTrace; + if (e.InnerException != null) + { + errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace; + } GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); DebugConsole.ThrowError("Error while reading a message from server.", e); new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", new string[2] { "[message]", "[targetsite]" }, new string[2] { e.Message, e.TargetSite.ToString() })); @@ -603,7 +607,7 @@ namespace Barotrauma.Networking if (IsServerOwner && connected && !connectCancelled) { - if (GameMain.ServerChildProcess?.HasExited??true) + if (GameMain.ServerChildProcess?.HasExited ?? true) { Disconnect(); var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), TextManager.Get("ServerProcessClosed")); @@ -626,9 +630,34 @@ namespace Barotrauma.Networking ReadLobbyUpdate(inc); break; case ServerPacketHeader.UPDATE_INGAME: - ReadIngameUpdate(inc); + try + { + ReadIngameUpdate(inc); + } + catch (Exception e) + { + string errorMsg = "Error while reading an ingame update message from server. {" + e + "}\n" + e.StackTrace; + if (e.InnerException != null) + { + errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace; + } + GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadDataMessage:ReadIngameUpdate", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + throw; + } break; case ServerPacketHeader.VOICE: + if (VoipClient == null) + { + string errorMsg = "Failed to read a voice packet from the server (VoipClient == null). "; + if (GameMain.Client == null) { errorMsg += "Client disposed. "; } + errorMsg += "\n" + Environment.StackTrace; + GameAnalyticsManager.AddErrorEventOnce( + "GameClient.ReadDataMessage:VoipClientNull", + GameMain.Client == null ? GameAnalyticsSDK.Net.EGAErrorSeverity.Error : GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, + errorMsg); + return; + } + VoipClient.Read(inc); break; case ServerPacketHeader.QUERY_STARTGAME: @@ -728,7 +757,7 @@ namespace Barotrauma.Networking string[] splitMsg = disconnectMsg.Split('/'); DisconnectReason disconnectReason = DisconnectReason.Unknown; - if (splitMsg.Length > 0) Enum.TryParse(splitMsg[0], out disconnectReason); + if (splitMsg.Length > 0) { Enum.TryParse(splitMsg[0], out disconnectReason); } if (disconnectMsg == Lidgren.Network.NetConnection.NoResponseMessage) { @@ -737,6 +766,19 @@ namespace Barotrauma.Networking DebugConsole.NewMessage("Received a disconnect message (" + disconnectMsg + ")"); + if (disconnectReason != DisconnectReason.Banned && + disconnectReason != DisconnectReason.ServerShutdown && + disconnectReason != DisconnectReason.TooManyFailedLogins && + disconnectReason != DisconnectReason.NotOnWhitelist && + disconnectReason != DisconnectReason.MissingContentPackage && + disconnectReason != DisconnectReason.InvalidVersion) + { + GameAnalyticsManager.AddErrorEventOnce( + "GameClient.HandleDisconnectMessage", + GameAnalyticsSDK.Net.EGAErrorSeverity.Debug, + "Client received a disconnect message. Reason: " + disconnectReason.ToString() + ", message: " + disconnectMsg); + } + if (disconnectReason == DisconnectReason.ServerFull) { CoroutineManager.StopCoroutines("WaitForStartingInfo"); @@ -1059,6 +1101,28 @@ namespace Barotrauma.Networking if (campaign == null) { + //this shouldn't happen, TrySelectSub should stop the coroutine if the correct sub/shuttle cannot be found + if (GameMain.NetLobbyScreen.SelectedSub == null || + GameMain.NetLobbyScreen.SelectedSub.Name != subName || + GameMain.NetLobbyScreen.SelectedSub.MD5Hash?.Hash != subHash) + { + string errorMsg = "Failed to select submarine \"" + subName + "\" (hash: " + subHash + ")."; + DebugConsole.ThrowError(errorMsg); + GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectSub" + subName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + CoroutineManager.StartCoroutine(EndGame("")); + yield return CoroutineStatus.Failure; + } + if (GameMain.NetLobbyScreen.SelectedShuttle == null || + GameMain.NetLobbyScreen.SelectedShuttle.Name != shuttleName || + GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash?.Hash != shuttleHash) + { + string errorMsg = "Failed to select shuttle \"" + shuttleName + "\" (hash: " + shuttleHash + ")."; + DebugConsole.ThrowError(errorMsg); + GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:FailedToSelectShuttle" + shuttleName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + CoroutineManager.StartCoroutine(EndGame("")); + yield return CoroutineStatus.Failure; + } + GameMain.GameSession = missionIndex < 0 ? new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, MissionType.None) : new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, MissionPrefab.List[missionIndex]); @@ -1252,7 +1316,8 @@ namespace Barotrauma.Networking { existingClient.SetPermissions(permissions, permittedConsoleCommands); name = tc.Name; - if (GameMain.NetLobbyScreen.CharacterNameBox != null) + if (GameMain.NetLobbyScreen.CharacterNameBox != null && + !GameMain.NetLobbyScreen.CharacterNameBox.Selected) { GameMain.NetLobbyScreen.CharacterNameBox.Text = name; } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/KarmaManager.cs b/Barotrauma/BarotraumaClient/Source/Networking/KarmaManager.cs index 227da1038..c184db56c 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/KarmaManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/KarmaManager.cs @@ -9,6 +9,11 @@ namespace Barotrauma { public void CreateSettingsFrame(GUIComponent parent) { + if (TextManager.ContainsTag("Karma.ResetKarmaBetweenRounds")) + { + CreateLabeledTickBox(parent, "ResetKarmaBetweenRounds"); + } + CreateLabeledSlider(parent, 0.0f, 40.0f, 1.0f, "KickBanThreshold"); if (TextManager.ContainsTag("Karma.KicksBeforeBan")) { @@ -102,5 +107,14 @@ namespace Barotrauma }; GameMain.NetworkMember.ServerSettings.AssignGUIComponent(propertyName, numInput); } + + private void CreateLabeledTickBox(GUIComponent parent, string propertyName) + { + var tickBox = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.1f), parent.RectTransform), TextManager.Get("Karma." + propertyName)) + { + ToolTip = TextManager.Get("Karma." + propertyName + "ToolTip", returnNull: true) ?? "" + }; + GameMain.NetworkMember.ServerSettings.AssignGUIComponent(propertyName, tickBox); + } } } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs b/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs index 466258b7f..92ddf7a01 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs @@ -96,7 +96,7 @@ namespace Barotrauma.Networking //find the first event that hasn't been sent in roundtriptime or at all eventLastSent.TryGetValue(events[i].ID, out float lastSent); - if (lastSent > Lidgren.Network.NetTime.Now - 50) //TODO: reimplement serverConnection.AverageRoundtripTime + if (lastSent > Lidgren.Network.NetTime.Now - 0.2) //TODO: reimplement serverConnection.AverageRoundtripTime { continue; } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/LidgrenClientPeer.cs b/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/LidgrenClientPeer.cs index 2bbdb9d7a..508499295 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/LidgrenClientPeer.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/LidgrenClientPeer.cs @@ -176,7 +176,11 @@ namespace Barotrauma.Networking outMsg.Write(contentPackage.MD5hash.Hash); } - netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered); + NetSendResult result = netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered); + if (result != NetSendResult.Queued && result != NetSendResult.Sent) + { + DebugConsole.NewMessage("Failed to send "+initializationStep.ToString()+" message to host: " + result); + } break; case ConnectionInitialization.Password: if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; } @@ -207,7 +211,11 @@ namespace Barotrauma.Networking byte[] saltedPw = ServerSettings.SaltPassword(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password)), passwordSalt); outMsg.Write((byte)saltedPw.Length); outMsg.Write(saltedPw, 0, saltedPw.Length); - netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered); + NetSendResult result = netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered); + if (result != NetSendResult.Queued && result != NetSendResult.Sent) + { + DebugConsole.NewMessage("Failed to send " + initializationStep.ToString() + " message to host: " + result); + } } public override void Close(string msg = null) @@ -247,7 +255,11 @@ namespace Barotrauma.Networking lidgrenMsg.Write((UInt16)length); lidgrenMsg.Write(msgData, 0, length); - netClient.SendMessage(lidgrenMsg, lidgrenDeliveryMethod); + NetSendResult result = netClient.SendMessage(lidgrenMsg, lidgrenDeliveryMethod); + if (result != NetSendResult.Queued && result != NetSendResult.Sent) + { + DebugConsole.NewMessage("Failed to send message to host: " + result); + } } } } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/SteamP2PClientPeer.cs b/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/SteamP2PClientPeer.cs index 223cf50b8..aa913f20a 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/SteamP2PClientPeer.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/SteamP2PClientPeer.cs @@ -66,7 +66,7 @@ namespace Barotrauma.Networking initializationStep = ConnectionInitialization.SteamTicketAndVersion; - timeout = 20.0; + timeout = NetworkConnection.TimeoutThreshold; heartbeatTimer = 1.0; isActive = true; @@ -83,7 +83,7 @@ namespace Barotrauma.Networking if (!isActive) { return; } if (steamId != hostSteamId) { return; } - timeout = 20.0; + timeout = NetworkConnection.TimeoutThreshold; byte incByte = data[0]; bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0; diff --git a/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/SteamP2POwnerPeer.cs b/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/SteamP2POwnerPeer.cs index 067920920..bb306eac3 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/SteamP2POwnerPeer.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/Primitives/Peers/SteamP2POwnerPeer.cs @@ -87,7 +87,7 @@ namespace Barotrauma.Networking private void OnAuthChange(ulong steamID, ulong ownerID, ClientAuthStatus status) { RemotePeer remotePeer = remotePeers.Find(p => p.SteamID == steamID); - DebugConsole.NewMessage(steamID + " validation: " + status + ", " + (remotePeer != null)); + DebugConsole.Log(steamID + " validation: " + status + ", " + (remotePeer != null)); if (remotePeer == null) { return; } @@ -106,7 +106,11 @@ namespace Barotrauma.Networking remotePeer.Authenticating = false; foreach (var msg in remotePeer.UnauthedMessages) { - netClient.SendMessage(msg.Second, msg.First); + NetSendResult result = netClient.SendMessage(msg.Second, msg.First); + if (result != NetSendResult.Queued && result != NetSendResult.Sent) + { + DebugConsole.NewMessage("Failed to send unauthed message to host: " + result); + } } remotePeer.UnauthedMessages.Clear(); } @@ -200,7 +204,11 @@ namespace Barotrauma.Networking } else { - netClient.SendMessage(outMsg, lidgrenDeliveryMethod); + NetSendResult result = netClient.SendMessage(outMsg, lidgrenDeliveryMethod); + if (result != NetSendResult.Queued && result != NetSendResult.Sent) + { + DebugConsole.NewMessage("Failed to send message from "+SteamManager.SteamIDUInt64ToString(remotePeer.SteamID)+" to host: " + result); + } } } @@ -344,7 +352,11 @@ namespace Barotrauma.Networking outMsg.Write(selfSteamID); outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep)); outMsg.Write(Name); - netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered); + NetSendResult result = netClient.SendMessage(outMsg, NetDeliveryMethod.ReliableUnordered); + if (result != NetSendResult.Queued && result != NetSendResult.Sent) + { + DebugConsole.NewMessage("Failed to send initialization message to host: " + result); + } return; } @@ -466,7 +478,11 @@ namespace Barotrauma.Networking lidgrenMsg.Write((UInt16)length); lidgrenMsg.Write(msgData, 0, length); - netClient.SendMessage(lidgrenMsg, lidgrenDeliveryMethod); + NetSendResult result = netClient.SendMessage(lidgrenMsg, lidgrenDeliveryMethod); + if (result != NetSendResult.Queued && result != NetSendResult.Sent) + { + DebugConsole.NewMessage("Failed to send own message to host: " + result); + } } } } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs index 128dfaaef..c9a7d687b 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs @@ -218,9 +218,9 @@ namespace Barotrauma.Steam instance.client.Lobby.CurrentLobbyData.SetData("allowrespawn", serverSettings.AllowRespawn.ToString()); instance.client.Lobby.CurrentLobbyData.SetData("traitors", serverSettings.TraitorsEnabled.ToString()); instance.client.Lobby.CurrentLobbyData.SetData("gamestarted", GameMain.Client.GameStarted.ToString()); - instance.client.Lobby.CurrentLobbyData.SetData("gamemode", serverSettings.GameModeIdentifier); + instance.client.Lobby.CurrentLobbyData.SetData("gamemode", GameMain.NetLobbyScreen?.SelectedMode?.Identifier??""); - DebugConsole.NewMessage("Lobby updated!", Microsoft.Xna.Framework.Color.Lime); + DebugConsole.Log("Lobby updated!"); } public static void LeaveLobby() @@ -770,6 +770,14 @@ namespace Barotrauma.Steam ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem.Directory.FullName, MetadataFileName)); string installedContentPackagePath = Path.GetFullPath(GetWorkshopItemContentPackagePath(tempContentPackage)); contentPackage = ContentPackage.List.Find(cp => Path.GetFullPath(cp.Path) == installedContentPackagePath); + + if (contentPackage == null && tempContentPackage.GameVersion <= new Version(0, 9, 1, 0)) + { + //try finding the content package in the lega + installedContentPackagePath = Path.GetFullPath(GetWorkshopItemContentPackagePath(tempContentPackage, legacy: false)); + contentPackage = ContentPackage.List.Find(cp => Path.GetFullPath(cp.Path) == installedContentPackagePath); + } + if (tempContentPackage.GameVersion > new Version(0, 9, 1, 0)) { itemEditor.Folder = Path.GetDirectoryName(installedContentPackagePath); @@ -784,6 +792,7 @@ namespace Barotrauma.Steam contentPackage.Path = newPath; itemEditor.Folder = newDir; if (!Directory.Exists(newDir)) { Directory.CreateDirectory(newDir); } + if (File.Exists(newPath)) { File.Delete(newPath); } File.Move(installedContentPackagePath, newPath); //move all files inside the Mods folder foreach (ContentFile cf in contentPackage.Files) @@ -793,10 +802,12 @@ namespace Barotrauma.Steam { string destinationPath = Path.Combine(newDir, cf.Path); if (!Directory.Exists(Path.GetDirectoryName(destinationPath))) { Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); } + if (File.Exists(destinationPath)) { File.Delete(destinationPath); } File.Move(cf.Path, destinationPath); cf.Path = destinationPath; } } + contentPackage.GameVersion = GameMain.Version; contentPackage.Save(contentPackage.Path); } catch (Exception e) @@ -1202,7 +1213,22 @@ namespace Barotrauma.Steam return false; } - string metaDataPath = Path.Combine(item.Directory.FullName, MetadataFileName); + string metaDataPath = ""; + try + { + metaDataPath = Path.Combine(item.Directory.FullName, MetadataFileName); + } + catch (ArgumentException e) + { + string errorMessage = "Metadata file for the Workshop item \"" + item.Title + + "\" not found. Could not combine path (" + (item.Directory.FullName ?? "directory name empty") + ")."; + DebugConsole.ThrowError(errorMessage); + GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemEnabled:PathCombineException" + item.Title, + GameAnalyticsSDK.Net.EGAErrorSeverity.Error, + errorMessage); + return false; + } + if (!File.Exists(metaDataPath)) { DebugConsole.ThrowError("Metadata file for the Workshop item \"" + item.Title + "\" not found. The file may be corrupted."); @@ -1309,12 +1335,18 @@ namespace Barotrauma.Steam } public static string GetWorkshopItemContentPackagePath(ContentPackage contentPackage) + { + return GetWorkshopItemContentPackagePath(contentPackage, legacy: contentPackage.GameVersion <= new Version(0, 9, 1, 0)); + } + + private static string GetWorkshopItemContentPackagePath(ContentPackage contentPackage, bool legacy) { string fileName = contentPackage.Name; - string invalidChars = ToolBox.RemoveInvalidFileNameChars(fileName); - return contentPackage.GameVersion > new Version(0, 9, 1, 0) ? - Path.Combine("Mods", fileName, MetadataFileName) : - Path.Combine("Data", "ContentPackages", fileName + ".xml"); //legacy support + fileName = ToolBox.RemoveInvalidFileNameChars(fileName); + + return legacy ? + Path.Combine("Data", "ContentPackages", fileName + ".xml") : + Path.Combine("Mods", fileName, MetadataFileName); } #endregion diff --git a/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipClient.cs index 74fe4b3fa..afa8e6663 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipClient.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipClient.cs @@ -94,7 +94,7 @@ namespace Barotrauma.Networking client.VoipSound = new VoipSound(GameMain.SoundManager, client.VoipQueue); } - if (client.Character != null && !client.Character.IsDead && client.Character.SpeechImpediment <= 100.0f) + if (client.Character != null && !client.Character.IsDead && !client.Character.Removed && client.Character.SpeechImpediment <= 100.0f) { var messageType = ChatMessage.CanUseRadio(client.Character, out WifiComponent radio) ? ChatMessageType.Radio : ChatMessageType.Default; client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]); @@ -119,7 +119,7 @@ namespace Barotrauma.Networking if (client.VoipSound.CurrentAmplitude > 0.1f) //TODO: might need to tweak { - if (client.Character != null) + if (client.Character != null && !client.Character.Removed) { Vector3 clientPos = new Vector3(client.Character.WorldPosition.X, client.Character.WorldPosition.Y, 0.0f); Vector3 listenerPos = GameMain.SoundManager.ListenerPosition; diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs index 532901020..55081b0a8 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs @@ -859,6 +859,9 @@ namespace Barotrauma private void FillStoreItemList() { + float prevStoreItemScroll = storeItemList.BarScroll; + float prevMyItemScroll = myItemList.BarScroll; + int width = storeItemList.Rect.Width; HashSet existingItemFrames = new HashSet(); foreach (MapEntityPrefab mapEntityPrefab in MapEntityPrefab.List) @@ -883,6 +886,9 @@ namespace Barotrauma storeItemList.Content.RectTransform.SortChildren( (x, y) => (x.GUIComponent.UserData as PurchasedItem).ItemPrefab.Name.CompareTo((y.GUIComponent.UserData as PurchasedItem).ItemPrefab.Name)); + + storeItemList.BarScroll = prevStoreItemScroll; + myItemList.BarScroll = prevMyItemScroll; } private void FilterStoreItems(MapEntityCategory? category, string filter) @@ -904,7 +910,7 @@ namespace Barotrauma btn.Selected = (MapEntityCategory)btn.UserData == selectedItemCategory; } storeItemList.UpdateScrollBarSize(); - storeItemList.BarScroll = 0.0f; + //storeItemList.BarScroll = 0.0f; } public string GetMoney() diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index e54e31b3b..5abe026f0 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -1216,7 +1216,7 @@ namespace Barotrauma CanBeFocused = false }; - var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name && s.MD5Hash.Hash == sub.MD5Hash.Hash); + var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name && s.MD5Hash?.Hash == sub.MD5Hash?.Hash); if (matchingSub == null) matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name); if (matchingSub == null) @@ -1224,7 +1224,7 @@ namespace Barotrauma subTextBlock.TextColor = new Color(subTextBlock.TextColor, 0.5f); frame.ToolTip = TextManager.Get("SubNotFound"); } - else if (matchingSub.MD5Hash.Hash != sub.MD5Hash.Hash) + else if (matchingSub?.MD5Hash == null || matchingSub.MD5Hash?.Hash != sub.MD5Hash?.Hash) { subTextBlock.TextColor = new Color(subTextBlock.TextColor, 0.5f); frame.ToolTip = TextManager.Get("SubDoesntMatch"); @@ -1383,25 +1383,44 @@ namespace Barotrauma OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) ClosePlayerFrame(btn, userdata); return true; } }; - Vector2 frameSize = GameMain.Client.HasPermission(ClientPermissions.ManagePermissions) ? new Vector2(.24f, .44f) : new Vector2(.24f, .24f); + Vector2 frameSize = GameMain.Client.HasPermission(ClientPermissions.ManagePermissions) ? new Vector2(.24f, .5f) : new Vector2(.24f, .24f); - var playerFrameInner = new GUIFrame(new RectTransform(frameSize, playerFrame.RectTransform, Anchor.Center)); - var paddedPlayerFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), playerFrameInner.RectTransform, Anchor.Center)) + var playerFrameInner = new GUIFrame(new RectTransform(frameSize, playerFrame.RectTransform, Anchor.Center) { MinSize = new Point(550, 0) }); + var paddedPlayerFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.88f), playerFrameInner.RectTransform, Anchor.Center)) { Stretch = true, - RelativeSpacing = 0.05f + RelativeSpacing = 0.03f }; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), + var headerContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true) + { + Stretch = true + }; + + var nameText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 1.0f), headerContainer.RectTransform), text: selectedClient.Name, font: GUI.LargeFont); - //TODO: UI for client permission management + if (selectedClient.SteamID != 0 && Steam.SteamManager.IsInitialized) + { + var viewSteamProfileButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), headerContainer.RectTransform, Anchor.TopCenter), + TextManager.Get("ViewSteamProfile")) + { + UserData = selectedClient + }; + + GUITextBlock.AutoScaleAndNormalize(nameText, viewSteamProfileButton.TextBlock); + + viewSteamProfileButton.OnClicked = (bt, userdata) => + { + Steam.SteamManager.Instance.Overlay.OpenUrl("https://steamcommunity.com/profiles/" + selectedClient.SteamID.ToString()); + return true; + }; + } + if (GameMain.Client.HasPermission(ClientPermissions.ManagePermissions)) { playerFrame.UserData = selectedClient; - - //new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), selectedClient.Connection.RemoteEndPoint.Address.ToString()); - + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), paddedPlayerFrame.RectTransform), TextManager.Get("Rank")); var rankDropDown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), @@ -1441,16 +1460,45 @@ namespace Barotrauma Stretch = true, RelativeSpacing = 0.05f }; - new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), permissionLabels.RectTransform), TextManager.Get("Permissions")); - new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), permissionLabels.RectTransform), TextManager.Get("PermittedConsoleCommands")); - + var permissionLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), permissionLabels.RectTransform), TextManager.Get("Permissions")); + var consoleCommandLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), permissionLabels.RectTransform), TextManager.Get("PermittedConsoleCommands")); + GUITextBlock.AutoScaleAndNormalize(permissionLabel, consoleCommandLabel); + var permissionContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.4f), paddedPlayerFrame.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; - var permissionsBox = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), permissionContainer.RectTransform)) + var listBoxContainerLeft = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), permissionContainer.RectTransform)) + { + Stretch = true, + RelativeSpacing = 0.05f + }; + + new GUITickBox(new RectTransform(new Vector2(0.15f, 0.15f), listBoxContainerLeft.RectTransform), TextManager.Get("all", fallBackTag: "clientpermission.all")) + { + Enabled = !myClient, + OnSelected = (tickbox) => + { + //reset rank to custom + rankDropDown.SelectItem(null); + + var client = playerFrame.UserData as Client; + if (client == null) { return false; } + + foreach (GUIComponent child in tickbox.Parent.GetChild().Content.Children) + { + var permissionTickBox = child as GUITickBox; + permissionTickBox.Enabled = false; + permissionTickBox.Selected = tickbox.Selected; + permissionTickBox.Enabled = true; + } + GameMain.Client.UpdateClientPermissions(client); + return true; + } + }; + var permissionsBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), listBoxContainerLeft.RectTransform)) { UserData = selectedClient }; @@ -1471,10 +1519,9 @@ namespace Barotrauma rankDropDown.SelectItem(null); var client = playerFrame.UserData as Client; - if (client == null) return false; + if (client == null) { return false; } var thisPermission = (ClientPermissions)tickBox.UserData; - if (tickBox.Selected) { client.GivePermission(thisPermission); @@ -1483,15 +1530,44 @@ namespace Barotrauma { client.RemovePermission(thisPermission); } - - GameMain.Client.UpdateClientPermissions(client); - + if (tickBox.Enabled) + { + GameMain.Client.UpdateClientPermissions(client); + } return true; } }; } - var commandList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), permissionContainer.RectTransform)) + var listBoxContainerRight = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), permissionContainer.RectTransform)) + { + Stretch = true, + RelativeSpacing = 0.05f + }; + + new GUITickBox(new RectTransform(new Vector2(0.15f, 0.15f), listBoxContainerRight.RectTransform), TextManager.Get("all", fallBackTag: "clientpermission.all")) + { + Enabled = !myClient, + OnSelected = (tickbox) => + { + //reset rank to custom + rankDropDown.SelectItem(null); + + var client = playerFrame.UserData as Client; + if (client == null) { return false; } + + foreach (GUIComponent child in tickbox.Parent.GetChild().Content.Children) + { + var commandTickBox = child as GUITickBox; + commandTickBox.Enabled = false; + commandTickBox.Selected = tickbox.Selected; + commandTickBox.Enabled = true; + } + GameMain.Client.UpdateClientPermissions(client); + return true; + } + }; + var commandList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), listBoxContainerRight.RectTransform)) { UserData = selectedClient }; @@ -1522,37 +1598,23 @@ namespace Barotrauma { client.PermittedConsoleCommands.Add(selectedCommand); } - - GameMain.Client.UpdateClientPermissions(client); + if (tickBox.Enabled) + { + GameMain.Client.UpdateClientPermissions(client); + } return true; }; } } - var buttonAreaUpper = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true); - var buttonAreaMiddle = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true); - var buttonAreaLower = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true); - - if (selectedClient.SteamID != 0 && Steam.SteamManager.IsInitialized) - { - var viewSteamProfileButton = new GUIButton(new RectTransform(new Vector2(0.8f, 1.0f), buttonAreaUpper.RectTransform, Anchor.TopCenter), - TextManager.Get("ViewSteamProfile")) - { - UserData = selectedClient - }; - - viewSteamProfileButton.OnClicked = (bt, userdata) => - { - Steam.SteamManager.Instance.Overlay.OpenUrl("https://steamcommunity.com/profiles/" + selectedClient.SteamID.ToString()); - return true; - }; - } - + var buttonAreaTop = myClient ? null : new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.08f), paddedPlayerFrame.RectTransform), isHorizontal: true); + var buttonAreaLower = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.08f), paddedPlayerFrame.RectTransform), isHorizontal: true); + if (!myClient) { if (GameMain.Client.HasPermission(ClientPermissions.Ban)) { - var banButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaMiddle.RectTransform), + var banButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaTop.RectTransform), TextManager.Get("Ban")) { UserData = selectedClient @@ -1560,7 +1622,7 @@ namespace Barotrauma banButton.OnClicked = (bt, userdata) => { BanPlayer(selectedClient); return true; }; banButton.OnClicked += ClosePlayerFrame; - var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaMiddle.RectTransform), + var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaTop.RectTransform), TextManager.Get("BanRange")) { UserData = selectedClient @@ -1594,7 +1656,7 @@ namespace Barotrauma kickButton.OnClicked += ClosePlayerFrame; } - new GUITickBox(new RectTransform(new Vector2(0.25f, 1.0f), buttonAreaMiddle.RectTransform, Anchor.TopRight), + new GUITickBox(new RectTransform(new Vector2(0.25f, 1.0f), buttonAreaTop.RectTransform, Anchor.TopRight), TextManager.Get("Mute")) { IgnoreLayoutGroups = true, @@ -1982,24 +2044,34 @@ namespace Barotrauma public bool TrySelectSub(string subName, string md5Hash, GUIListBox subList) { - if (GameMain.Client == null) return false; + if (GameMain.Client == null) { return false; } //already downloading the selected sub file if (GameMain.Client.FileReceiver.ActiveTransfers.Any(t => t.FileName == subName + ".sub")) { return false; } + + Submarine sub = subList.Content.Children + .FirstOrDefault(c => c.UserData is Submarine s && s.Name == subName && s.MD5Hash?.Hash == md5Hash)? + .UserData as Submarine; - Submarine sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName && m.MD5Hash.Hash == md5Hash); - if (sub == null) sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName); - - var matchingListSub = subList.Content.GetChildByUserData(sub); - if (sub != null && subList.SelectedData as Submarine == sub) + //matching sub found and already selected, all good + if (sub != null && subList.SelectedData is Submarine selectedSub && selectedSub.MD5Hash?.Hash == md5Hash) { return true; } - if (matchingListSub != null) + //sub not found, see if we have a sub with the same name + if (sub == null) + { + sub = subList.Content.Children + .FirstOrDefault(c => c.UserData is Submarine s && s.Name == subName)? + .UserData as Submarine; + } + + //found a sub that at least has the same name, select it + if (sub != null) { if (subList.Parent is GUIDropDown subDropDown) { @@ -2008,7 +2080,7 @@ namespace Barotrauma else { subList.OnSelected -= VotableClicked; - subList.Select(subList.Content.GetChildIndex(matchingListSub), true); + subList.Select(sub, force: true); subList.OnSelected += VotableClicked; } @@ -2016,58 +2088,62 @@ namespace Barotrauma FailedSelectedSub = null; else FailedSelectedShuttle = null; + + //hashes match, all good + if (sub.MD5Hash?.Hash == md5Hash) + { + return true; + } + } + + //------------------------------------------------------------------------------------- + //if we get to this point, a matching sub was not found or it has an incorrect MD5 hash + + if (subList == SubList) + FailedSelectedSub = new Pair(subName, md5Hash); + else + FailedSelectedShuttle = new Pair(subName, md5Hash); + + string errorMsg = ""; + if (sub == null) + { + errorMsg = TextManager.GetWithVariable("SubNotFoundError", "[subname]", subName) + " "; + } + else if (sub.MD5Hash?.Hash == null) + { + errorMsg = TextManager.GetWithVariable("SubLoadError", "[subname]", subName) + " "; + subList.Content.GetChildByUserData(sub).GetChild().TextColor = Color.Red; + } + else + { + errorMsg = TextManager.GetWithVariables("SubDoesntMatchError", new string[3] { "[subname]" , "[myhash]", "[serverhash]" }, + new string[3] { sub.Name, sub.MD5Hash.ShortHash, Md5Hash.GetShortHash(md5Hash) }) + " "; } - if (sub == null || sub.MD5Hash.Hash != md5Hash) + errorMsg += TextManager.Get("DownloadSubQuestion"); + + //already showing a message about the same sub + if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "request" + subName)) { - if (subList == SubList) - FailedSelectedSub = new Pair(subName, md5Hash); - else - FailedSelectedShuttle = new Pair(subName, md5Hash); - - string errorMsg = ""; - if (sub == null) - { - errorMsg = TextManager.GetWithVariable("SubNotFoundError", "[subname]", subName) + " "; - } - else if (sub.MD5Hash.Hash == null) - { - errorMsg = TextManager.GetWithVariable("SubLoadError", "[subname]", subName) + " "; - if (matchingListSub != null) matchingListSub.GetChild().TextColor = Color.Red; - } - else - { - errorMsg = TextManager.GetWithVariables("SubDoesntMatchError", new string[3] { "[subname]" , "[myhash]", "[serverhash]" }, - new string[3] { sub.Name, sub.MD5Hash.ShortHash, Md5Hash.GetShortHash(md5Hash) }) + " "; - } - - errorMsg += TextManager.Get("DownloadSubQuestion"); - - //already showing a message about the same sub - if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "request" + subName)) - { - return false; - } - - var requestFileBox = new GUIMessageBox(TextManager.Get("DownloadSubLabel"), errorMsg, - new string[] { TextManager.Get("Yes"), TextManager.Get("No") }) - { - UserData = "request" + subName - }; - requestFileBox.Buttons[0].UserData = new string[] { subName, md5Hash }; - requestFileBox.Buttons[0].OnClicked += requestFileBox.Close; - requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) => - { - string[] fileInfo = (string[])userdata; - GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo[0], fileInfo[1]); - return true; - }; - requestFileBox.Buttons[1].OnClicked += requestFileBox.Close; - return false; } - return true; + var requestFileBox = new GUIMessageBox(TextManager.Get("DownloadSubLabel"), errorMsg, + new string[] { TextManager.Get("Yes"), TextManager.Get("No") }) + { + UserData = "request" + subName + }; + requestFileBox.Buttons[0].UserData = new string[] { subName, md5Hash }; + requestFileBox.Buttons[0].OnClicked += requestFileBox.Close; + requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) => + { + string[] fileInfo = (string[])userdata; + GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo[0], fileInfo[1]); + return true; + }; + requestFileBox.Buttons[1].OnClicked += requestFileBox.Close; + + return false; } } diff --git a/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs index 40b685ec1..b99db8748 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs @@ -1297,13 +1297,13 @@ namespace Barotrauma string modFolder = Path.GetDirectoryName(itemContentPackage.Path); string filePathRelativeToModFolder = UpdaterUtil.GetRelativePath(file, Path.Combine(Environment.CurrentDirectory, modFolder)); - string destinationPath = Path.Combine(modFolder, Path.GetFileName(file)); + string destinationPath; //file is not inside the mod folder, we need to move it if (filePathRelativeToModFolder.StartsWith("..") || Path.GetPathRoot(Environment.CurrentDirectory) != Path.GetPathRoot(file)) { - string tryPath = destinationPath; + destinationPath = Path.Combine(modFolder, Path.GetFileName(file)); //add a number to the filename if a file with the same name already exists i = 2; while (File.Exists(destinationPath)) @@ -1317,10 +1317,14 @@ namespace Barotrauma } catch (Exception e) { - DebugConsole.ThrowError("Copying the file \""+file+"\" to the mod folder failed.", e); + DebugConsole.ThrowError("Copying the file \"" + file + "\" to the mod folder failed.", e); return; } } + else + { + destinationPath = Path.Combine(modFolder, filePathRelativeToModFolder); + } itemContentPackage.AddFile(destinationPath, ContentType.None); } itemContentPackage.Save(itemContentPackage.Path); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs index 1bcf46154..9208bc39c 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs @@ -23,7 +23,7 @@ namespace Barotrauma "CrewExperienceHigh" }; - private readonly Point defaultPreviewImageSize = new Point(512, 368); + private readonly Point defaultPreviewImageSize = new Point(640, 368); private Camera cam; @@ -158,7 +158,26 @@ namespace Barotrauma var button = new GUIButton(new RectTransform(new Vector2(0.07f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft), TextManager.Get("Back")) { - OnClicked = GameMain.MainMenuScreen.ReturnToMainMenu + OnClicked = (b, d) => + { + var msgBox = new GUIMessageBox("", TextManager.Get("PauseMenuQuitVerificationEditor"), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") }) + { + UserData = "verificationprompt" + }; + msgBox.Buttons[0].OnClicked = (yesBtn, userdata) => + { + GUIMessageBox.CloseAll(); + GameMain.MainMenuScreen.Select(); + return true; + }; + msgBox.Buttons[0].OnClicked += msgBox.Close; + msgBox.Buttons[1].OnClicked = (_, userdata) => + { + msgBox.Close(); + return true; + }; + return true; + } }; button = new GUIButton(new RectTransform(new Vector2(0.07f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.07f, 0.0f) }, TextManager.Get("OpenSubButton")) @@ -497,9 +516,8 @@ namespace Barotrauma foreach (MapEntityPrefab ep in MapEntityPrefab.List) { - var itemAssemblyPrefab = ep as ItemAssemblyPrefab; #if !DEBUG - if (itemAssemblyPrefab != null && itemAssemblyPrefab.HideInMenus) { continue; } + if (ep.HideInMenus) { continue; } #endif bool legacy = ep.Category == MapEntityCategory.Legacy; @@ -548,7 +566,7 @@ namespace Barotrauma }; } - if (ep.Category == MapEntityCategory.ItemAssembly) + if (ep is ItemAssemblyPrefab itemAssemblyPrefab) { new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.75f), paddedFrame.RectTransform, Anchor.TopCenter), onDraw: itemAssemblyPrefab.DrawIcon, onUpdate: null) @@ -2448,7 +2466,7 @@ namespace Barotrauma Matrix.CreateScale(new Vector3(scale, scale, 1)) * viewMatrix; - Sprite backgroundSprite = LevelGenerationParams.LevelParams.Find(l => l.BackgroundTopSprite != null).BackgroundTopSprite; + /*Sprite backgroundSprite = LevelGenerationParams.LevelParams.Find(l => l.BackgroundTopSprite != null).BackgroundTopSprite;*/ using (RenderTarget2D rt = new RenderTarget2D( GameMain.Instance.GraphicsDevice, @@ -2457,14 +2475,14 @@ namespace Barotrauma { GameMain.Instance.GraphicsDevice.SetRenderTarget(rt); - GameMain.Instance.GraphicsDevice.Clear(Color.Black); + GameMain.Instance.GraphicsDevice.Clear(new Color(8, 13, 19)); - if (backgroundSprite != null) + /*if (backgroundSprite != null) { spriteBatch.Begin(); backgroundSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(width, height), color: new Color(0.025f, 0.075f, 0.131f, 1.0f)); spriteBatch.End(); - } + }*/ spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null, transform); Submarine.Draw(spriteBatch, false); diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs index f3332b751..61516ac78 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs @@ -484,12 +484,19 @@ namespace Barotrauma public static SoundChannel PlaySound(string soundTag, Vector2 position, float? volume = null, float? range = null, Hull hullGuess = null) { var sound = GetSound(soundTag); - if (sound == null) return null; + if (sound == null) { return null; } return PlaySound(sound, position, volume ?? sound.BaseGain, range ?? sound.BaseFar, hullGuess); } public static SoundChannel PlaySound(Sound sound, Vector2 position, float? volume = null, float? range = null, Hull hullGuess = null) { + if (sound == null) + { + string errorMsg = "Error in SoundPlayer.PlaySound (sound was null)\n" + Environment.StackTrace; + GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.PlaySound:SoundNull" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + return null; + } + float far = range ?? sound.BaseFar; if (Vector2.DistanceSquared(new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y), position) > far * far) return null; diff --git a/Barotrauma/BarotraumaClient/Source/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaClient/Source/StatusEffects/StatusEffect.cs index 9b9bd5e70..1842db331 100644 --- a/Barotrauma/BarotraumaClient/Source/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaClient/Source/StatusEffects/StatusEffect.cs @@ -35,7 +35,7 @@ namespace Barotrauma break; case "sound": var sound = Submarine.LoadRoundSound(subElement); - if (sound != null) + if (sound?.Sound != null) { loopSound = subElement.GetAttributeBool("loop", false); if (subElement.Attribute("selectionmode") != null) diff --git a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs index ea94e4035..888728512 100644 --- a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs +++ b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.9.2.1")] -[assembly: AssemblyFileVersion("0.9.2.1")] +[assembly: AssemblyVersion("0.9.3.2")] +[assembly: AssemblyFileVersion("0.9.3.2")] diff --git a/Barotrauma/BarotraumaServer/Server.csproj b/Barotrauma/BarotraumaServer/Server.csproj index fc904e4f6..65d30a973 100644 --- a/Barotrauma/BarotraumaServer/Server.csproj +++ b/Barotrauma/BarotraumaServer/Server.csproj @@ -191,6 +191,7 @@ + diff --git a/Barotrauma/BarotraumaServer/Source/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaServer/Source/Characters/CharacterNetworking.cs index bdf17c1b5..2dc77d5da 100644 --- a/Barotrauma/BarotraumaServer/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaServer/Source/Characters/CharacterNetworking.cs @@ -263,20 +263,20 @@ namespace Barotrauma switch ((NetEntityEvent.Type)extraData[0]) { case NetEntityEvent.Type.InventoryState: - msg.WriteRangedIntegerDeprecated(0, 3, 0); + msg.WriteRangedInteger(0, 0, 3); Inventory.SharedWrite(msg, extraData); break; case NetEntityEvent.Type.Control: - msg.WriteRangedIntegerDeprecated(0, 3, 1); + msg.WriteRangedInteger(1, 0, 3); Client owner = ((Client)extraData[1]); msg.Write(owner == null ? (byte)0 : owner.ID); break; case NetEntityEvent.Type.Status: - msg.WriteRangedIntegerDeprecated(0, 3, 2); + msg.WriteRangedInteger(2, 0, 3); WriteStatus(msg); break; case NetEntityEvent.Type.UpdateSkills: - msg.WriteRangedIntegerDeprecated(0, 3, 3); + msg.WriteRangedInteger(3, 0, 3); if (Info?.Job == null) { msg.Write((byte)0); @@ -411,10 +411,10 @@ namespace Barotrauma msg.Write(IsDead); if (IsDead) { - msg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(CauseOfDeathType)).Length - 1, (int)CauseOfDeath.Type); + msg.WriteRangedInteger((int)CauseOfDeath.Type, 0, Enum.GetValues(typeof(CauseOfDeathType)).Length - 1); if (CauseOfDeath.Type == CauseOfDeathType.Affliction) { - msg.WriteRangedIntegerDeprecated(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(CauseOfDeath.Affliction)); + msg.WriteRangedInteger(AfflictionPrefab.List.IndexOf(CauseOfDeath.Affliction), 0, AfflictionPrefab.List.Count - 1); } if (AnimController?.LimbJoints == null) diff --git a/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/TraitorManager.cs b/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/TraitorManager.cs deleted file mode 100644 index fcd96b419..000000000 --- a/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/TraitorManager.cs +++ /dev/null @@ -1,177 +0,0 @@ -using Barotrauma.Networking; -using System.Collections.Generic; -using System.IO; - -namespace Barotrauma -{ - partial class Traitor - { - public readonly Character Character; - public Character TargetCharacter; //TODO: make a modular objective system (similar to crew missions) that allows for things OTHER than assasinations. - - public Traitor(Character character) - { - Character = character; - } - - public void Greet(GameServer server, string codeWords, string codeResponse) - { - string greetingMessage = TextManager.GetWithVariable("TraitorStartMessage", "[targetname]", TargetCharacter.Name); - string moreAgentsMessage = TextManager.GetWithVariables("TraitorMoreAgentsMessage", - new string[2] { "[codewords]", "[coderesponse]" }, new string[2] { codeWords, codeResponse }); - - var greetingChatMsg = ChatMessage.Create(null, greetingMessage, ChatMessageType.Server, null); - var moreAgentsChatMsg = ChatMessage.Create(null, moreAgentsMessage, ChatMessageType.Server, null); - - var moreAgentsMsgBox = ChatMessage.Create(null, moreAgentsMessage, ChatMessageType.MessageBox, null); - var greetingMsgBox = ChatMessage.Create(null, greetingMessage, ChatMessageType.MessageBox, null); - - Client traitorClient = server.ConnectedClients.Find(c => c.Character == Character); - GameMain.Server.SendDirectChatMessage(greetingChatMsg, traitorClient); - GameMain.Server.SendDirectChatMessage(moreAgentsChatMsg, traitorClient); - GameMain.Server.SendDirectChatMessage(greetingMsgBox, traitorClient); - GameMain.Server.SendDirectChatMessage(moreAgentsMsgBox, traitorClient); - - Client ownerClient = server.ConnectedClients.Find(c => c.Connection == server.OwnerConnection); - if (traitorClient != ownerClient && ownerClient != null && ownerClient.Character == null) - { - var ownerMsg = ChatMessage.Create( - null,//TextManager.Get("NewTraitor"), - TextManager.GetWithVariables("TraitorStartMessageServer", new string[2] { "[targetname]", "[traitorname]" }, new string[2] { TargetCharacter.Name, Character.Name }), - ChatMessageType.MessageBox, - null - ); - GameMain.Server.SendDirectChatMessage(ownerMsg, ownerClient); - } - } - } - - partial class TraitorManager - { - private static string wordsTxt = Path.Combine("Content", "CodeWords.txt"); - - public List TraitorList - { - get { return traitorList; } - } - - private List traitorList = new List(); - - public string codeWords, codeResponse; - - public TraitorManager(GameServer server, int traitorCount) - { - if (traitorCount < 1) //what why how - { - traitorCount = 1; - DebugConsole.ThrowError("Traitor Manager: TraitorCount somehow ended up less than 1, setting it to 1."); - } - Start(server, traitorCount); - } - - private void Start(GameServer server, int traitorCount) - { - if (server == null) return; - - List characters = new List(); //ANYONE can be a target. - List traitorCandidates = new List(); //Keep this to not re-pick traitors twice - foreach (Client client in server.ConnectedClients) - { - if (client.Character != null) - { - characters.Add(client.Character); - traitorCandidates.Add(client.Character); - } - } - - if (server.Character != null) - { - characters.Add(server.Character); //Add host character - traitorCandidates.Add(server.Character); - } - - if (characters.Count < 2) - { - return; - } - - codeWords = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt); - codeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt); - - while (traitorCount-- > 0) - { - if (traitorCandidates.Count <= 0) break; - - int traitorIndex = Rand.Int(traitorCandidates.Count); - Character traitorCharacter = traitorCandidates[traitorIndex]; - traitorCandidates.Remove(traitorCharacter); - - //Add them to the list - traitorList.Add(new Traitor(traitorCharacter)); - } - - //Now that traitors have been decided, let's do objectives in post for deciding things like Document Exchange. - foreach (Traitor traitor in traitorList) - { - Character traitorCharacter = traitor.Character; - int targetIndex = Rand.Int(characters.Count); - while (characters[targetIndex] == traitorCharacter) //Cannot target self - { - targetIndex = Rand.Int(characters.Count); - } - - Character targetCharacter = characters[targetIndex]; - traitor.TargetCharacter = targetCharacter; - traitor.Greet(server, codeWords, codeResponse); - } - } - - public string GetEndMessage() - { - if (GameMain.Server == null || traitorList.Count <= 0) return ""; - - string endMessage = ""; - - foreach (Traitor traitor in traitorList) - { - Character traitorCharacter = traitor.Character; - Character targetCharacter = traitor.TargetCharacter; - string messageTag; - - if (targetCharacter.IsDead) //Partial or complete mission success - { - if (traitorCharacter.IsDead) - { - messageTag = "TraitorEndMessageSuccessTraitorDead"; - } - else if (traitorCharacter.LockHands) - { - messageTag = "TraitorEndMessageSuccessTraitorDetained"; - } - else - messageTag = "TraitorEndMessageSuccess"; - } - else //Partial or complete failure - { - if (traitorCharacter.IsDead) - { - messageTag = "TraitorEndMessageFailureTraitorDead"; - } - else if (traitorCharacter.LockHands) - { - messageTag = "TraitorEndMessageFailureTraitorDetained"; - } - else - { - messageTag = "TraitorEndMessageFailure"; - } - } - - endMessage += (TextManager.ReplaceGenderPronouns(TextManager.GetWithVariables(messageTag, new string[2] { "[traitorname]", "[targetname]" }, - new string[2] { traitorCharacter.Name, targetCharacter.Name }), traitorCharacter.Info.Gender) + "\n"); - } - - return endMessage; - } - } -} diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Controller.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Controller.cs new file mode 100644 index 000000000..21c3e65cf --- /dev/null +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Controller.cs @@ -0,0 +1,12 @@ +using Barotrauma.Networking; + +namespace Barotrauma.Items.Components +{ + partial class Controller : ItemComponent, IServerSerializable + { + public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null) + { + msg.Write(state); + } + } +} diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Engine.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Engine.cs index a5c5697c7..af8583204 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Engine.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Engine.cs @@ -1,8 +1,5 @@ -using Microsoft.Xna.Framework; +using Barotrauma.Networking; using System; -using System.Globalization; -using System.Xml.Linq; -using Barotrauma.Networking; namespace Barotrauma.Items.Components { @@ -11,7 +8,7 @@ namespace Barotrauma.Items.Components public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null) { //force can only be adjusted at 10% intervals -> no need for more accuracy than this - msg.WriteRangedIntegerDeprecated(-10, 10, (int)(targetForce / 10.0f)); + msg.WriteRangedInteger((int)(targetForce / 10.0f), -10, 10); } public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Fabricator.cs index f5f6f4bdb..5edc2c619 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Fabricator.cs @@ -34,7 +34,7 @@ namespace Barotrauma.Items.Components public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null) { int itemIndex = fabricatedItem == null ? -1 : fabricationRecipes.IndexOf(fabricatedItem); - msg.WriteRangedIntegerDeprecated(-1, fabricationRecipes.Count - 1, itemIndex); + msg.WriteRangedInteger(itemIndex, -1, fabricationRecipes.Count - 1); UInt16 userID = fabricatedItem == null || user == null ? (UInt16)0 : user.ID; msg.Write(userID); } diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Pump.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Pump.cs index f5f114a84..4b8261204 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Pump.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Pump.cs @@ -35,7 +35,7 @@ namespace Barotrauma.Items.Components public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null) { //flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this - msg.WriteRangedIntegerDeprecated(-10, 10, (int)(flowPercentage / 10.0f)); + msg.WriteRangedInteger((int)(flowPercentage / 10.0f), -10, 10); msg.Write(IsActive); } } diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Power/PowerContainer.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Power/PowerContainer.cs index 76fbd282e..8b9acaaf8 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Power/PowerContainer.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Power/PowerContainer.cs @@ -22,7 +22,7 @@ namespace Barotrauma.Items.Components public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null) { - msg.WriteRangedIntegerDeprecated(0, 10, (int)(rechargeSpeed / MaxRechargeSpeed * 10)); + msg.WriteRangedInteger((int)(rechargeSpeed / MaxRechargeSpeed * 10), 0, 10); float chargeRatio = MathHelper.Clamp(charge / capacity, 0.0f, 1.0f); msg.WriteRangedSingle(chargeRatio, 0.0f, 1.0f, 8); diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/ConnectionPanel.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/ConnectionPanel.cs index 75655d62e..cd0f7b77c 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/ConnectionPanel.cs @@ -88,7 +88,10 @@ namespace Barotrauma.Items.Components existingWire.RemoveConnection(item); item.GetComponent()?.DisconnectedWires.Add(existingWire); - GameMain.Server.KarmaManager.OnWireDisconnected(c.Character, existingWire); + if (!wires.Any(w => w.Contains(existingWire))) + { + GameMain.Server.KarmaManager.OnWireDisconnected(c.Character, existingWire); + } if (existingWire.Connections[0] == null && existingWire.Connections[1] == null) { diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/Wire.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/Wire.cs index 92730a790..68ee2349c 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/Wire.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/Wire.cs @@ -28,8 +28,8 @@ namespace Barotrauma.Items.Components int nodeStartIndex = eventIndex * MaxNodesPerNetworkEvent; int nodeCount = MathHelper.Clamp(nodes.Count - nodeStartIndex, 0, MaxNodesPerNetworkEvent); - msg.WriteRangedIntegerDeprecated(0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent), eventIndex); - msg.WriteRangedIntegerDeprecated(0, MaxNodesPerNetworkEvent, nodeCount); + msg.WriteRangedInteger(eventIndex, 0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent)); + msg.WriteRangedInteger(nodeCount, 0, MaxNodesPerNetworkEvent); for (int i = nodeStartIndex; i < nodeStartIndex + nodeCount; i++) { msg.Write(nodes[i].X); diff --git a/Barotrauma/BarotraumaServer/Source/Items/Inventory.cs b/Barotrauma/BarotraumaServer/Source/Items/Inventory.cs index af6943965..907307bef 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Inventory.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Inventory.cs @@ -11,13 +11,13 @@ namespace Barotrauma public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { List prevItems = new List(Items); - ushort[] newItemIDs = new ushort[capacity]; - for (int i = 0; i < capacity; i++) + byte itemCount = msg.ReadByte(); + ushort[] newItemIDs = new ushort[itemCount]; + for (int i = 0; i < itemCount; i++) { newItemIDs[i] = msg.ReadUInt16(); } - if (c == null || c.Character == null) return; diff --git a/Barotrauma/BarotraumaServer/Source/Items/Item.cs b/Barotrauma/BarotraumaServer/Source/Items/Item.cs index 2c52c7bdc..290ad0804 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Item.cs @@ -25,7 +25,7 @@ namespace Barotrauma { errorMsg = "Failed to write a network event for the item \"" + Name + "\" - event type not set."; } - msg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)NetEntityEvent.Type.Invalid); + msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1); DebugConsole.Log(errorMsg); GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:InvalidData" + Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; @@ -34,7 +34,7 @@ namespace Barotrauma int initialWritePos = msg.LengthBits; NetEntityEvent.Type eventType = (NetEntityEvent.Type)extraData[0]; - msg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)eventType); + msg.WriteRangedInteger((int)eventType, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1); switch (eventType) { case NetEntityEvent.Type.ComponentState: @@ -54,7 +54,7 @@ namespace Barotrauma errorMsg = "Failed to write a component state event for the item \"" + Name + "\" - component \"" + components[componentIndex] + "\" is not server serializable."; break; } - msg.WriteRangedIntegerDeprecated(0, components.Count - 1, componentIndex); + msg.WriteRangedInteger(componentIndex, 0, components.Count - 1); (components[componentIndex] as IServerSerializable).ServerWrite(msg, c, extraData); break; case NetEntityEvent.Type.InventoryState: @@ -74,7 +74,7 @@ namespace Barotrauma errorMsg = "Failed to write an inventory state event for the item \"" + Name + "\" - component \"" + components[containerIndex] + "\" is not server serializable."; break; } - msg.WriteRangedIntegerDeprecated(0, components.Count - 1, containerIndex); + msg.WriteRangedInteger(containerIndex, 0, components.Count - 1); (components[containerIndex] as ItemContainer).Inventory.ServerWrite(msg, c); break; case NetEntityEvent.Type.Status: @@ -91,7 +91,7 @@ namespace Barotrauma byte targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255; msg.Write((byte)components.IndexOf(targetComponent)); - msg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(ActionType)).Length - 1, (int)actionType); + msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1); msg.Write(targetID); msg.Write(targetLimbIndex); } @@ -106,7 +106,7 @@ namespace Barotrauma Character targetCharacter = FindEntityByID(targetID) as Character; byte targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255; - msg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(ActionType)).Length - 1, (int)actionType); + msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1); msg.Write((byte)(targetComponent == null ? 255 : components.IndexOf(targetComponent))); msg.Write(targetID); msg.Write(targetLimbIndex); @@ -132,7 +132,7 @@ namespace Barotrauma //something went wrong - rewind the write position and write invalid event type to prevent creating an unreadable event msg.BitPosition = initialWritePos; msg.LengthBits = initialWritePos; - msg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)NetEntityEvent.Type.Invalid); + msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1); DebugConsole.Log(errorMsg); GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:" + errorMsg, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); } @@ -345,7 +345,7 @@ namespace Barotrauma if (!ItemList.Contains(this)) { - string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet."; + string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; diff --git a/Barotrauma/BarotraumaServer/Source/Map/Hull.cs b/Barotrauma/BarotraumaServer/Source/Map/Hull.cs index 8f802cd06..dd3b8de39 100644 --- a/Barotrauma/BarotraumaServer/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaServer/Source/Map/Hull.cs @@ -56,7 +56,7 @@ namespace Barotrauma message.Write(FireSources.Count > 0); if (FireSources.Count > 0) { - message.WriteRangedIntegerDeprecated(0, 16, Math.Min(FireSources.Count, 16)); + message.WriteRangedInteger(Math.Min(FireSources.Count, 16), 0, 16); for (int i = 0; i < Math.Min(FireSources.Count, 16); i++) { var fireSource = FireSources[i]; diff --git a/Barotrauma/BarotraumaServer/Source/Networking/BanList.cs b/Barotrauma/BarotraumaServer/Source/Networking/BanList.cs index 35a48bc46..2e089edc2 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/BanList.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/BanList.cs @@ -18,7 +18,7 @@ namespace Barotrauma.Networking this.ExpirationTime = expirationTime; this.UniqueIdentifier = LastIdentifier; LastIdentifier++; - this.IsRangeBan = IP.IndexOf(".x")>-1; + this.IsRangeBan = IP.IndexOf(".x") > -1; } public BannedPlayer(string name, ulong steamID, string reason, DateTime? expirationTime) @@ -30,6 +30,8 @@ namespace Barotrauma.Networking this.UniqueIdentifier = LastIdentifier; LastIdentifier++; this.IsRangeBan = false; + + this.IP = ""; } public bool CompareTo(string ipCompare) @@ -276,28 +278,42 @@ namespace Barotrauma.Networking public void ServerAdminWrite(IWriteMessage outMsg, Client c) { - if (!c.HasPermission(ClientPermissions.Ban)) + try { - outMsg.Write(false); outMsg.WritePadBits(); - return; - } - outMsg.Write(true); - outMsg.Write(c.Connection == GameMain.Server.OwnerConnection); + if (outMsg == null) { throw new ArgumentException("OutMsg was null"); } + if (GameMain.Server == null) { throw new Exception("GameMain.Server was null"); } - outMsg.WritePadBits(); - outMsg.WriteVariableUInt32((UInt32)bannedPlayers.Count); - for (int i = 0; i < bannedPlayers.Count; i++) - { - BannedPlayer bannedPlayer = bannedPlayers[i]; - - outMsg.Write(bannedPlayer.Name); - outMsg.Write(bannedPlayer.UniqueIdentifier); - outMsg.Write(bannedPlayer.IsRangeBan); outMsg.WritePadBits(); - if (c.Connection == GameMain.Server.OwnerConnection) + if (!c.HasPermission(ClientPermissions.Ban)) { - outMsg.Write(bannedPlayer.IP); - outMsg.Write(bannedPlayer.SteamID); + outMsg.Write(false); outMsg.WritePadBits(); + return; } + + outMsg.Write(true); + outMsg.Write(c.Connection == GameMain.Server.OwnerConnection); + + outMsg.WritePadBits(); + outMsg.WriteVariableUInt32((UInt32)bannedPlayers.Count); + for (int i = 0; i < bannedPlayers.Count; i++) + { + BannedPlayer bannedPlayer = bannedPlayers[i]; + + outMsg.Write(bannedPlayer.Name); + outMsg.Write(bannedPlayer.UniqueIdentifier); + outMsg.Write(bannedPlayer.IsRangeBan); outMsg.WritePadBits(); + if (c.Connection == GameMain.Server.OwnerConnection) + { + outMsg.Write(bannedPlayer.IP); + outMsg.Write(bannedPlayer.SteamID); + } + } + } + + catch (Exception e) + { + string errorMsg = "Error while writing banlist. {" + e + "}\n" + e.StackTrace; + GameAnalyticsManager.AddErrorEventOnce("Banlist.ServerAdminWrite", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + throw; } } diff --git a/Barotrauma/BarotraumaServer/Source/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaServer/Source/Networking/EntitySpawner.cs index 07997e710..f899b254d 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/EntitySpawner.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/EntitySpawner.cs @@ -6,6 +6,11 @@ namespace Barotrauma partial class EntitySpawner : Entity, IServerSerializable { public void CreateNetworkEvent(Entity entity, bool remove) + { + CreateNetworkEventProjSpecific(entity, remove); + } + + partial void CreateNetworkEventProjSpecific(Entity entity, bool remove) { if (GameMain.Server != null && entity != null) { diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs index 49b25cc3d..385882aed 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs @@ -264,17 +264,15 @@ namespace Barotrauma.Networking { newClient.GivePermission(ClientPermissions.All); newClient.PermittedConsoleCommands.AddRange(DebugConsole.Commands); - - GameMain.Server.UpdateClientPermissions(newClient); - GameMain.Server.SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient); + SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient); } - GameMain.Server.SendChatMessage($"ServerMessage.JoinedServer~[client]={clName}", ChatMessageType.Server, null); + SendChatMessage($"ServerMessage.JoinedServer~[client]={clName}", ChatMessageType.Server, null); serverSettings.ServerDetailsChanged = true; if (previousPlayer != null && previousPlayer.Name != newClient.Name) { - GameMain.Server.SendChatMessage($"ServerMessage.PreviousClientName~[client]={clName}~[previousname]={previousPlayer.Name}", ChatMessageType.Server, null); + SendChatMessage($"ServerMessage.PreviousClientName~[client]={clName}~[previousname]={previousPlayer.Name}", ChatMessageType.Server, null); previousPlayer.Name = newClient.Name; } @@ -299,6 +297,8 @@ namespace Barotrauma.Networking newClient.SetPermissions(ClientPermissions.None, new List()); } } + + UpdateClientPermissions(newClient); } private void OnClientDisconnect(NetworkConnection connection, string disconnectMsg) @@ -653,9 +653,18 @@ namespace Barotrauma.Networking catch (Exception e) { DebugConsole.ThrowError("Failed to write a network message for the client \"" + c.Name + "\"!", e); - GameAnalyticsManager.AddErrorEventOnce("GameServer.Update:ClientWriteFailed" + e.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, - "Failed to write a network message for the client \"" + c.Name + "\"! (MidRoundSyncing: " + c.NeedsMidRoundSync + ")\n" - + e.Message + "\n" + e.StackTrace); + + string errorMsg = "Failed to write a network message for the client \"" + c.Name + "\"! (MidRoundSyncing: " + c.NeedsMidRoundSync + ")\n" + + e.Message + "\n" + e.StackTrace; + if (e.InnerException != null) + { + errorMsg += "\nInner exception: " + e.InnerException.Message + "\n" + e.InnerException.StackTrace; + } + + GameAnalyticsManager.AddErrorEventOnce( + "GameServer.Update:ClientWriteFailed" + e.StackTrace, + GameAnalyticsSDK.Net.EGAErrorSeverity.Error, + errorMsg); } } @@ -832,7 +841,7 @@ namespace Barotrauma.Networking } Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error); - GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:LevelsDontMatch" + error, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr); + GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:" + errorStr, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr); if (c.Connection == OwnerConnection) { @@ -1475,7 +1484,7 @@ namespace Barotrauma.Networking private void WriteClientList(Client c, IWriteMessage outmsg) { bool hasChanged = NetIdUtils.IdMoreRecent(LastClientListUpdateID, c.LastRecvClientListUpdate); - if (!hasChanged) return; + if (!hasChanged) { return; } outmsg.Write((byte)ServerNetObject.CLIENT_LIST); outmsg.Write(LastClientListUpdateID); @@ -1502,6 +1511,8 @@ namespace Barotrauma.Networking outmsg.Write((byte)ServerNetObject.SYNC_IDS); + int settingsBytes = outmsg.LengthBytes; + if (NetIdUtils.IdMoreRecent(GameMain.NetLobbyScreen.LastUpdateID, c.LastRecvLobbyUpdate)) { outmsg.Write(true); @@ -1534,9 +1545,9 @@ namespace Barotrauma.Networking outmsg.Write(serverSettings.AllowSpectating); - outmsg.WriteRangedIntegerDeprecated(0, 2, (int)serverSettings.TraitorsEnabled); + outmsg.WriteRangedInteger((int)serverSettings.TraitorsEnabled, 0, 2); - outmsg.WriteRangedIntegerDeprecated(0, Enum.GetValues(typeof(MissionType)).Length - 1, (GameMain.NetLobbyScreen.MissionTypeIndex)); + outmsg.WriteRangedInteger((GameMain.NetLobbyScreen.MissionTypeIndex), 0, Enum.GetValues(typeof(MissionType)).Length - 1); outmsg.Write((byte)GameMain.NetLobbyScreen.SelectedModeIndex); outmsg.Write(GameMain.NetLobbyScreen.LevelSeed); @@ -1556,9 +1567,12 @@ namespace Barotrauma.Networking outmsg.Write(false); outmsg.WritePadBits(); } + settingsBytes = outmsg.LengthBytes - settingsBytes; + int campaignBytes = outmsg.LengthBytes; var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign; - if (campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode && + if (outmsg.LengthBytes < MsgConstants.MTU - 500 && + campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode && NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.LastRecvCampaignUpdate)) { outmsg.Write(true); @@ -1570,12 +1584,20 @@ namespace Barotrauma.Networking outmsg.Write(false); outmsg.WritePadBits(); } + campaignBytes = outmsg.LengthBytes - campaignBytes; outmsg.Write(c.LastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server - WriteClientList(c, outmsg); + int clientListBytes = outmsg.LengthBytes; + if (outmsg.LengthBytes < MsgConstants.MTU - 500) + { + WriteClientList(c, outmsg); + } + clientListBytes = outmsg.LengthBytes - clientListBytes; + int chatMessageBytes = outmsg.LengthBytes; WriteChatMessages(outmsg, c); + chatMessageBytes = outmsg.LengthBytes - outmsg.LengthBytes; outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE); @@ -1597,7 +1619,14 @@ namespace Barotrauma.Networking { if (outmsg.LengthBytes > MsgConstants.MTU) { - DebugConsole.ThrowError("Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU + ")"); + string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU + ")"; + errorMsg += + " Client list size: " + clientListBytes + " bytes\n" + + " Chat message size: " + chatMessageBytes + " bytes\n" + + " Campaign size: " + campaignBytes + " bytes\n" + + " Settings size: " + settingsBytes + " bytes\n\n"; + DebugConsole.ThrowError(errorMsg); + GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:ClientWriteLobby" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); } serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable); @@ -2018,6 +2047,8 @@ namespace Barotrauma.Networking c.PositionUpdateLastSent.Clear(); } + KarmaManager.OnRoundEnded(); + #if DEBUG messageCount.Clear(); #endif @@ -2644,25 +2675,48 @@ namespace Barotrauma.Networking } } + //send the message to the client whose permissions are being modified and the clients who are allowed to modify permissions + List recipients = new List() { client }; + foreach (Client otherClient in connectedClients) + { + if (otherClient.HasPermission(ClientPermissions.ManagePermissions) && !recipients.Contains(otherClient)) + { + recipients.Add(otherClient); + } + } + foreach (Client recipient in recipients) + { + CoroutineManager.StartCoroutine(SendClientPermissionsAfterClientListSynced(recipient, client)); + } + serverSettings.SaveClientPermissions(); + } + + + private IEnumerable SendClientPermissionsAfterClientListSynced(Client recipient, Client client) + { + DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 10); + while (recipient.LastRecvClientListUpdate < LastClientListUpdateID) + { + if (DateTime.Now > timeOut || GameMain.Server == null || !connectedClients.Contains(recipient)) + { + yield return CoroutineStatus.Success; + } + yield return null; + } + + SendClientPermissions(recipient, client); + yield return CoroutineStatus.Success; + } + + + private void SendClientPermissions(Client recipient, Client client) + { + if (recipient?.Connection == null) { return; } + IWriteMessage msg = new WriteOnlyMessage(); msg.Write((byte)ServerPacketHeader.PERMISSIONS); client.WritePermissions(msg); - - //send the message to the client whose permissions are being modified and the clients who are allowed to modify permissions - List recipients = new List() { client.Connection }; - foreach (Client otherClient in connectedClients) - { - if (otherClient.HasPermission(ClientPermissions.ManagePermissions) && !recipients.Contains(otherClient.Connection)) - { - recipients.Add(otherClient.Connection); - } - } - foreach (NetworkConnection c in recipients) - { - serverPeer.Send(msg, c, DeliveryMethod.Reliable); - } - - serverSettings.SaveClientPermissions(); + serverPeer.Send(msg, recipient.Connection, DeliveryMethod.Reliable); } public void GiveAchievement(Character character, string achievementIdentifier) diff --git a/Barotrauma/BarotraumaServer/Source/Networking/KarmaManager.cs b/Barotrauma/BarotraumaServer/Source/Networking/KarmaManager.cs index 37c1f8cce..a2bcd18d8 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/KarmaManager.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/KarmaManager.cs @@ -12,7 +12,6 @@ namespace Barotrauma { public List> WireDisconnectTime = new List>(); - //the client's karma value when they were last sent a notification about it (e.g. "your karma is very low") public float PreviousNotifiedKarma; public float StructureDamageAccumulator; @@ -23,6 +22,13 @@ namespace Barotrauma get { return Math.Max(StructureDamageAccumulator, structureDamagePerSecond); } set { structureDamagePerSecond = value; } } + + //when did a given character last attack this one + public Dictionary LastAttackTime + { + get; + private set; + } = new Dictionary(); } public bool TestMode = false; @@ -31,9 +37,7 @@ namespace Barotrauma private readonly List bannedClients = new List(); private DateTime perSecondUpdate; - - private double KarmaNotificationTime; - + public void UpdateClients(IEnumerable clients, float deltaTime) { if (!GameMain.Server.GameStarted) { return; } @@ -41,32 +45,34 @@ namespace Barotrauma bannedClients.Clear(); foreach (Client client in clients) { - var clientMemory = GetClientMemory(client); UpdateClient(client, deltaTime); if (perSecondUpdate < DateTime.Now) { + var clientMemory = GetClientMemory(client); clientMemory.StructureDamagePerSecond = clientMemory.StructureDamageAccumulator; clientMemory.StructureDamageAccumulator = 0.0f; + + var toRemove = clientMemory.LastAttackTime.Where(pair => pair.Value < Timing.TotalTime - AllowedRetaliationTime).Select(pair => pair.Key).ToList(); + foreach (var lastAttacker in toRemove) + { + clientMemory.LastAttackTime.Remove(lastAttacker); + } } } if (perSecondUpdate < DateTime.Now) - { - perSecondUpdate = DateTime.Now + new TimeSpan(0, 0, 1); - } - - if (TestMode || Timing.TotalTime > KarmaNotificationTime) { foreach (Client client in clients) { SendKarmaNotifications(client); } - KarmaNotificationTime = Timing.TotalTime + KarmaNotificationInterval; + perSecondUpdate = DateTime.Now + new TimeSpan(0, 0, 1); } - + foreach (Client bannedClient in bannedClients) { - if (bannedClient.KarmaKickCount < KicksBeforeBan) + bannedClient.KarmaKickCount++; + if (bannedClient.KarmaKickCount <= KicksBeforeBan) { GameMain.Server.KickClient(bannedClient, $"KarmaKicked~[banthreshold]={(int)KickBanThreshold}", resetKarma: true); } @@ -74,15 +80,17 @@ namespace Barotrauma { GameMain.Server.BanClient(bannedClient, $"KarmaBanned~[banthreshold]={(int)KickBanThreshold}", duration: TimeSpan.FromSeconds(GameMain.Server.ServerSettings.AutoBanTime)); } - bannedClient.KarmaKickCount++; } } private void SendKarmaNotifications(Client client, string debugKarmaChangeReason = "") { + //send a notification about karma changing if the karma has changed by x% within the last second + var clientMemory = GetClientMemory(client); float karmaChange = client.Karma - clientMemory.PreviousNotifiedKarma; - if (Math.Abs(karmaChange) > KarmaNotificationInterval || (TestMode && Math.Abs(karmaChange) > 2.0f)) + if (Math.Abs(karmaChange) > 1.0f && + (TestMode || Math.Abs(karmaChange) / clientMemory.PreviousNotifiedKarma > KarmaNotificationInterval / 100.0f)) { if (TestMode) { @@ -102,8 +110,8 @@ namespace Barotrauma { GameMain.Server.SendDirectChatMessage(TextManager.Get(karmaChange < 0 ? "KarmaDecreasedUnknownAmount" : "KarmaIncreasedUnknownAmount"), client); } - clientMemory.PreviousNotifiedKarma = client.Karma; } + clientMemory.PreviousNotifiedKarma = client.Karma; } private void UpdateClient(Client client, float deltaTime) @@ -156,7 +164,7 @@ namespace Barotrauma AdjustKarma(client.Character, karmaDecrease, "Disconnected excessive number of wires"); } } - + if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedConstruction != null) { if (client.Character.SelectedConstruction.GetComponent() != null) @@ -180,6 +188,18 @@ namespace Barotrauma } } + public void OnRoundEnded() + { + if (ResetKarmaBetweenRounds) + { + clientMemories.Clear(); + foreach (Client client in GameMain.Server.ConnectedClients) + { + client.Karma = Math.Max(50.0f, client.Karma); + } + } + } + public void OnClientDisconnected(Client client) { clientMemories.Remove(client); @@ -210,7 +230,41 @@ namespace Barotrauma isEnemy = true; } } - + + bool targetIsHusk = target.CharacterHealth?.GetAffliction("huskinfection")?.State == AfflictionHusk.InfectionState.Active; + bool attackerIsHusk = attacker.CharacterHealth?.GetAffliction("huskinfection")?.State == AfflictionHusk.InfectionState.Active; + //huskified characters count as enemies to healthy characters and vice versa + if (targetIsHusk != attackerIsHusk) { isEnemy = true; } + + if (appliedAfflictions != null) + { + foreach (Affliction affliction in appliedAfflictions) + { + if (MathUtils.NearlyEqual(affliction.Prefab.KarmaChangeOnApplied, 0.0f)) { continue; } + damage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength; + } + } + + Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == target); + if (damage > 0 && targetClient != null) + { + var targetMemory = GetClientMemory(targetClient); + targetMemory.LastAttackTime[attacker] = Timing.TotalTime; + } + + Client attackerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == attacker); + if (attackerClient != null) + { + //if the attacker has been attacked by the target within the last x seconds, ignore the damage + //(= no karma penalty from retaliating against someone who attacked you) + var attackerMemory = GetClientMemory(attackerClient); + if (attackerMemory.LastAttackTime.ContainsKey(target) && + attackerMemory.LastAttackTime[target] > Timing.TotalTime - AllowedRetaliationTime) + { + damage = Math.Min(damage, 0); + } + } + //attacking/healing clowns has a smaller effect on karma if (target.HasEquippedItem("clownmask") && target.HasEquippedItem("clowncostume")) @@ -218,15 +272,21 @@ namespace Barotrauma damage *= 0.5f; } - if (appliedAfflictions != null) + //smaller karma penalty for attacking someone who's aiming with a weapon + if (damage > 0.0f && + target.IsKeyDown(InputType.Aim) && + target.SelectedItems.Any(it => it != null && (it.GetComponent() != null || it.GetComponent() != null))) { - foreach (Affliction affliction in appliedAfflictions) - { - if (MathUtils.NearlyEqual(affliction.Prefab.KarmaChangeOnApplied, 0.0f)) { continue; } - damage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength; - } + damage *= 0.5f; } + //damage scales according to the karma of the target + //(= smaller karma penalty from attacking someone who has a low karma) + if (damage > 0 && targetClient != null) + { + damage *= MathUtils.InverseLerp(0.0f, 50.0f, targetClient.Karma); + } + if (isEnemy) { if (damage > 0) diff --git a/Barotrauma/BarotraumaServer/Source/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs b/Barotrauma/BarotraumaServer/Source/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs index 2f78ec9e4..05e8e4c4d 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs @@ -37,8 +37,8 @@ namespace Barotrauma.Networking Retries = 0; SteamID = null; PasswordSalt = null; - UpdateTime = Timing.TotalTime; - TimeOut = 20.0; + UpdateTime = Timing.TotalTime+Timing.Step*3.0; + TimeOut = NetworkConnection.TimeoutThreshold; AuthSessionStarted = false; } } @@ -319,7 +319,7 @@ namespace Barotrauma.Networking { if (netServer == null) { return; } - pendingClient.TimeOut = 20.0; + pendingClient.TimeOut = NetworkConnection.TimeoutThreshold; ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte(); @@ -327,6 +327,8 @@ namespace Barotrauma.Networking if (pendingClient.InitializationStep != initializationStep) return; + pendingClient.UpdateTime = Timing.TotalTime + Timing.Step; + switch (initializationStep) { case ConnectionInitialization.SteamTicketAndVersion: @@ -522,6 +524,7 @@ namespace Barotrauma.Networking } OnInitializationComplete?.Invoke(newConnection); + return; } @@ -554,6 +557,10 @@ namespace Barotrauma.Networking } NetSendResult result = netServer.SendMessage(outMsg, pendingClient.Connection, NetDeliveryMethod.ReliableUnordered); + if (result != NetSendResult.Sent && result != NetSendResult.Queued) + { + DebugConsole.NewMessage("Failed to send initialization step " + pendingClient.InitializationStep.ToString() + " to pending client: " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow); + } //DebugConsole.NewMessage("sent update to pending client: "+result); } @@ -588,7 +595,7 @@ namespace Barotrauma.Networking if (netServer == null) { return; } PendingClient pendingClient = pendingClients.Find(c => c.SteamID == steamID); - DebugConsole.NewMessage(steamID + " validation: " + status+", "+(pendingClient!=null)); + DebugConsole.Log(steamID + " validation: " + status+", "+(pendingClient!=null)); if (pendingClient == null) { @@ -653,7 +660,11 @@ namespace Barotrauma.Networking lidgrenMsg.Write((UInt16)length); lidgrenMsg.Write(msgData, 0, length); - netServer.SendMessage(lidgrenMsg, lidgrenConn.NetConnection, lidgrenDeliveryMethod); + NetSendResult result = netServer.SendMessage(lidgrenMsg, lidgrenConn.NetConnection, lidgrenDeliveryMethod); + if (result != NetSendResult.Sent && result != NetSendResult.Queued) + { + DebugConsole.NewMessage("Failed to send message to "+conn.Name+": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow); + } } public override void Disconnect(NetworkConnection conn,string msg=null) diff --git a/Barotrauma/BarotraumaServer/Source/Networking/Primitives/Peers/Server/SteamP2PServerPeer.cs b/Barotrauma/BarotraumaServer/Source/Networking/Primitives/Peers/Server/SteamP2PServerPeer.cs index afa84a137..eb0253df4 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/Primitives/Peers/Server/SteamP2PServerPeer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/Primitives/Peers/Server/SteamP2PServerPeer.cs @@ -40,14 +40,14 @@ namespace Barotrauma.Networking Retries = 0; SteamID = steamId; PasswordSalt = null; - UpdateTime = Timing.TotalTime; - TimeOut = 20.0; + UpdateTime = Timing.TotalTime+Timing.Step*3.0; + TimeOut = NetworkConnection.TimeoutThreshold; AuthSessionStarted = false; } public void Heartbeat() { - TimeOut = 5.0; + TimeOut = NetworkConnection.TimeoutThreshold; } } @@ -228,7 +228,7 @@ namespace Barotrauma.Networking if (isServerMessage) { - DebugConsole.ThrowError("got server message from" + senderSteamId.ToString()); + DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString()); return; } @@ -346,7 +346,11 @@ namespace Barotrauma.Networking NetOutgoingMessage outMsg = netServer.CreateMessage(); outMsg.Write(OwnerSteamID); outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep | PacketHeader.IsServerMessage)); - netServer.SendMessage(outMsg, netConnection, NetDeliveryMethod.ReliableUnordered); + NetSendResult result = netServer.SendMessage(outMsg, netConnection, NetDeliveryMethod.ReliableUnordered); + if (result != NetSendResult.Sent && result != NetSendResult.Queued) + { + DebugConsole.NewMessage("Failed to send connection confirmation message to owner: " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow); + } break; case NetConnectionStatus.Disconnected: DebugConsole.NewMessage("Owner disconnected: closing the server..."); @@ -360,7 +364,7 @@ namespace Barotrauma.Networking { if (netServer == null) { return; } - pendingClient.TimeOut = 20.0; + pendingClient.TimeOut = NetworkConnection.TimeoutThreshold; ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte(); @@ -368,6 +372,8 @@ namespace Barotrauma.Networking if (pendingClient.InitializationStep != initializationStep) return; + pendingClient.UpdateTime = Timing.TotalTime+Timing.Step; + switch (initializationStep) { case ConnectionInitialization.SteamTicketAndVersion: @@ -549,6 +555,10 @@ namespace Barotrauma.Networking if (netConnection != null) { NetSendResult result = netServer.SendMessage(outMsg, netConnection, NetDeliveryMethod.ReliableUnordered); + if (result != NetSendResult.Sent && result != NetSendResult.Queued) + { + DebugConsole.NewMessage("Failed to send initialization step " + pendingClient.InitializationStep.ToString() + " to pending client: " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow); + } } } @@ -609,7 +619,11 @@ namespace Barotrauma.Networking lidgrenMsg.Write((UInt16)length); lidgrenMsg.Write(msgData, 0, length); - netServer.SendMessage(lidgrenMsg, netConnection, lidgrenDeliveryMethod); + NetSendResult result = netServer.SendMessage(lidgrenMsg, netConnection, lidgrenDeliveryMethod); + if (result != NetSendResult.Sent && result != NetSendResult.Queued) + { + DebugConsole.NewMessage("Failed to send message to " + conn.Name + ": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow); + } } private void SendDisconnectMessage(UInt64 steamId, string msg) @@ -622,7 +636,11 @@ namespace Barotrauma.Networking lidgrenMsg.Write((byte)(PacketHeader.IsDisconnectMessage | PacketHeader.IsServerMessage)); lidgrenMsg.Write(msg); - netServer.SendMessage(lidgrenMsg, netConnection, NetDeliveryMethod.ReliableUnordered); + NetSendResult result = netServer.SendMessage(lidgrenMsg, netConnection, NetDeliveryMethod.ReliableUnordered); + if (result != NetSendResult.Sent && result != NetSendResult.Queued) + { + DebugConsole.NewMessage("Failed to send disconnect message to " + Steam.SteamManager.SteamIDUInt64ToString(steamId) + ": " + result.ToString(), Microsoft.Xna.Framework.Color.Yellow); + } } private void Disconnect(NetworkConnection conn, string msg, bool sendDisconnectMessage) diff --git a/Barotrauma/BarotraumaServer/Source/Networking/RespawnManager.cs b/Barotrauma/BarotraumaServer/Source/Networking/RespawnManager.cs index c280a0463..0463e349e 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/RespawnManager.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/RespawnManager.cs @@ -327,7 +327,7 @@ namespace Barotrauma.Networking public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null) { - msg.WriteRangedIntegerDeprecated(0, Enum.GetNames(typeof(State)).Length, (int)CurrentState); + msg.WriteRangedInteger((int)CurrentState, 0, Enum.GetNames(typeof(State)).Length); switch (CurrentState) { diff --git a/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs b/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs index 2a6fd1307..a2999b34d 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs @@ -50,7 +50,7 @@ namespace Barotrauma.Networking outMsg.Write(HasPassword); outMsg.Write(isPublic); outMsg.WritePadBits(); - outMsg.WriteRangedIntegerDeprecated(1, 60, TickRate); + outMsg.WriteRangedInteger(TickRate, 1, 60); WriteExtraCargo(outMsg); @@ -236,8 +236,8 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen.SetLevelDifficulty(selectedLevelDifficulty); GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled); - - string[] allowedClientNameCharsStr = doc.Root.GetAttributeStringArray("AllowedClientNameChars", + + string[] defaultAllowedClientNameChars = new string[] { "32-33", "38-46", @@ -248,8 +248,16 @@ namespace Barotrauma.Networking "95-122", "192-255", "384-591", - "1024-1279" - }); + "1024-1279", + "19968-40959","13312-19903","131072-173791","173824-178207","178208-183983","63744-64255","194560-195103" //CJK + }; + + string[] allowedClientNameCharsStr = doc.Root.GetAttributeStringArray("AllowedClientNameChars", defaultAllowedClientNameChars); + if (doc.Root.GetAttributeString("AllowedClientNameChars", "") == "65-90,97-122,48-59") + { + allowedClientNameCharsStr = defaultAllowedClientNameChars; + } + foreach (string allowedClientNameCharRange in allowedClientNameCharsStr) { string[] splitRange = allowedClientNameCharRange.Split('-'); @@ -275,7 +283,7 @@ namespace Barotrauma.Networking } } - if (min > -1 && max > -1) AllowedClientNameChars.Add(new Pair(min, max)); + if (min > -1 && max > -1) { AllowedClientNameChars.Add(new Pair(min, max)); } } AllowedRandomMissionTypes = new List(); diff --git a/Barotrauma/BarotraumaServer/Source/Traitors/Goals/GoalFloodPercentOfSub.cs b/Barotrauma/BarotraumaServer/Source/Traitors/Goals/GoalFloodPercentOfSub.cs index 0cbe16075..d61125e58 100644 --- a/Barotrauma/BarotraumaServer/Source/Traitors/Goals/GoalFloodPercentOfSub.cs +++ b/Barotrauma/BarotraumaServer/Source/Traitors/Goals/GoalFloodPercentOfSub.cs @@ -24,6 +24,7 @@ namespace Barotrauma foreach (Hull hull in Hull.hullList) { if (hull.Submarine == null || hull.Submarine.IsOutpost || hull.Submarine.TeamID != Traitor.Character.TeamID) { continue; } + if (hull.Submarine == GameMain.Server?.RespawnManager?.RespawnShuttle) { continue; } ++validHullsCount; floodingAmount += hull.WaterVolume / hull.Volume; } diff --git a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml index 93a775acf..2a154bde9 100644 --- a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml +++ b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml @@ -93,6 +93,7 @@ + @@ -117,6 +118,11 @@ + + + + + diff --git a/Barotrauma/BarotraumaShared/Data/karmasettings.xml b/Barotrauma/BarotraumaShared/Data/karmasettings.xml index bbbda55fc..2f25acf5c 100644 --- a/Barotrauma/BarotraumaShared/Data/karmasettings.xml +++ b/Barotrauma/BarotraumaShared/Data/karmasettings.xml @@ -7,20 +7,22 @@ karmaincrease="0.1" karmaincreasethreshold="50" structurerepairkarmaincrease="0.05" - structuredamagekarmadecrease="0.1" + structuredamagekarmadecrease="0.08" itemrepairkarmaincrease="0.03" reactoroverheatkarmadecrease="0.5" reactormeltdownkarmadecrease="30" damageenemykarmaincrease="0.1" - damagefriendlykarmadecrease="0.2" + damagefriendlykarmadecrease="0.15" extinguishfirekarmaincrease="1" - allowedwiredisconnectionsperminute="3" + allowedwiredisconnectionsperminute="5" wiredisconnectionkarmadecrease="6.0" steersubkarmaincrease="0.15" spamfilterkarmadecrease="15" herpesthreshold="40" kickbanthreshold="1" - karmanotificationinterval="10" /> + kicksbeforeban="3" + karmanotificationinterval="15" + resetkarmabetweenrounds="true" /> + kicksbeforeban="1" + karmanotificationinterval="15" + resetkarmabetweenrounds="true" /> + kicksbeforeban="3" + karmanotificationinterval="15" + resetkarmabetweenrounds="true" /> \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedContent.projitems b/Barotrauma/BarotraumaShared/SharedContent.projitems index bdcdd6528..56ac50329 100644 --- a/Barotrauma/BarotraumaShared/SharedContent.projitems +++ b/Barotrauma/BarotraumaShared/SharedContent.projitems @@ -592,6 +592,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -602,6 +605,15 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -611,6 +623,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -635,6 +650,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -2399,6 +2417,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -2420,6 +2444,9 @@ PreserveNewest + + PreserveNewest + @@ -3593,4 +3620,4 @@ PreserveNewest - + \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs index 10cd8374d..df302f351 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -387,14 +387,17 @@ namespace Barotrauma private void UpdateIdle(float deltaTime) { - if (Character.Submarine == null && SimPosition.Y < ConvertUnits.ToSimUnits(Character.CharacterHealth.CrushDepth * 0.75f)) + if (Character.Submarine == null && + SimPosition.Y < ConvertUnits.ToSimUnits(Character.CharacterHealth.CrushDepth * 0.75f)) { //steer straight up if very deep steeringManager.SteeringManual(deltaTime, Vector2.UnitY); return; } - if (wallTarget != null) return; + SteerInsideLevel(deltaTime); + + if (wallTarget != null) { return; } if (SelectedAiTarget != null) { @@ -451,6 +454,10 @@ namespace Barotrauma } } } + else + { + SteerInsideLevel(deltaTime); + } if (escapePoint != Vector2.Zero && Vector2.DistanceSquared(Character.SimPosition, escapePoint) > 1) { SteeringManager.SteeringSeek(escapePoint); @@ -1396,6 +1403,30 @@ namespace Barotrauma AttackingLimb = null; } + private void SteerInsideLevel(float deltaTime) + { + if (Level.Loaded == null) { return; } + + Vector2 levelSimSize = new Vector2( + ConvertUnits.ToSimUnits(Level.Loaded.Size.X), + ConvertUnits.ToSimUnits(Level.Loaded.Size.Y)); + + float margin = 10.0f; + + if (SimPosition.Y < 0.0f) + { + steeringManager.SteeringManual(deltaTime, Vector2.UnitY * MathUtils.InverseLerp(0.0f, -margin, SimPosition.Y)); + } + if (SimPosition.X < 0.0f) + { + steeringManager.SteeringManual(deltaTime, Vector2.UnitX * MathUtils.InverseLerp(0.0f, -margin, SimPosition.X)); + } + if (SimPosition.X > levelSimSize.X) + { + steeringManager.SteeringManual(deltaTime, Vector2.UnitX * MathUtils.InverseLerp(levelSimSize.X, levelSimSize.X + margin, SimPosition.X)); + } + } + private int GetMinimumPassableHoleCount() { return (int)Math.Ceiling(ConvertUnits.ToDisplayUnits(colliderSize) / Structure.WallSectionSize); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs index 912084633..ec5cd01bb 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs @@ -81,7 +81,7 @@ namespace Barotrauma public override void Update(float deltaTime) { - if (DisableCrewAI || Character.IsUnconscious) return; + if (DisableCrewAI || Character.IsUnconscious || Character.Removed) { return; } float maxDistanceToSub = 3000; if (Character.Submarine != null || SelectedAiTarget?.Entity?.Submarine != null && diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs index 5e05bc20b..90ade2677 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs @@ -312,8 +312,10 @@ namespace Barotrauma Vector2 colliderBottom = character.AnimController.GetColliderBottom(); Vector2 colliderSize = collider.GetSize(); Vector2 velocity = collider.LinearVelocity; + // If the character is smaller than this, it fails to use the waypoint nodes, because they are always too high. + float minHeight = 1; // Cannot use the head position, because not all characters have head or it can be below the total height of the character - float characterHeight = colliderSize.Y + character.AnimController.ColliderHeightFromFloor; + float characterHeight = Math.Max(colliderSize.Y + character.AnimController.ColliderHeightFromFloor, minHeight); float horizontalDistance = Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X); bool isAboveFeet = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y; bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + characterHeight; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs index 952243dfb..5a5b773b8 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs @@ -132,7 +132,9 @@ namespace Barotrauma { if (Frozen) return; if (MainLimb == null) { return; } - + + levitatingCollider = true; + if (!character.AllowInput) { levitatingCollider = false; @@ -542,8 +544,6 @@ namespace Barotrauma //limbs are disabled when simple physics is enabled, no need to move them if (SimplePhysicsEnabled) { return; } - float mainLimbHeight = ColliderHeightFromFloor; - Vector2 colliderBottom = GetColliderBottom(); float movementAngle = 0.0f; @@ -569,9 +569,9 @@ namespace Barotrauma Vector2 pos = colliderBottom + Vector2.UnitY * TorsoPosition.Value; if (torso != MainLimb) + { pos.X = torso.SimPosition.X; - else - mainLimbHeight = TorsoPosition.Value; + } torso.MoveToPos(pos, TorsoMoveForce); torso.PullJointEnabled = true; @@ -591,9 +591,9 @@ namespace Barotrauma Vector2 pos = colliderBottom + Vector2.UnitY * HeadPosition.Value; if (head != MainLimb) + { pos.X = head.SimPosition.X; - else - mainLimbHeight = HeadPosition.Value; + } head.MoveToPos(pos, HeadMoveForce); head.PullJointEnabled = true; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs index d0cabdce8..532a6b480 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs @@ -1298,7 +1298,8 @@ namespace Barotrauma Vector2 colliderPos = GetColliderBottom(); - bool wasCritical = target.Vitality < 0.0f; + float prevVitality = target.Vitality; + bool wasCritical = prevVitality < 0.0f; if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) //Serverside code { @@ -1357,7 +1358,7 @@ namespace Barotrauma AfflictionPrefab.InternalDamage.Instantiate((CPRSettings.DamageSkillThreshold - skill) * CPRSettings.DamageSkillMultiplier, source: character) }, - 0.0f, true, 0.0f, character); + 0.0f, true, 0.0f, attacker: null); } if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) //Serverside code { @@ -1389,9 +1390,12 @@ namespace Barotrauma character.Info.IncreaseSkillLevel("medical", 0.5f, character.WorldPosition + Vector2.UnitY * 150.0f); SteamAchievementManager.OnCharacterRevived(target, character); lastReviveTime = (float)Timing.TotalTime; +#if SERVER + GameMain.Server?.KarmaManager?.OnCharacterHealthChanged(target, character, damage: Math.Min(prevVitality - target.Vitality, 0.0f)); +#endif //reset attacker, we don't want the character to start attacking us //because we caused a bit of damage to them during CPR - if (target.LastAttacker == character) target.LastAttacker = null; + if (target.LastAttacker == character) { target.LastAttacker = null; } } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimParams.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimParams.cs deleted file mode 100644 index 848b1f905..000000000 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimParams.cs +++ /dev/null @@ -1,150 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using System; -using System.Collections.Generic; -using System.Text; -using System.Xml; -using System.Xml.Linq; - -namespace Barotrauma -{ - partial class HumanoidAnimParams : ISerializableEntity - { - public string Name - { - get; - private set; - } - - public Dictionary SerializableProperties - { - get; - private set; - } - - public HumanoidAnimParams(string file) - { - XDocument doc = XMLExtensions.TryLoadXml(file); - if (doc == null || doc.Root == null) return; - - Name = doc.Root.Name.ToString(); - - SerializableProperties = SerializableProperty.DeserializeProperties(this, doc.Root); - } - - [Serialize(0.3f, true), Editable] - public float GetUpSpeed - { - get; - set; - } - - [Serialize(1.54f, true), Editable] - public float HeadPosition - { - get; - set; - } - - [Serialize(1.15f, true), Editable] - public float TorsoPosition - { - get; - set; - } - - - [Serialize(0.25f, true), Editable] - public float HeadLeanAmount - { - get; - set; - } - - [Serialize(0.25f, true), Editable] - public float TorsoLeanAmount - { - get; - set; - } - - [Serialize(5.0f, true), Editable] - public float CycleSpeed - { - get; - set; - } - - [Serialize(15.0f, true), Editable] - public float FootMoveStrength - { - get; - set; - } - [Serialize(20.0f, true), Editable] - public float FootRotateStrength - { - get; - set; - } - - - [Serialize("0.4,0.12", true), Editable] - public Vector2 StepSize - { - get; - set; - } - - [Serialize("0.0, 0.0", true), Editable] - public Vector2 FootMoveOffset - { - get; - set; - } - - [Serialize(10.0f, true), Editable] - public float LegCorrectionTorque - { - get; - set; - } - - [Serialize(15.0f, true), Editable] - public float ThighCorrectionTorque - { - get; - set; - } - - [Serialize("0.4, 0.15", true), Editable] - public Vector2 HandMoveAmount - { - get; - set; - } - - [Serialize("-0.15, 0.0", true), Editable] - public Vector2 HandMoveOffset - { - get; - set; - } - - [Serialize(0.7f, true), Editable] - public float HandMoveStrength - { - get; - set; - } - - [Serialize(-1.0f, true), Editable] - public float HandClampY - { - get; - set; - } - - } -} diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index 8488c6b14..c748180bf 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -1252,7 +1252,7 @@ namespace Barotrauma rayEnd.Y -= Collider.height * 0.5f + Collider.radius + ColliderHeightFromFloor*1.2f; Vector2 colliderBottomDisplay = ConvertUnits.ToDisplayUnits(GetColliderBottom()); - if (!inWater && !character.IsDead && character.Stun <= 0f && levitatingCollider && Collider.LinearVelocity.Y>-ImpactTolerance) + if (!inWater && !character.IsDead && character.Stun <= 0f && levitatingCollider && Collider.LinearVelocity.Y > -ImpactTolerance) { float closestFraction = 1.0f; Fixture closestFixture = null; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 37a406016..9c1bb73fe 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -1484,6 +1484,9 @@ namespace Barotrauma return (wall == null || !wall.CastShadow) && (door == null || door.IsOpen); } + public bool HasItem(Item item, bool requireEquipped = false) => + requireEquipped ? HasEquippedItem(item) : item.FindParentInventory(i => i.Owner == this) != null; + public bool HasEquippedItem(Item item) { for (int i = 0; i < Inventory.Capacity; i++) @@ -1595,7 +1598,7 @@ namespace Barotrauma #if CLIENT if (Screen.Selected == GameMain.SubEditorScreen) { hidden = false; } #endif - if (!CanInteract || hidden) return false; + if (!CanInteract || hidden || item.NonInteractable) return false; if (item.ParentInventory != null) { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs index 7a377e847..6ddafd729 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs @@ -764,13 +764,13 @@ namespace Barotrauma public void SetSkillLevel(string skillIdentifier, float level, Vector2 worldPos) { - if (Job == null) return; + if (Job == null) { return; } var skill = Job.Skills.Find(s => s.Identifier == skillIdentifier); if (skill == null) { Job.Skills.Add(new Skill(skillIdentifier, level)); - OnSkillChanged(skillIdentifier, 0.0f, skill.Level, worldPos); + OnSkillChanged(skillIdentifier, 0.0f, level, worldPos); } else { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/Affliction.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/Affliction.cs deleted file mode 100644 index 2e798a641..000000000 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/Affliction.cs +++ /dev/null @@ -1,141 +0,0 @@ -using Microsoft.Xna.Framework; -using System; -using System.Linq; - -namespace Barotrauma -{ - class Affliction - { - public readonly AfflictionPrefab Prefab; - - public float Strength; - - public float DamagePerSecond; - public float DamagePerSecondTimer; - public float PreviousVitalityDecrease; - - /// - /// Which character gave this affliction - /// - public Character Source; - - public Affliction(AfflictionPrefab prefab, float strength) - { - Prefab = prefab; - Strength = strength; - } - - public Affliction CreateMultiplied(float multiplier) - { - return Prefab.Instantiate(Strength * multiplier, Source); - } - - public override string ToString() - { - return "Affliction (" + Prefab.Name + ")"; - } - - public float GetVitalityDecrease(CharacterHealth characterHealth) - { - if (Strength < Prefab.ActivationThreshold) return 0.0f; - AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength); - if (currentEffect == null) return 0.0f; - if (currentEffect.MaxStrength - currentEffect.MinStrength <= 0.0f) return 0.0f; - - float currVitalityDecrease = MathHelper.Lerp( - currentEffect.MinVitalityDecrease, - currentEffect.MaxVitalityDecrease, - (Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)); - - if (currentEffect.MultiplyByMaxVitality) currVitalityDecrease *= characterHealth == null ? 100.0f : characterHealth.MaxVitality; - - return currVitalityDecrease; - } - - public float GetScreenDistortStrength() - { - if (Strength < Prefab.ActivationThreshold) return 0.0f; - AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength); - if (currentEffect == null) return 0.0f; - if (currentEffect.MaxScreenDistortStrength - currentEffect.MinScreenDistortStrength <= 0.0f) return 0.0f; - - return MathHelper.Lerp( - currentEffect.MinScreenDistortStrength, - currentEffect.MaxScreenDistortStrength, - (Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)); - } - - public float GetRadialDistortStrength() - { - if (Strength < Prefab.ActivationThreshold) return 0.0f; - AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength); - if (currentEffect == null) return 0.0f; - if (currentEffect.MaxRadialDistortStrength - currentEffect.MinRadialDistortStrength <= 0.0f) return 0.0f; - - return MathHelper.Lerp( - currentEffect.MinRadialDistortStrength, - currentEffect.MaxRadialDistortStrength, - (Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)); - } - - public float GetChromaticAberrationStrength() - { - if (Strength < Prefab.ActivationThreshold) return 0.0f; - AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength); - if (currentEffect == null) return 0.0f; - if (currentEffect.MaxChromaticAberrationStrength - currentEffect.MinChromaticAberrationStrength <= 0.0f) return 0.0f; - - return MathHelper.Lerp( - currentEffect.MinChromaticAberrationStrength, - currentEffect.MaxChromaticAberrationStrength, - (Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)); - } - - public float GetScreenBlurStrength() - { - if (Strength < Prefab.ActivationThreshold) return 0.0f; - AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength); - if (currentEffect == null) return 0.0f; - if (currentEffect.MaxScreenBlurStrength - currentEffect.MinScreenBlurStrength <= 0.0f) return 0.0f; - - return MathHelper.Lerp( - currentEffect.MinScreenBlurStrength, - currentEffect.MaxScreenBlurStrength, - (Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)); - } - - public void CalculateDamagePerSecond(float currentVitalityDecrease) - { - DamagePerSecond = Math.Max(DamagePerSecond, currentVitalityDecrease - PreviousVitalityDecrease); - if (DamagePerSecondTimer >= 1.0f) - { - DamagePerSecond = currentVitalityDecrease - PreviousVitalityDecrease; - PreviousVitalityDecrease = currentVitalityDecrease; - DamagePerSecondTimer = 0.0f; - } - } - - public virtual void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime) - { - AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength); - if (currentEffect == null) return; - - Strength += currentEffect.StrengthChange * deltaTime; - foreach (StatusEffect statusEffect in currentEffect.StatusEffects) - { - if (statusEffect.HasTargetType(StatusEffect.TargetType.Character)) - { - statusEffect.Apply(ActionType.OnActive, deltaTime, characterHealth.Character, characterHealth.Character); - } - if (targetLimb != null && statusEffect.HasTargetType(StatusEffect.TargetType.Limb)) - { - statusEffect.Apply(ActionType.OnActive, deltaTime, characterHealth.Character, targetLimb); - } - if (targetLimb != null && statusEffect.HasTargetType(StatusEffect.TargetType.AllLimbs)) - { - statusEffect.Apply(ActionType.OnActive, deltaTime, targetLimb.character, targetLimb.character.AnimController.Limbs.Cast().ToList()); - } - } - } - } -} diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionBleeding.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionBleeding.cs deleted file mode 100644 index 2059661f5..000000000 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionBleeding.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Barotrauma -{ - class AfflictionBleeding : Affliction - { - public AfflictionBleeding(AfflictionPrefab prefab, float strength) : - base(prefab, strength) - { - } - - public override void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime) - { - base.Update(characterHealth, targetLimb, deltaTime); - characterHealth.BloodlossAmount += Strength * (1.0f / 60.0f) * deltaTime; - } - } -} diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionPrefab.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionPrefab.cs deleted file mode 100644 index da334e0ff..000000000 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionPrefab.cs +++ /dev/null @@ -1,336 +0,0 @@ -using Microsoft.Xna.Framework; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Xml.Linq; - -namespace Barotrauma -{ - public static class CPRSettings - { - public static float ReviveChancePerSkill { get; private set; } - public static float ReviveChanceExponent { get; private set; } - public static float ReviveChanceMin { get; private set; } - public static float ReviveChanceMax { get; private set; } - public static float StabilizationPerSkill { get; private set; } - public static float StabilizationMin { get; private set; } - public static float StabilizationMax { get; private set; } - public static float DamageSkillThreshold { get; private set; } - public static float DamageSkillMultiplier { get; private set; } - - public static void Load(XElement element) - { - ReviveChancePerSkill = Math.Max(element.GetAttributeFloat("revivechanceperskill", 0.01f), 0.0f); - ReviveChanceExponent = Math.Max(element.GetAttributeFloat("revivechanceexponent", 2.0f), 0.0f); - ReviveChanceMin = MathHelper.Clamp(element.GetAttributeFloat("revivechancemin", 0.05f), 0.0f, 1.0f); - ReviveChanceMax = MathHelper.Clamp(element.GetAttributeFloat("revivechancemax", 0.9f), ReviveChanceMin, 1.0f); - - StabilizationPerSkill = Math.Max(element.GetAttributeFloat("stabilizationperskill", 0.01f), 0.0f); - StabilizationMin = MathHelper.Max(element.GetAttributeFloat("stabilizationmin", 0.05f), 0.0f); - StabilizationMax = MathHelper.Max(element.GetAttributeFloat("stabilizationmax", 2.0f), StabilizationMin); - - DamageSkillThreshold = MathHelper.Clamp(element.GetAttributeFloat("damageskillthreshold", 40.0f), 0.0f, 100.0f); - DamageSkillMultiplier = MathHelper.Clamp(element.GetAttributeFloat("damageskillmultiplier", 0.1f), 0.0f, 100.0f); - } - } - - class AfflictionPrefab - { - public class Effect - { - //this effect is applied when the strength is within this range - public float MinStrength, MaxStrength; - - public readonly float MinVitalityDecrease = 0.0f; - public readonly float MaxVitalityDecrease = 0.0f; - - //how much the strength of the affliction changes per second - public readonly float StrengthChange = 0.0f; - - public readonly bool MultiplyByMaxVitality; - - public float MinScreenBlurStrength, MaxScreenBlurStrength; - public float MinScreenDistortStrength, MaxScreenDistortStrength; - public float MinRadialDistortStrength, MaxRadialDistortStrength; - public float MinChromaticAberrationStrength, MaxChromaticAberrationStrength; - - public string DialogFlag; - - //statuseffects applied on the character when the affliction is active - public readonly List StatusEffects = new List(); - - public Effect(XElement element, string parentDebugName) - { - MinStrength = element.GetAttributeFloat("minstrength", 0); - MaxStrength = element.GetAttributeFloat("maxstrength", 0); - - MultiplyByMaxVitality = element.GetAttributeBool("multiplybymaxvitality", false); - - MinVitalityDecrease = element.GetAttributeFloat("minvitalitydecrease", 0.0f); - MaxVitalityDecrease = element.GetAttributeFloat("maxvitalitydecrease", 0.0f); - MaxVitalityDecrease = Math.Max(MinVitalityDecrease, MaxVitalityDecrease); - - MinScreenDistortStrength = element.GetAttributeFloat("minscreendistort", 0.0f); - MaxScreenDistortStrength = element.GetAttributeFloat("maxscreendistort", 0.0f); - MaxScreenDistortStrength = Math.Max(MinScreenDistortStrength, MaxScreenDistortStrength); - - MinRadialDistortStrength = element.GetAttributeFloat("minradialdistort", 0.0f); - MaxRadialDistortStrength = element.GetAttributeFloat("maxradialdistort", 0.0f); - MaxRadialDistortStrength = Math.Max(MinRadialDistortStrength, MaxRadialDistortStrength); - - MinChromaticAberrationStrength = element.GetAttributeFloat("minchromaticaberration", 0.0f); - MaxChromaticAberrationStrength = element.GetAttributeFloat("maxchromaticaberration", 0.0f); - MaxChromaticAberrationStrength = Math.Max(MinChromaticAberrationStrength, MaxChromaticAberrationStrength); - - MinScreenBlurStrength = element.GetAttributeFloat("minscreenblur", 0.0f); - MaxScreenBlurStrength = element.GetAttributeFloat("maxscreenblur", 0.0f); - MaxScreenBlurStrength = Math.Max(MinScreenBlurStrength, MaxScreenBlurStrength); - - DialogFlag = element.GetAttributeString("dialogflag", ""); - - StrengthChange = element.GetAttributeFloat("strengthchange", 0.0f); - - foreach (XElement subElement in element.Elements()) - { - switch (subElement.Name.ToString().ToLowerInvariant()) - { - case "statuseffect": - StatusEffects.Add(StatusEffect.Load(subElement, parentDebugName)); - break; - } - } - } - } - - public static AfflictionPrefab InternalDamage; - public static AfflictionPrefab Bleeding; - public static AfflictionPrefab Burn; - public static AfflictionPrefab OxygenLow; - public static AfflictionPrefab Bloodloss; - public static AfflictionPrefab Pressure; - public static AfflictionPrefab Stun; - public static AfflictionPrefab Husk; - - public static List List = new List(); - - //Arbitrary string that is used to identify the type of the affliction. - //Afflictions with the same type stack up, and items may be configured to cure specific types of afflictions. - public readonly string AfflictionType; - - //Does the affliction affect a specific limb or the whole character - public readonly bool LimbSpecific; - - //If not a limb-specific affliction, which limb is the indicator shown on in the health menu - //(e.g. mental health problems on head, lack of oxygen on torso...) - public readonly LimbType IndicatorLimb; - - public readonly string Identifier; - - public readonly string Name, Description; - - public readonly string CauseOfDeathDescription, SelfCauseOfDeathDescription; - - //how high the strength has to be for the affliction to take affect - public readonly float ActivationThreshold = 0.0f; - //how high the strength has to be for the affliction icon to be shown in the UI - public readonly float ShowIconThreshold = 0.0f; - public readonly float MaxStrength = 100.0f; - - public float BurnOverlayAlpha; - public float DamageOverlayAlpha; - - //steam achievement given when the affliction is removed from the controlled character - public readonly string AchievementOnRemoved; - - public readonly Sprite Icon; - public readonly Color IconColor; - - private List effects = new List(); - - private Dictionary treatmentSuitability = new Dictionary(); - - private readonly string typeName; - - private readonly ConstructorInfo constructor; - - public Dictionary TreatmentSuitability - { - get { return treatmentSuitability; } - } - - public static void LoadAll(IEnumerable filePaths) - { - foreach (string filePath in filePaths) - { - XDocument doc = XMLExtensions.TryLoadXml(filePath); - if (doc == null || doc.Root == null) continue; - - foreach (XElement element in doc.Root.Elements()) - { - switch (element.Name.ToString().ToLowerInvariant()) - { - case "internaldamage": - List.Add(InternalDamage = new AfflictionPrefab(element, typeof(Affliction))); - break; - case "bleeding": - List.Add(Bleeding = new AfflictionPrefab(element, typeof(AfflictionBleeding))); - break; - case "burn": - List.Add(Burn = new AfflictionPrefab(element, typeof(Affliction))); - break; - case "oxygenlow": - List.Add(OxygenLow = new AfflictionPrefab(element, typeof(Affliction))); - break; - case "bloodloss": - List.Add(Bloodloss = new AfflictionPrefab(element, typeof(Affliction))); - break; - case "pressure": - List.Add(Pressure = new AfflictionPrefab(element, typeof(Affliction))); - break; - case "stun": - List.Add(Stun = new AfflictionPrefab(element, typeof(Affliction))); - break; - case "husk": - case "afflictionhusk": - List.Add(Husk = new AfflictionPrefab(element, typeof(AfflictionHusk))); - break; - case "cprsettings": - CPRSettings.Load(element); - break; - default: - List.Add(new AfflictionPrefab(element)); - break; - } - } - } - - if (InternalDamage == null) DebugConsole.ThrowError("Affliction \"Internal Damage\" not defined in the affliction prefabs."); - if (Bleeding == null) DebugConsole.ThrowError("Affliction \"Bleeding\" not defined in the affliction prefabs."); - if (Burn == null) DebugConsole.ThrowError("Affliction \"Burn\" not defined in the affliction prefabs."); - if (OxygenLow == null) DebugConsole.ThrowError("Affliction \"OxygenLow\" not defined in the affliction prefabs."); - if (Bloodloss == null) DebugConsole.ThrowError("Affliction \"Bloodloss\" not defined in the affliction prefabs."); - if (Pressure == null) DebugConsole.ThrowError("Affliction \"Pressure\" not defined in the affliction prefabs."); - if (Stun == null) DebugConsole.ThrowError("Affliction \"Stun\" not defined in the affliction prefabs."); - if (Husk == null) DebugConsole.ThrowError("Affliction \"Husk\" not defined in the affliction prefabs."); - } - - public AfflictionPrefab(XElement element, Type type = null) - { - typeName = type == null ? element.Name.ToString() : type.Name; - - Identifier = element.GetAttributeString("identifier", ""); - - AfflictionType = element.GetAttributeString("type", ""); - Name = TextManager.Get("AfflictionName." + Identifier, true) ?? element.GetAttributeString("name", ""); - Description = TextManager.Get("AfflictionDescription." + Identifier, true) ?? element.GetAttributeString("description", ""); - - LimbSpecific = element.GetAttributeBool("limbspecific", false); - if (!LimbSpecific) - { - string indicatorLimbName = element.GetAttributeString("indicatorlimb", "Torso"); - if (!Enum.TryParse(indicatorLimbName, out IndicatorLimb)) - { - DebugConsole.ThrowError("Error in affliction prefab " + Name + " - limb type \"" + indicatorLimbName + "\" not found."); - } - } - - ActivationThreshold = element.GetAttributeFloat("activationthreshold", 0.0f); - ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", ActivationThreshold); - MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f); - - DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f); - BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f); - - CauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeath." + Identifier, true) ?? element.GetAttributeString("causeofdeathdescription", ""); - SelfCauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeathSelf." + Identifier, true) ?? element.GetAttributeString("selfcauseofdeathdescription", ""); - - AchievementOnRemoved = element.GetAttributeString("achievementonremoved", ""); - - foreach (XElement subElement in element.Elements()) - { - switch (subElement.Name.ToString().ToLowerInvariant()) - { - case "icon": - Icon = new Sprite(subElement); - IconColor = subElement.GetAttributeColor("color", Color.White); - break; - case "effect": - effects.Add(new Effect(subElement, Name)); - break; - } - } - - try - { - if (type == null) - { - type = Type.GetType("Barotrauma." + typeName, true, true); - if (type == null) - { - DebugConsole.ThrowError("Could not find an affliction class of the type \"" + typeName + "\"."); - return; - } - } - } - catch - { - DebugConsole.ThrowError("Could not find an affliction class of the type \"" + typeName + "\"."); - return; - } - - constructor = type.GetConstructor(new[] { typeof(AfflictionPrefab), typeof(float) }); - } - - public override string ToString() - { - return "AfflictionPrefab (" + Name + ")"; - } - - public Affliction Instantiate(float strength, Character source = null) - { - object instance = null; - try - { - instance = constructor.Invoke(new object[] { this, strength }); - } - catch (Exception ex) - { - DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString()); - } - Affliction affliction = instance as Affliction; - affliction.Source = source; - return affliction; - } - - public Effect GetActiveEffect(float currentStrength) - { - foreach (Effect effect in effects) - { - if (currentStrength > effect.MinStrength && currentStrength <= effect.MaxStrength) return effect; - } - - //if above the strength range of all effects, use the highest strength effect - Effect strongestEffect = null; - float largestStrength = currentStrength; - foreach (Effect effect in effects) - { - if (currentStrength > effect.MaxStrength && - (strongestEffect == null || effect.MaxStrength > largestStrength)) - { - strongestEffect = effect; - largestStrength = effect.MaxStrength; - } - } - return strongestEffect; - } - - public float GetTreatmentSuitability(Item item) - { - if (item == null || !treatmentSuitability.ContainsKey(item.Prefab.Identifier.ToLowerInvariant())) - { - return 0.0f; - } - return treatmentSuitability[item.Prefab.Identifier.ToLowerInvariant()]; - } - } -} diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionPsychosis.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionPsychosis.cs deleted file mode 100644 index 7b3b9066f..000000000 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/AfflictionPsychosis.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.Xna.Framework; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Barotrauma.Extensions; -using System.Xml.Linq; -using System; - -namespace Barotrauma -{ - partial class AfflictionPsychosis : Affliction - { - - public AfflictionPsychosis(AfflictionPrefab prefab, float strength) : base(prefab, strength) - { - - } - - public override void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime) - { - base.Update(characterHealth, targetLimb, deltaTime); - UpdateProjSpecific(characterHealth, targetLimb, deltaTime); - } - - partial void UpdateProjSpecific(CharacterHealth characterHealth, Limb targetLimb, float deltaTime); - } -} diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs index 59d72e9e1..fb71dbcd9 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs @@ -739,7 +739,7 @@ namespace Barotrauma msg.Write((byte)activeAfflictions.Count); foreach (Affliction affliction in activeAfflictions) { - msg.WriteRangedIntegerDeprecated(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(affliction.Prefab)); + msg.WriteRangedInteger(AfflictionPrefab.List.IndexOf(affliction.Prefab), 0, AfflictionPrefab.List.Count - 1); 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.WriteRangedIntegerDeprecated(0, limbHealths.Count - 1, limbHealths.IndexOf(limbAffliction.First)); - msg.WriteRangedIntegerDeprecated(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(limbAffliction.Second.Prefab)); + msg.WriteRangedInteger(limbHealths.IndexOf(limbAffliction.First), 0, limbHealths.Count - 1); + msg.WriteRangedInteger(AfflictionPrefab.List.IndexOf(limbAffliction.Second.Prefab), 0, AfflictionPrefab.List.Count - 1); msg.WriteRangedSingle( MathHelper.Clamp(limbAffliction.Second.Strength, 0.0f, limbAffliction.Second.Prefab.MaxStrength), 0.0f, limbAffliction.Second.Prefab.MaxStrength, 8); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs index 631a520f2..f41fa5226 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs @@ -90,6 +90,7 @@ namespace Barotrauma.Items.Components item.body.FarseerBody.CollisionCategories = Physics.CollisionProjectile; item.body.FarseerBody.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall; item.body.FarseerBody.OnCollision += OnCollision; + item.body.FarseerBody.IsBullet = true; if (!character.AnimController.InWater) { @@ -207,9 +208,9 @@ namespace Barotrauma.Items.Components private void RestoreCollision() { item.body.FarseerBody.OnCollision -= OnCollision; - item.body.CollisionCategories = Physics.CollisionItem; item.body.CollidesWith = Physics.CollisionWall; + item.body.FarseerBody.IsBullet = false; } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs index 50fd0fa90..83455c182 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs @@ -132,7 +132,7 @@ namespace Barotrauma.Items.Components return false; } - if (character.AnimController.InWater) + if (item.InWater) { if (UsableIn == UseEnvironment.Air) { @@ -160,7 +160,8 @@ namespace Barotrauma.Items.Components } else { - rayStart = ConvertUnits.ToSimUnits(item.WorldPosition); + rayStart = Submarine.LastPickedPosition + Submarine.LastPickedNormal * 0.1f; + if (item.Submarine != null) { rayStart += item.Submarine.SimPosition; } } Vector2 rayEnd = rayStart + @@ -201,12 +202,12 @@ namespace Barotrauma.Items.Components Repair(rayStart - character.Submarine.SimPosition, rayEnd - character.Submarine.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies); } - UseProjSpecific(deltaTime); + UseProjSpecific(deltaTime, rayStart); return true; } - partial void UseProjSpecific(float deltaTime); + partial void UseProjSpecific(float deltaTime, Vector2 raystart); private readonly HashSet hitCharacters = new HashSet(); private readonly List fireSourcesInRange = new List(); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs index 9c2a10c9e..28ae29b89 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs @@ -1,4 +1,5 @@ using FarseerPhysics; +using Barotrauma.Networking; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; @@ -19,7 +20,7 @@ namespace Barotrauma.Items.Components } } - partial class Controller : ItemComponent + partial class Controller : ItemComponent, IServerSerializable { //where the limbs of the user should be positioned when using the controller private List limbPositions; @@ -270,17 +271,21 @@ namespace Barotrauma.Items.Components { if (IsToggle) { - state = !state; + if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) + { + state = !state; +#if SERVER + item.CreateServerEvent(this); +#endif + } } else { item.SendSignal(0, "1", "signal_out", picker); } - #if CLIENT PlaySound(ActionType.OnUse, item.WorldPosition, picker); #endif - return true; } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerContainer.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerContainer.cs index 8008375d6..2500c23dd 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerContainer.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerContainer.cs @@ -273,14 +273,18 @@ namespace Barotrauma.Items.Components { if (connection.Name == "set_rate") { - float tempSpeed; - if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out tempSpeed)) + if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempSpeed)) { - if (!MathUtils.IsValid(tempSpeed)) return; - RechargeSpeed = MathHelper.Clamp(tempSpeed / 100.0f, 0.0f, 1.0f) * MaxRechargeSpeed; + if (!MathUtils.IsValid(tempSpeed)) { return; } + + float rechargeRate = MathHelper.Clamp(tempSpeed / 100.0f, 0.0f, 1.0f); + RechargeSpeed = rechargeRate * MaxRechargeSpeed; +#if CLIENT + rechargeSpeedSlider.BarScroll = rechargeRate; +#endif } } - if (!connection.IsPower) return; + if (!connection.IsPower) { return; } if (connection.Name == "power_in") { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs index 012e970f9..bacbdc216 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs @@ -146,6 +146,10 @@ namespace Barotrauma.Items.Components currentFixerAction = FixActions.None; #if SERVER item.CreateServerEvent(this); +#endif +#if CLIENT + repairSoundChannel?.FadeOutAndDispose(); + repairSoundChannel = null; #endif return true; } @@ -221,8 +225,8 @@ namespace Barotrauma.Items.Components if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } - float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f; - + float successFactor = requiredSkills.Count == 0 ? 1.0f : DegreeOfSuccess(CurrentFixer, requiredSkills); + //item must have been below the repair threshold for the player to get an achievement or XP for repairing it if (item.ConditionPercentage < ShowRepairUIThreshold) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs index 1416ce582..0572819bc 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs @@ -122,22 +122,7 @@ namespace Barotrauma.Items.Components public override void Update(float deltaTime, Camera cam) { -#if CLIENT - foreach (Wire wire in DisconnectedWires) - { - if (Rand.Range(0.0f, 500.0f) < 1.0f) - { - SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull); - Vector2 baseVel = new Vector2(0.0f, -100.0f); - for (int i = 0; i < 5; i++) - { - var particle = GameMain.ParticleManager.CreateParticle("spark", item.WorldPosition, - baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull); - if (particle != null) { particle.Size *= Rand.Range(0.5f, 1.0f); } - } - } - } -#endif + UpdateProjSpecific(deltaTime); if (user == null || user.SelectedConstruction != item) { @@ -150,6 +135,8 @@ namespace Barotrauma.Items.Components user.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f)); } + partial void UpdateProjSpecific(float deltaTime); + public override bool Select(Character picker) { //attaching wires to items with a body is not allowed @@ -260,8 +247,14 @@ namespace Barotrauma.Items.Components } } } + +#if CLIENT + rewireSoundChannel?.FadeOutAndDispose(); + rewireSoundChannel = null; +#endif } + public void ClientWrite(IWriteMessage msg, object[] extraData = null) { foreach (Connection connection in Connections) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs index 47d479c7b..0cd9b4373 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs @@ -160,6 +160,14 @@ namespace Barotrauma.Items.Components item.AddTag("light"); } +#if CLIENT + public override void OnScaleChanged() + { + light.SpriteScale = Vector2.One * item.Scale; + light.Position = ParentBody != null ? ParentBody.Position : item.Position; + } +#endif + public override void OnItemLoaded() { base.OnItemLoaded(); @@ -175,7 +183,6 @@ namespace Barotrauma.Items.Components UpdateOnActiveEffects(deltaTime); #if CLIENT - light.SpriteScale = Vector2.One * item.Scale; light.ParentSub = item.Submarine; if (item.Container != null) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Inventory.cs b/Barotrauma/BarotraumaShared/Source/Items/Inventory.cs index 5848bbfc7..10343a8c8 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Inventory.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Inventory.cs @@ -336,30 +336,39 @@ namespace Barotrauma } } - public Item FindItemByTag(string tag) + public Item FindItem(Func predicate, bool recursive) { - if (tag == null) return null; - return Items.FirstOrDefault(i => i != null && i.HasTag(tag)); + Item match = Items.FirstOrDefault(predicate); + if (match == null && recursive) + { + foreach (var item in Items) + { + if (item == null) { continue; } + if (item.OwnInventory != null) + { + match = item.OwnInventory.FindItem(predicate, true); + if (match != null) + { + return match; + } + } + } + } + return match; } - public Item FindItemByIdentifier(string identifier) + public Item FindItemByTag(string tag, bool recursive = false) + { + if (tag == null) { return null; } + return FindItem(i => i != null && i.HasTag(tag), recursive); + } + + public Item FindItemByIdentifier(string identifier, bool recursive = false) { if (identifier == null) return null; - return Items.FirstOrDefault(i => i != null && i.Prefab.Identifier == identifier); + return FindItem(i => i != null && i.Prefab.Identifier == identifier, recursive); } - /*public Item FindItem(string[] itemNames) - { - if (itemNames == null) return null; - - foreach (string itemName in itemNames) - { - var item = FindItem(itemName); - if (item != null) return item; - } - return null; - }*/ - public virtual void RemoveItem(Item item) { if (item == null) return; @@ -376,6 +385,7 @@ namespace Barotrauma public void SharedWrite(IWriteMessage msg, object[] extraData = null) { + msg.Write((byte)capacity); for (int i = 0; i < capacity; i++) { msg.Write((ushort)(Items[i] == null ? 0 : Items[i].ID)); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 1606e1a78..fd3bbc5a2 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -126,24 +126,6 @@ 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 { @@ -177,6 +159,13 @@ namespace Barotrauma set; } + [Editable, Serialize(false, true)] + public bool NonInteractable + { + get; + set; + } + public float ImpactTolerance { get { return Prefab.ImpactTolerance; } @@ -398,6 +387,7 @@ namespace Barotrauma } else if (!MathUtils.NearlyEqual(lastSentCondition, condition) && (condition <= 0.0f || condition >= Prefab.Health)) { + sendConditionUpdateTimer = 0.0f; conditionUpdatePending = true; } } @@ -955,7 +945,6 @@ namespace Barotrauma return CurrentHull; } - CurrentHull = Hull.FindHull(WorldPosition, CurrentHull); if (body != null && body.Enabled) { @@ -978,7 +967,23 @@ namespace Barotrauma return rootContainer; } - + + public Inventory FindParentInventory(Func predicate) + { + if (parentInventory != null) + { + if (predicate(parentInventory)) + { + return parentInventory; + } + if (parentInventory.Owner is Item owner) + { + return owner.FindParentInventory(predicate); + } + } + return null; + } + public void SetContainedItemPositions() { foreach (ItemComponent component in components) @@ -1137,6 +1142,17 @@ namespace Barotrauma return CurrentHull.WaterVolume > 0.0f && Position.Y < surfaceY; } + public void SendPendingNetworkUpdates() + { + if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsServer) { return; } + if (conditionUpdatePending) + { + GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status }); + lastSentCondition = condition; + sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval; + conditionUpdatePending = false; + } + } public override void Update(float deltaTime, Camera cam) { @@ -1153,16 +1169,10 @@ namespace Barotrauma if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { sendConditionUpdateTimer -= deltaTime; - if (conditionUpdatePending) + if (conditionUpdatePending && sendConditionUpdateTimer <= 0.0f) { - if (sendConditionUpdateTimer <= 0.0f) - { - GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status }); - lastSentCondition = condition; - sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval; - conditionUpdatePending = false; - } - } + SendPendingNetworkUpdates(); + } } ApplyStatusEffects(ActionType.Always, deltaTime, null); @@ -1878,7 +1888,7 @@ namespace Barotrauma var propertyOwner = allProperties.Find(p => p.Second == property); if (allProperties.Count > 1) { - msg.WriteRangedIntegerDeprecated(0, allProperties.Count - 1, allProperties.FindIndex(p => p.Second == property)); + msg.WriteRangedInteger(allProperties.FindIndex(p => p.Second == property), 0, allProperties.Count - 1); } object value = property.GetValue(propertyOwner.First); @@ -2141,7 +2151,8 @@ namespace Barotrauma if (element.GetAttributeBool("flippedx", false)) item.FlipX(false); if (element.GetAttributeBool("flippedy", false)) item.FlipY(false); - item.condition = element.GetAttributeFloat("condition", item.Prefab.Health); + float condition = element.GetAttributeFloat("condition", item.MaxCondition); + item.condition = MathHelper.Clamp(condition, 0, item.MaxCondition); item.lastSentCondition = item.condition; item.SetActiveSprite(); diff --git a/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs b/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs index eb0ae3a08..8fd102884 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/ItemInventory.cs @@ -1,6 +1,7 @@ using Barotrauma.Items.Components; using Barotrauma.Networking; using Microsoft.Xna.Framework; +using System; using System.Collections.Generic; namespace Barotrauma @@ -92,10 +93,10 @@ namespace Barotrauma { if (!Item.ItemList.Contains(container.Item)) { - string errorMsg = "Attempted to create a network event for an item (" + container.Item.Name + ") that hasn't been fully initialized yet."; + string errorMsg = "Attempted to create a network event for an item (" + container.Item.Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce( - "ItemInventory.CreateServerEvent:EventForUninitializedItem" + container.Item.Name + container.Item.ID, + "ItemInventory.CreateServerEvent:EventForUninitializedItem" + container.Item.Name + container.Item.ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } diff --git a/Barotrauma/BarotraumaShared/Source/Map/ItemAssemblyPrefab.cs b/Barotrauma/BarotraumaShared/Source/Map/ItemAssemblyPrefab.cs index 3e1024dfa..002befbf7 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/ItemAssemblyPrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/ItemAssemblyPrefab.cs @@ -12,10 +12,7 @@ namespace Barotrauma { private readonly XElement configElement; private readonly string configPath; - - [Serialize(false, false)] - public bool HideInMenus { get; set; } - + public List> DisplayEntities { get; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs index 3528cfee9..35c4eb5c4 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs @@ -409,7 +409,7 @@ namespace Barotrauma public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null) { LevelObject obj = extraData[0] as LevelObject; - msg.WriteRangedIntegerDeprecated(0, objects.Count, objects.IndexOf(obj)); + msg.WriteRangedInteger(objects.IndexOf(obj), 0, objects.Count); obj.ServerWrite(msg, c); } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/MapEntity.cs b/Barotrauma/BarotraumaShared/Source/Map/MapEntity.cs index 53c6e4171..4532f68be 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/MapEntity.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/MapEntity.cs @@ -344,7 +344,11 @@ namespace Barotrauma structure.Update(deltaTime, cam); } - foreach (Gap gap in Gap.GapList) + //update gaps in random order, because otherwise in rooms with multiple gaps + //the water/air will always tend to flow through the first gap in the list, + //which may lead to weird behavior like water draining down only through + //one gap in a room even if there are several + foreach (Gap gap in Gap.GapList.OrderBy(g => Rand.Int(int.MaxValue))) { gap.Update(deltaTime, cam); } @@ -562,7 +566,7 @@ namespace Barotrauma } } - [Serialize(1f, true), Editable(0.1f, 10f, DecimalCount = 3, ValueStep = 0.1f)] + [Serialize(1f, true), Editable(0.01f, 10f, DecimalCount = 3, ValueStep = 0.1f)] public virtual float Scale { get; set; } = 1; #endregion } diff --git a/Barotrauma/BarotraumaShared/Source/Map/MapEntityPrefab.cs b/Barotrauma/BarotraumaShared/Source/Map/MapEntityPrefab.cs index 732d6f999..5510fad73 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/MapEntityPrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/MapEntityPrefab.cs @@ -77,7 +77,10 @@ namespace Barotrauma get; protected set; } - + + [Serialize(false, false)] + public bool HideInMenus { get; set; } + [Serialize(false, false)] public bool Linkable { diff --git a/Barotrauma/BarotraumaShared/Source/Map/Md5Hash.cs b/Barotrauma/BarotraumaShared/Source/Map/Md5Hash.cs index e1e2c8776..f9e8be7e1 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Md5Hash.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Md5Hash.cs @@ -8,6 +8,8 @@ namespace Barotrauma { public class Md5Hash { + private static Regex removeWhitespaceRegex = new Regex(@"\s+", RegexOptions.Compiled | RegexOptions.IgnoreCase); + public string Hash { get; private set; } public string ShortHash { get; private set; } @@ -34,14 +36,13 @@ namespace Barotrauma public Md5Hash(XDocument doc) { - if (doc == null) return; - - string docString = Regex.Replace(doc.ToString(), @"\s+", ""); - + if (doc == null) { return; } + + string docString = removeWhitespaceRegex.Replace(doc.ToString(), ""); + byte[] inputBytes = Encoding.ASCII.GetBytes(docString); - - Hash = CalculateHash(inputBytes); - + + Hash = CalculateHash(inputBytes); ShortHash = GetShortHash(Hash); } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs index 7808fb626..72be3d368 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs @@ -1296,7 +1296,6 @@ namespace Barotrauma 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; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index 8331c8af6..c7ebbc0a2 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -10,6 +10,7 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; using System.Xml.Linq; using Voronoi2; @@ -82,6 +83,7 @@ namespace Barotrauma private static float lastPickedFraction; private static Vector2 lastPickedNormal; + private Task hashTask; private Md5Hash hash; private string filePath; @@ -165,10 +167,12 @@ namespace Barotrauma { get { - if (hash != null) return hash; - - XDocument doc = OpenFile(filePath); - hash = new Md5Hash(doc); + if (hash == null) + { + XDocument doc = OpenFile(filePath); + StartHashDocTask(doc); + hashTask.Wait(); + } return hash; } @@ -316,7 +320,7 @@ namespace Barotrauma DebugConsole.ThrowError("Error loading submarine " + filePath + "!", e); } - if (hash != "") + if (!string.IsNullOrWhiteSpace(hash)) { this.hash = new Md5Hash(hash); } @@ -341,6 +345,11 @@ namespace Barotrauma if (doc != null && doc.Root != null) { + if (string.IsNullOrWhiteSpace(hash)) + { + StartHashDocTask(doc); + } + displayName = TextManager.Get("Submarine.Name." + name, true); if (displayName == null || displayName.Length == 0) displayName = name; @@ -397,6 +406,18 @@ namespace Barotrauma FreeID(); } + public void StartHashDocTask(XDocument doc) + { + if (hash != null) { return; } + if (hashTask != null) { return; } + + hashTask = new Task(() => + { + hash = new Md5Hash(doc); + }); + hashTask.Start(); + } + public bool HasTag(SubmarineTag tag) { return tags.HasFlag(tag); diff --git a/Barotrauma/BarotraumaShared/Source/Networking/BanList.cs b/Barotrauma/BarotraumaShared/Source/Networking/BanList.cs index da8b03ca9..0a2c6b91a 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/BanList.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/BanList.cs @@ -18,7 +18,7 @@ namespace Barotrauma.Networking { const string SavePath = "Data/bannedplayers.txt"; - private List bannedPlayers; + private readonly List bannedPlayers; public IEnumerable BannedNames { diff --git a/Barotrauma/BarotraumaShared/Source/Networking/ChatMessage.cs b/Barotrauma/BarotraumaShared/Source/Networking/ChatMessage.cs index 984a42c28..7add8f969 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/ChatMessage.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/ChatMessage.cs @@ -233,11 +233,10 @@ namespace Barotrauma.Networking public static bool CanUseRadio(Character sender, out WifiComponent radio) { radio = null; - if (sender == null) { return false; } - var senderItem = sender.Inventory.Items.FirstOrDefault(i => i?.GetComponent() != null); - if (senderItem == null) { return false; } - radio = senderItem.GetComponent(); - return sender.HasEquippedItem(senderItem) && radio.CanTransmit(); + if (sender?.Inventory == null || sender.Removed) { return false; } + radio = sender.Inventory.Items.FirstOrDefault(i => i?.GetComponent() != null)?.GetComponent(); + if (radio?.Item == null) { return false; } + return sender.HasEquippedItem(radio.Item) && radio.CanTransmit(); } } } diff --git a/Barotrauma/BarotraumaShared/Source/Networking/ClientPermissions.cs b/Barotrauma/BarotraumaShared/Source/Networking/ClientPermissions.cs index 42163b1c3..87b3a9ade 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/ClientPermissions.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/ClientPermissions.cs @@ -69,11 +69,12 @@ namespace Barotrauma.Networking public static void LoadAll(string file) { - if (!File.Exists(file)) return; + if (!File.Exists(file)) { return; } XDocument doc = XMLExtensions.TryLoadXml(file); - if (doc == null || doc.Root == null) return; + if (doc == null || doc.Root == null) { return; } + List.Clear(); foreach (XElement element in doc.Root.Elements()) { List.Add(new PermissionPreset(element)); diff --git a/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs index e89da30e1..fff151815 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs @@ -176,9 +176,7 @@ namespace Barotrauma var spawnedEntity = entitySpawnInfo.Spawn(); if (spawnedEntity != null) { -#if SERVER - CreateNetworkEvent(spawnedEntity, false); -#endif + CreateNetworkEventProjSpecific(spawnedEntity, false); if (spawnedEntity is Item) { ((Item)spawnedEntity).Condition = ((ItemSpawnInfo)entitySpawnInfo).Condition; @@ -189,18 +187,17 @@ namespace Barotrauma while (removeQueue.Count > 0) { var removedEntity = removeQueue.Dequeue(); - -#if SERVER - if (GameMain.Server != null) + if (removedEntity is Item item) { - CreateNetworkEvent(removedEntity, true); + item.SendPendingNetworkUpdates(); } -#endif - + CreateNetworkEventProjSpecific(removedEntity, true); removedEntity.Remove(); } } + partial void CreateNetworkEventProjSpecific(Entity entity, bool remove); + public void Reset() { removeQueue.Clear(); diff --git a/Barotrauma/BarotraumaShared/Source/Networking/KarmaManager.cs b/Barotrauma/BarotraumaShared/Source/Networking/KarmaManager.cs index 18692dfb5..1730122c7 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/KarmaManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/KarmaManager.cs @@ -15,6 +15,9 @@ namespace Barotrauma public Dictionary SerializableProperties { get; private set; } + [Serialize(true, true)] + public bool ResetKarmaBetweenRounds { get; set; } + [Serialize(0.1f, true)] public float KarmaDecay { get; set; } @@ -75,13 +78,16 @@ 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; } + [Serialize(120.0f, true)] + public float AllowedRetaliationTime { get; set; } + private readonly AfflictionPrefab herpesAffliction; public Dictionary Presets = new Dictionary(); diff --git a/Barotrauma/BarotraumaShared/Source/Networking/Primitives/Message/IWriteMessage.cs b/Barotrauma/BarotraumaShared/Source/Networking/Primitives/Message/IWriteMessage.cs index 4e5e87453..653364ae1 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/Primitives/Message/IWriteMessage.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/Primitives/Message/IWriteMessage.cs @@ -17,7 +17,6 @@ namespace Barotrauma.Networking 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); diff --git a/Barotrauma/BarotraumaShared/Source/Networking/Primitives/Message/Message.cs b/Barotrauma/BarotraumaShared/Source/Networking/Primitives/Message/Message.cs index d9b84ab1d..8372bc26f 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/Primitives/Message/Message.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/Primitives/Message/Message.cs @@ -493,11 +493,6 @@ namespace Barotrauma.Networking 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); @@ -547,7 +542,7 @@ namespace Barotrauma.Networking 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); + DebugConsole.Log("Compressed message: " + LengthBytes + " to " + length); } } } @@ -632,7 +627,7 @@ namespace Barotrauma.Networking buf = new byte[decompressedData.Length]; Array.Copy(decompressedData, 0, buf, 0, decompressedData.Length); lengthBits = decompressedData.Length * 8; - DebugConsole.NewMessage("Decompressing message: " + inLength + " to " + LengthBytes); + DebugConsole.Log("Decompressing message: " + inLength + " to " + LengthBytes); } else { @@ -847,10 +842,6 @@ namespace Barotrauma.Networking 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) { diff --git a/Barotrauma/BarotraumaShared/Source/Networking/Primitives/NetworkConnection/NetworkConnection.cs b/Barotrauma/BarotraumaShared/Source/Networking/Primitives/NetworkConnection/NetworkConnection.cs index f1e377a7d..9ee8439ce 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/Primitives/NetworkConnection/NetworkConnection.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/Primitives/NetworkConnection/NetworkConnection.cs @@ -13,6 +13,8 @@ namespace Barotrauma.Networking public abstract class NetworkConnection { + public const double TimeoutThreshold = 60.0; //full minute for timeout because loading screens can take quite a while + public string Name; public UInt64 SteamID diff --git a/Barotrauma/BarotraumaShared/Source/Networking/Primitives/NetworkConnection/SteamP2PConnection.cs b/Barotrauma/BarotraumaShared/Source/Networking/Primitives/NetworkConnection/SteamP2PConnection.cs index 5b27c958d..5e1ed7c05 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/Primitives/NetworkConnection/SteamP2PConnection.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/Primitives/NetworkConnection/SteamP2PConnection.cs @@ -23,7 +23,7 @@ namespace Barotrauma.Networking public void Heartbeat() { - Timeout = 20.0; + Timeout = NetworkConnection.TimeoutThreshold; } } } diff --git a/Barotrauma/BarotraumaShared/Source/Networking/ServerInfo.cs b/Barotrauma/BarotraumaShared/Source/Networking/ServerInfo.cs deleted file mode 100644 index 475fa9e3d..000000000 --- a/Barotrauma/BarotraumaShared/Source/Networking/ServerInfo.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Barotrauma.Networking -{ - partial class ServerInfo - { - public string IP; - public string Port; - public string ServerName; - public string ServerMessage; - public bool GameStarted; - public int PlayerCount; - public int MaxPlayers; - public bool HasPassword; - - public bool PingChecked; - public int Ping = -1; - - //null value means that the value isn't known (the server may be using - //an old version of the game that didn't report these values or the FetchRules query to Steam may not have finished yet) - public bool? UsingWhiteList; - public SelectionMode? ModeSelectionMode; - public SelectionMode? SubSelectionMode; - public bool? AllowSpectating; - public bool? AllowRespawn; - public YesNoMaybe? TraitorsEnabled; - public string GameMode; - - public bool? RespondedToSteamQuery = null; - - public string GameVersion; - public List ContentPackageNames - { - get; - private set; - } = new List(); - public List ContentPackageHashes - { - get; - private set; - } = new List(); - public List ContentPackageWorkshopUrls - { - get; - private set; - } = new List(); - - public bool ContentPackagesMatch(IEnumerable myContentPackages) - { - return ContentPackagesMatch(myContentPackages.Select(cp => cp.MD5hash.Hash)); - } - - public bool ContentPackagesMatch(IEnumerable myContentPackageHashes) - { - HashSet contentPackageHashes = new HashSet(ContentPackageHashes); - return contentPackageHashes.SetEquals(myContentPackageHashes); - } - } -} diff --git a/Barotrauma/BarotraumaShared/Source/StatusEffects/PropertyConditional.cs b/Barotrauma/BarotraumaShared/Source/StatusEffects/PropertyConditional.cs index 5239d9717..165e3537b 100644 --- a/Barotrauma/BarotraumaShared/Source/StatusEffects/PropertyConditional.cs +++ b/Barotrauma/BarotraumaShared/Source/StatusEffects/PropertyConditional.cs @@ -20,7 +20,8 @@ namespace Barotrauma SpeciesName, HasTag, HasStatusTag, - Affliction + Affliction, + EntityType } public enum Comparison @@ -161,15 +162,18 @@ namespace Barotrauma { case ConditionType.PropertyValue: SerializableProperty property; + if (target?.SerializableProperties == null) { return Operator == OperatorType.NotEquals; } if (target.SerializableProperties.TryGetValue(AttributeName, out property)) { return Matches(target, property); } return false; case ConditionType.Name: + if (target == null) { return Operator == OperatorType.NotEquals; } return (Operator == OperatorType.Equals) == (target.Name == valStr); case ConditionType.HasTag: { + if (target == null) { return Operator == OperatorType.NotEquals; } string[] readTags = valStr.Split(','); int matches = 0; foreach (string tag in readTags) @@ -179,6 +183,8 @@ namespace Barotrauma return Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0; } case ConditionType.HasStatusTag: + if (target == null) { return Operator == OperatorType.NotEquals; } + List durations = StatusEffect.DurationList.FindAll(d => d.Targets.Contains(target)); List delays = DelayedEffect.DelayList.FindAll(d => d.Targets.Contains(target)); @@ -218,10 +224,29 @@ namespace Barotrauma } return success; case ConditionType.SpeciesName: + if (target == null) { return Operator == OperatorType.NotEquals; } Character targetCharacter = target as Character; - if (targetCharacter == null) return false; + if (targetCharacter == null) { return false; } return (Operator == OperatorType.Equals) == (targetCharacter.SpeciesName == valStr); + case ConditionType.EntityType: + switch (valStr) + { + case "character": + case "Character": + return (Operator == OperatorType.Equals) == target is Character; + case "item": + case "Item": + return (Operator == OperatorType.Equals) == target is Item; + case "structure": + case "Structure": + return (Operator == OperatorType.Equals) == target is Structure; + case "null": + return (Operator == OperatorType.Equals) == (target == null); + default: + return false; + } case ConditionType.Affliction: + if (target == null) { return Operator == OperatorType.NotEquals; } if (target is Character targetChar) { var health = targetChar.CharacterHealth; diff --git a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs index 7b063568f..c855f91ac 100644 --- a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs @@ -13,6 +13,7 @@ namespace Barotrauma public Entity Entity; public List Targets; public float Timer; + public Character User; } partial class StatusEffect @@ -432,7 +433,6 @@ namespace Barotrauma case PropertyConditional.Comparison.Or: foreach (ISerializableEntity target in targets) { - if (target == null || target.SerializableProperties == null) { continue; } foreach (PropertyConditional pc in propertyConditionals) { if (!string.IsNullOrEmpty(pc.TargetItemComponentName)) @@ -449,7 +449,6 @@ namespace Barotrauma case PropertyConditional.Comparison.And: foreach (ISerializableEntity target in targets) { - if (target == null || target.SerializableProperties == null) { continue; } foreach (PropertyConditional pc in propertyConditionals) { if (!string.IsNullOrEmpty(pc.TargetItemComponentName)) @@ -516,6 +515,7 @@ namespace Barotrauma if (existingEffect != null) { existingEffect.Timer = Math.Max(existingEffect.Timer, duration); + existingEffect.User = user; return; } } @@ -554,6 +554,7 @@ namespace Barotrauma if (existingEffect != null) { existingEffect.Timer = Math.Max(existingEffect.Timer, duration); + existingEffect.User = user; return; } } @@ -607,7 +608,8 @@ namespace Barotrauma Parent = this, Timer = duration, Entity = entity, - Targets = targets + Targets = targets, + User = user }; DurationList.Add(element); @@ -641,6 +643,7 @@ namespace Barotrauma if (target is Character character) { + if (character.Removed) { continue; } character.LastDamageSource = entity; foreach (Limb limb in character.AnimController.Limbs) { @@ -651,6 +654,7 @@ namespace Barotrauma } else if (target is Limb limb) { + if (limb.character.Removed) { continue; } limb.character.DamageLimb(entity.WorldPosition, limb, new List() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source); } } @@ -669,7 +673,7 @@ namespace Barotrauma targetLimb = limb; targetCharacter = limb.character; } - if (targetCharacter != null) + if (targetCharacter != null && !targetCharacter.Removed) { float prevVitality = targetCharacter.Vitality; targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAmount); @@ -827,11 +831,13 @@ namespace Barotrauma if (target is Character character) { - character.AddDamage(character.WorldPosition, new List() { multipliedAffliction }, stun: 0.0f, playSound: false); + if (character.Removed) { continue; } + character.AddDamage(character.WorldPosition, new List() { multipliedAffliction }, stun: 0.0f, playSound: false, attacker: element.User); } else if (target is Limb limb) { - limb.character.DamageLimb(limb.WorldPosition, limb, new List() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f); + if (limb.character.Removed) { continue; } + limb.character.DamageLimb(limb.WorldPosition, limb, new List() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: element.User); } } @@ -848,12 +854,12 @@ namespace Barotrauma targetLimb = limb; targetCharacter = limb.character; } - if (targetCharacter != null) + if (targetCharacter != null && !targetCharacter.Removed) { float prevVitality = targetCharacter.Vitality; targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAffliction.Second * deltaTime); #if SERVER - GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, element.Parent.user, prevVitality - targetCharacter.Vitality); + GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, element.User, prevVitality - targetCharacter.Vitality); #endif } } @@ -861,7 +867,7 @@ namespace Barotrauma element.Timer -= deltaTime; - if (element.Timer > 0.0f) continue; + if (element.Timer > 0.0f) { continue; } DurationList.Remove(element); } } @@ -873,20 +879,17 @@ namespace Barotrauma CoroutineManager.StopCoroutines("statuseffect"); DelayedEffect.DelayList.Clear(); DurationList.Clear(); -#if CLIENT - //ActiveLoopingSounds.Clear(); -#endif } public void AddTag(string tag) { - if (tags.Contains(tag)) return; + if (tags.Contains(tag)) { return; } tags.Add(tag); } public bool HasTag(string tag) { - if (tag == null) return true; + if (tag == null) { return true; } return (tags.Contains(tag) || tags.Contains(tag.ToLowerInvariant())); } diff --git a/Barotrauma/BarotraumaShared/Source/TextManager.cs b/Barotrauma/BarotraumaShared/Source/TextManager.cs index 743d9f8d6..9f821d949 100644 --- a/Barotrauma/BarotraumaShared/Source/TextManager.cs +++ b/Barotrauma/BarotraumaShared/Source/TextManager.cs @@ -311,7 +311,16 @@ namespace Barotrauma } } - return string.Format(text, args); + try + { + return string.Format(text, args); + } + catch (FormatException) + { + string errorMsg = "Failed to format text \"" + text + "\", args: " + string.Join(", ", args); + GameAnalyticsManager.AddErrorEventOnce("TextManager.GetFormatted:FormatException", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + return text; + } } public static string FormatServerMessage(string textId) @@ -621,6 +630,8 @@ namespace Barotrauma static Regex isCJK = new Regex( @"\p{IsHangulJamo}|" + + @"\p{IsHiragana}|" + + @"\p{IsKatakana}|" + @"\p{IsCJKRadicalsSupplement}|" + @"\p{IsCJKSymbolsandPunctuation}|" + @"\p{IsEnclosedCJKLettersandMonths}|" + @@ -635,6 +646,7 @@ namespace Barotrauma /// public static bool IsCJK(string text) { + if (string.IsNullOrEmpty(text)) { return false; } return isCJK.IsMatch(text); } diff --git a/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs b/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs index 2e473befc..f891d4797 100644 --- a/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs +++ b/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs @@ -427,12 +427,8 @@ namespace Barotrauma FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { - string temppath = Path.Combine(destDirName, file.Name); - if (overwriteExisting && File.Exists(temppath)) - { - File.Delete(temppath); - } - file.CopyTo(temppath, false); + string tempPath = Path.Combine(destDirName, file.Name); + file.CopyTo(tempPath, overwriteExisting); } // If copying subdirectories, copy them and their contents to new location. @@ -440,8 +436,8 @@ namespace Barotrauma { foreach (DirectoryInfo subdir in dirs) { - string temppath = Path.Combine(destDirName, subdir.Name); - CopyFolder(subdir.FullName, temppath, copySubDirs); + string tempPath = Path.Combine(destDirName, subdir.Name); + CopyFolder(subdir.FullName, tempPath, copySubDirs, overwriteExisting); } } } diff --git a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub index 8575cc8e6..6a87d75a5 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub and b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub b/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub index 5914e7c7c..20ee3fc14 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub and b/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index 0b2142564..92da02cb2 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub and b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Humpback.sub b/Barotrauma/BarotraumaShared/Submarines/Humpback.sub index 0706b361a..d09115a11 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Humpback.sub and b/Barotrauma/BarotraumaShared/Submarines/Humpback.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub b/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub new file mode 100644 index 000000000..e80bf3664 Binary files /dev/null and b/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/KastrullDrone.sub b/Barotrauma/BarotraumaShared/Submarines/KastrullDrone.sub new file mode 100644 index 000000000..a5aa70838 Binary files /dev/null and b/Barotrauma/BarotraumaShared/Submarines/KastrullDrone.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Nautilus.sub b/Barotrauma/BarotraumaShared/Submarines/Nautilus.sub new file mode 100644 index 000000000..7aa59dae8 Binary files /dev/null and b/Barotrauma/BarotraumaShared/Submarines/Nautilus.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Orca.sub b/Barotrauma/BarotraumaShared/Submarines/Orca.sub index 12819a0ef..dd685f996 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Orca.sub and b/Barotrauma/BarotraumaShared/Submarines/Orca.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Remora.sub b/Barotrauma/BarotraumaShared/Submarines/Remora.sub index 42c99e935..048cc52a7 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Remora.sub and b/Barotrauma/BarotraumaShared/Submarines/Remora.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Selkie.sub b/Barotrauma/BarotraumaShared/Submarines/Selkie.sub index e152c08e2..276029127 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Selkie.sub and b/Barotrauma/BarotraumaShared/Submarines/Selkie.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub index 247c0ec7c..fb6c3ad05 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub and b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Venture.sub b/Barotrauma/BarotraumaShared/Submarines/Venture.sub index f6a081923..30c08d65e 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Venture.sub and b/Barotrauma/BarotraumaShared/Submarines/Venture.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 62c0fd3a9..38ea8fcc2 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,132 @@ +--------------------------------------------------------------------------------------------------------- +v0.9.3.2 +--------------------------------------------------------------------------------------------------------- + +- Fixed a networking error that prevented clients from selecting the correct submarine in multiplayer if +the settings menu had been toggled open, which caused the client to start the round with an incorrect +submarine and get kicked out from the server due to entity ID mismatch errors. +- Added Japanese translation. +- Added Polish, Spanish and Turkish NPC conversations. +- Improvements to Russian and Spanish translations. + +--------------------------------------------------------------------------------------------------------- +v0.9.3.1 +--------------------------------------------------------------------------------------------------------- + +- Fixed clients waiting an excessive amount of time before resending network messages the server has not +acknowledged receiving. Caused clients to occasionally enter a state where they can't interact with devices +or pick up items. +- Fixed fonts getting messed up when playing in Simplified Chinese. + +--------------------------------------------------------------------------------------------------------- +v0.9.3.0 +--------------------------------------------------------------------------------------------------------- + +Traitor improvements: +- Players who haven't been a traitor yet are more likely to get selected as a traitor. +- If both teams have a traitor during a combat mission, traitor win will result in the opposite team's victory. +- Fixed the order of the traitor objective chat messages. +- Fixed flooding the respawn shuttle counting as flooding a part of the submarine when doing the "flood +the submarine" traitor task. + +Multiplayer fixes: +- Fixed server not sending a condition update for an item that gets removed when its condition drops to zero. +Caused potassium and magnesium to explode continuously client-side when immersed in water. +- Fixed server list only displaying servers that are located close to the client. +- Fixed server list displaying the selected game mode incorrectly. +- Fixed Chinese server names and descriptions showing up as "?????" in the server list when not playing in Chinese. +- Fixed occasional "error while reading a message from the server" console errors when joining a server. +- Fixed clients occasionally timing out and disconnecting during the loading screens. +- Fixed character name box resetting in the server lobby when another client joins or disconnects. +- Fixed "collection was modified" error when a client who's been given control of a bot tries to use +the report buttons. +- Fixed server hanging when a client joins if the server has a very large number of submarines installed. +- Fixed a bunch of client-side console commands crashing the game if the client disconnects while a question +prompt is active and then enters something in the console. +- Fixed full SteamP2P servers not showing up on the server list regardless of the filters. +- Fixed "remove ban" and "range ban" buttons in the server settings menu doing nothing. +- Fixed switches occasionally getting desynced between clients. +- Added a tickbox for selecting/deselecting all permissions to the client management panel. +- Fixed clients with campaign management permissions not seeing the permissions of newly joined clients. +- Fixed statuseffects with a duration not having an effect on karma. +- Causing an "oxygen low" affliction decreases karma (for example when injecting large amounts of fentanyl). +- Fixed scroll values resetting in the multiplayer campaign store menu when purchasing/selling items. + +Miscellaneous improvements: +- Attempting to drop an item to an empty inventory slot that's not of the right type (e.g. trying to put +an extinguisher in the normal inventory slots), automatically moves the item to the correct slot if it's free. +- Made irrelevant items (light components, lamps, small automated pumps, etc) non-interactable in the tutorials. +- The first patient cannot be healed in the doctor tutorial until the "take the patient to the medbay" +objective has been completed. +- The docking interface does not become active in the captain tutorial when leaving the first outpost. +- Added sounds for rewiring and repairing. + +Miscellaneous fixes: +- Fixed skills not having any effect on repair durations. +- Fixed inability to enable content packages created for a version prior to 0.9.2.0 (ones that have the +content package in the Data/ContentPackages folder instead of in the mod folder). +- Use a welding tool icon to indicate damaged walls in the mechanic tutorial, because the generic repair +icon caused some players to think the wall needs to be repaired with a wrench. +- Minor changes to the instruction texts in the mechanic tutorial. +- Reduced repair durations in the engineer/mechanic tutorials. +- Fixed draggable subinventories staying visible when stunned. +- Fixed light sprites not being scaled in the sub editor when resizing lamps. +- Fixed contained items not changing their position when flipping the container (e.g. oxygen generator +with some tanks inside). +- Fixed projectiles emitting sparks when they hit a character (not just when they hit a structure). +- Fixed sonar's zoom slider not moving when autodocking zooms in. +- Fixed small creatures being unable to reach waypoints. +- Fixed bots getting stuck while trying to repair items (in practice hatches) while holding ladders. +- Hid SMG rounds and coilgun bolts from the sub editor because they're not usable by themselves, but are +spawned automatically when the SMG/coilgun is fired. +- Hid the junction box tutorial variant from the sub editor. +- Stun guns and darts can be fabricated and purchased. +- More reliable melee weapon hit detection (for example in the security tutorial it was previously very +difficult to hit the crawler after it falls to the ground). +- Enemies steer back towards the level if they end up outside the boundaries. +- Added confirmation popups when exiting the sub editor or character editor to prevent losing unsaved work. +- Fixed reactor warning lights being clickable and becoming invisible when pressed. +- Fixed CPR button becoming invisible when pressed. +- Fixed water occasionally flowing only through one gap in a hull, for example water only draining +through a hole at a one side of a room even if there is another hole at the other side of the room. +- Fixed scrollbars not resizing when filtering lists (e.g. the submarine list in the "new game" menu). +- Fixed being able to fire flamers (and other repair tools) through walls and doors. +- Fixed flamers working underwater when the water is shallow enough for the character to stand. +- Fixed battery recharge rate slider not moving when the recharge rate is set by an incoming signal +or a remote player. +- Fixed some connection panels being rewireable in vanilla subs when a wire is equipped. +- Fixed some pre-placed body armors and grenades in vanilla subs having an incorrect scale. +- Fixed crashing when a character gains a skill level on a skill that's not initially configured +for the character class. +- Fixed "Add File" dialog of the Workshop publish screen always using the mod's root folder as the file +path even if the file is in a subfolder. +- Fixed contentpackage version number not being updated when updating an older workshop item. +- Fixed subinventories closing when items are dropped into or removed from them, which made reloading +weapons or putting several items into a container cumbersome. + +--------------------------------------------------------------------------------------------------------- +v0.9.2.2 +--------------------------------------------------------------------------------------------------------- + +Karma improvements: +- Attacking someone who has just recently attacked you doesn't reduce karma. +- The karma penalty from attacking someone scales according to their karma (i.e. smaller penalty for +attacking a griefer whose karma is low). +- Damaging characters by performing CPR doesn't decrease karma. +- Fixed karma decreasing when moving a wire from a connection to another. +- Reviving characters with CPR increases karma. +- Made the default karma settings a little less strict. +- Fixed clients always getting kicked (never banned) if the "kicks before ban" karma setting is set above 0. +- Players aren't notified about their karma decreasing when it decreases "naturally" towards the neutral +value of 50. + +Bugfixes: +- Fixed mechanic tutorial crashing after the welding task when playing in Russian. +- Less restrictive client name symbol constraints. Fixes clients failing to join servers if their name +contains Chinese symbols for example. +- Fixed inability to enable/update content packages created for a version prior to 0.9.2.0 (ones that have +the content package in the Data/ContentPackages folder instead of in the mod folder). + --------------------------------------------------------------------------------------------------------- v0.9.2.1 --------------------------------------------------------------------------------------------------------- diff --git a/Barotrauma/BarotraumaShared/serversettings.xml b/Barotrauma/BarotraumaShared/serversettings.xml index 2ad8722a5..ad168d01a 100644 --- a/Barotrauma/BarotraumaShared/serversettings.xml +++ b/Barotrauma/BarotraumaShared/serversettings.xml @@ -48,5 +48,5 @@ autorestart="false" LevelDifficulty="20" AllowedRandomMissionTypes="Random,Salvage,Monster,Cargo,Combat" - AllowedClientNameChars="32-33,38-46,48-57,65-90,91-91,93-93,95-122,192-255,384-591,1024-1279" + AllowedClientNameChars="32-33,38-46,48-57,65-90,91-91,93-93,95-122,192-255,384-591,1024-1279,19968-40959,13312-19903,131072-173791,173824-178207,178208-183983,63744-64255,194560-195103" ServerMessage="" /> \ No newline at end of file diff --git a/Libraries/Facepunch.Steamworks/Client/Lobby.cs b/Libraries/Facepunch.Steamworks/Client/Lobby.cs index bae3249df..937784236 100644 --- a/Libraries/Facepunch.Steamworks/Client/Lobby.cs +++ b/Libraries/Facepunch.Steamworks/Client/Lobby.cs @@ -448,9 +448,16 @@ namespace Facepunch.Steamworks /// Array of member SteamIDs public ulong[] GetMemberIDs() { - ulong[] memIDs = new ulong[NumMembers]; - for ( int i = 0; i < NumMembers; i++ ) + int numMembers = NumMembers; + ulong[] memIDs = new ulong[numMembers]; + for ( int i = 0; i < numMembers; i++ ) { + int currNumMembers = NumMembers; + if (i >= currNumMembers) + { + Array.Resize(ref memIDs, currNumMembers); + break; + } memIDs[i] = client.native.matchmaking.GetLobbyMemberByIndex( CurrentLobby, i ); } return memIDs; diff --git a/Libraries/Facepunch.Steamworks/Client/LobbyList.cs b/Libraries/Facepunch.Steamworks/Client/LobbyList.cs index 0b4d1258f..9614057ec 100644 --- a/Libraries/Facepunch.Steamworks/Client/LobbyList.cs +++ b/Libraries/Facepunch.Steamworks/Client/LobbyList.cs @@ -41,8 +41,9 @@ namespace Facepunch.Steamworks { filter = new Filter(); filter.StringFilters.Add("appid", client.AppId.ToString()); - client.native.matchmaking.RequestLobbyList(OnLobbyList); - return; + filter.DistanceFilter = Filter.Distance.Worldwide; + //client.native.matchmaking.RequestLobbyList(OnLobbyList); + //return; } client.native.matchmaking.AddRequestLobbyListDistanceFilter((SteamNative.LobbyDistanceFilter)filter.DistanceFilter); diff --git a/Libraries/Facepunch.Steamworks/Interfaces/Workshop.Query.cs b/Libraries/Facepunch.Steamworks/Interfaces/Workshop.Query.cs index 4b26d47db..d02f351b6 100644 --- a/Libraries/Facepunch.Steamworks/Interfaces/Workshop.Query.cs +++ b/Libraries/Facepunch.Steamworks/Interfaces/Workshop.Query.cs @@ -123,7 +123,9 @@ namespace Facepunch.Steamworks void ResultCallback( SteamNative.SteamUGCQueryCompleted_t data, bool bFailed ) { if ( bFailed ) - throw new System.Exception( "bFailed!" ); + { + throw new System.Exception("Steam UGC Query failed: "+data.Result.ToString()); + } var gotFiles = 0; for ( int i = 0; i < data.NumResultsReturned; i++ ) diff --git a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Interface.cs b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Interface.cs index 85f430592..2ae617e27 100644 --- a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Interface.cs +++ b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Interface.cs @@ -349,7 +349,7 @@ namespace SteamNative int /*int*/ ISteamMatchmaking_GetNumLobbyMembers( ulong steamIDLobby ); CSteamID /*(class CSteamID)*/ ISteamMatchmaking_GetLobbyMemberByIndex( ulong steamIDLobby, int /*int*/ iMember ); IntPtr ISteamMatchmaking_GetLobbyData( ulong steamIDLobby, string /*const char **/ pchKey ); - bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ); + bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ); int /*int*/ ISteamMatchmaking_GetLobbyDataCount( ulong steamIDLobby ); bool /*bool*/ ISteamMatchmaking_GetLobbyDataByIndex( ulong steamIDLobby, int /*int*/ iLobbyData, System.Text.StringBuilder /*char **/ pchKey, int /*int*/ cchKeyBufferSize, System.Text.StringBuilder /*char **/ pchValue, int /*int*/ cchValueBufferSize ); bool /*bool*/ ISteamMatchmaking_DeleteLobbyData( ulong steamIDLobby, string /*const char **/ pchKey ); diff --git a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Linux32.cs b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Linux32.cs index e162920e8..bf0b40654 100644 --- a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Linux32.cs +++ b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Linux32.cs @@ -1125,7 +1125,7 @@ namespace SteamNative return Native.SteamAPI_ISteamMatchmaking_GetLobbyData(_ptr, steamIDLobby, pchKey); } - public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ) + public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ) { if ( _ptr == IntPtr.Zero ) throw new System.Exception( "ISteamMatchmaking _ptr is null!" ); @@ -4461,7 +4461,7 @@ namespace SteamNative [DllImport( "libsteam_api.so", CallingConvention = CallingConvention.Cdecl )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetNumLobbyMembers( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "libsteam_api.so", CallingConvention = CallingConvention.Cdecl )] internal static extern CSteamID /*(class CSteamID)*/ SteamAPI_ISteamMatchmaking_GetLobbyMemberByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iMember ); [DllImport( "libsteam_api.so", CallingConvention = CallingConvention.Cdecl )] internal static extern IntPtr SteamAPI_ISteamMatchmaking_GetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); - [DllImport( "libsteam_api.so", CallingConvention = CallingConvention.Cdecl )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ); + [DllImport( "libsteam_api.so", CallingConvention = CallingConvention.Cdecl )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ); [DllImport( "libsteam_api.so", CallingConvention = CallingConvention.Cdecl )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetLobbyDataCount( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "libsteam_api.so", CallingConvention = CallingConvention.Cdecl )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_GetLobbyDataByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iLobbyData, System.Text.StringBuilder /*char **/ pchKey, int /*int*/ cchKeyBufferSize, System.Text.StringBuilder /*char **/ pchValue, int /*int*/ cchValueBufferSize ); [DllImport( "libsteam_api.so", CallingConvention = CallingConvention.Cdecl )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_DeleteLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); diff --git a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Linux64.cs b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Linux64.cs index c42c571e7..09351963b 100644 --- a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Linux64.cs +++ b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Linux64.cs @@ -1125,7 +1125,7 @@ namespace SteamNative return Native.SteamAPI_ISteamMatchmaking_GetLobbyData(_ptr, steamIDLobby, pchKey); } - public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ) + public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ) { if ( _ptr == IntPtr.Zero ) throw new System.Exception( "ISteamMatchmaking _ptr is null!" ); @@ -4461,7 +4461,7 @@ namespace SteamNative [DllImport( "libsteam_api64.so" )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetNumLobbyMembers( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "libsteam_api64.so" )] internal static extern CSteamID /*(class CSteamID)*/ SteamAPI_ISteamMatchmaking_GetLobbyMemberByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iMember ); [DllImport( "libsteam_api64.so" )] internal static extern IntPtr SteamAPI_ISteamMatchmaking_GetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); - [DllImport( "libsteam_api64.so" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ); + [DllImport( "libsteam_api64.so" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ); [DllImport( "libsteam_api64.so" )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetLobbyDataCount( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "libsteam_api64.so" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_GetLobbyDataByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iLobbyData, System.Text.StringBuilder /*char **/ pchKey, int /*int*/ cchKeyBufferSize, System.Text.StringBuilder /*char **/ pchValue, int /*int*/ cchValueBufferSize ); [DllImport( "libsteam_api64.so" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_DeleteLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); diff --git a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Mac.cs b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Mac.cs index 640270048..099bc6053 100644 --- a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Mac.cs +++ b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Mac.cs @@ -1125,7 +1125,7 @@ namespace SteamNative return Native.SteamAPI_ISteamMatchmaking_GetLobbyData(_ptr, steamIDLobby, pchKey); } - public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ) + public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ) { if ( _ptr == IntPtr.Zero ) throw new System.Exception( "ISteamMatchmaking _ptr is null!" ); @@ -4461,7 +4461,7 @@ namespace SteamNative [DllImport( "libsteam_api.dylib" )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetNumLobbyMembers( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "libsteam_api.dylib" )] internal static extern CSteamID /*(class CSteamID)*/ SteamAPI_ISteamMatchmaking_GetLobbyMemberByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iMember ); [DllImport( "libsteam_api.dylib" )] internal static extern IntPtr SteamAPI_ISteamMatchmaking_GetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); - [DllImport( "libsteam_api.dylib" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ); + [DllImport( "libsteam_api.dylib" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ); [DllImport( "libsteam_api.dylib" )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetLobbyDataCount( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "libsteam_api.dylib" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_GetLobbyDataByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iLobbyData, System.Text.StringBuilder /*char **/ pchKey, int /*int*/ cchKeyBufferSize, System.Text.StringBuilder /*char **/ pchValue, int /*int*/ cchValueBufferSize ); [DllImport( "libsteam_api.dylib" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_DeleteLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); diff --git a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Win32.cs b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Win32.cs index 2906b17df..920e53044 100644 --- a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Win32.cs +++ b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Win32.cs @@ -1122,7 +1122,7 @@ namespace SteamNative return Native.SteamAPI_ISteamMatchmaking_GetLobbyData(_ptr, steamIDLobby, pchKey); } - public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ) + public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ) { if ( _ptr == IntPtr.Zero ) throw new System.Exception( "ISteamMatchmaking _ptr is null!" ); @@ -4427,7 +4427,7 @@ namespace SteamNative [DllImport( "steam_api.dll", CallingConvention = CallingConvention.Cdecl )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetNumLobbyMembers( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "steam_api.dll", CallingConvention = CallingConvention.Cdecl )] internal static extern CSteamID /*(class CSteamID)*/ SteamAPI_ISteamMatchmaking_GetLobbyMemberByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iMember ); [DllImport( "steam_api.dll", CallingConvention = CallingConvention.Cdecl )] internal static extern IntPtr SteamAPI_ISteamMatchmaking_GetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); - [DllImport( "steam_api.dll", CallingConvention = CallingConvention.Cdecl )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ); + [DllImport( "steam_api.dll", CallingConvention = CallingConvention.Cdecl )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ); [DllImport( "steam_api.dll", CallingConvention = CallingConvention.Cdecl )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetLobbyDataCount( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "steam_api.dll", CallingConvention = CallingConvention.Cdecl )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_GetLobbyDataByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iLobbyData, System.Text.StringBuilder /*char **/ pchKey, int /*int*/ cchKeyBufferSize, System.Text.StringBuilder /*char **/ pchValue, int /*int*/ cchValueBufferSize ); [DllImport( "steam_api.dll", CallingConvention = CallingConvention.Cdecl )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_DeleteLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); diff --git a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Win64.cs b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Win64.cs index acb0c6af0..26b19cd65 100644 --- a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Win64.cs +++ b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Platform.Win64.cs @@ -1122,7 +1122,7 @@ namespace SteamNative return Native.SteamAPI_ISteamMatchmaking_GetLobbyData(_ptr, steamIDLobby, pchKey); } - public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ) + public virtual bool /*bool*/ ISteamMatchmaking_SetLobbyData( ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ) { if ( _ptr == IntPtr.Zero ) throw new System.Exception( "ISteamMatchmaking _ptr is null!" ); @@ -4427,7 +4427,7 @@ namespace SteamNative [DllImport( "steam_api64.dll" )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetNumLobbyMembers( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "steam_api64.dll" )] internal static extern CSteamID /*(class CSteamID)*/ SteamAPI_ISteamMatchmaking_GetLobbyMemberByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iMember ); [DllImport( "steam_api64.dll" )] internal static extern IntPtr SteamAPI_ISteamMatchmaking_GetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); - [DllImport( "steam_api64.dll" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey, string /*const char **/ pchValue ); + [DllImport( "steam_api64.dll" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_SetLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, IntPtr /*const char **/ pchKey, IntPtr /*const char **/ pchValue ); [DllImport( "steam_api64.dll" )] internal static extern int /*int*/ SteamAPI_ISteamMatchmaking_GetLobbyDataCount( IntPtr ISteamMatchmaking, ulong steamIDLobby ); [DllImport( "steam_api64.dll" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_GetLobbyDataByIndex( IntPtr ISteamMatchmaking, ulong steamIDLobby, int /*int*/ iLobbyData, System.Text.StringBuilder /*char **/ pchKey, int /*int*/ cchKeyBufferSize, System.Text.StringBuilder /*char **/ pchValue, int /*int*/ cchValueBufferSize ); [DllImport( "steam_api64.dll" )] internal static extern bool /*bool*/ SteamAPI_ISteamMatchmaking_DeleteLobbyData( IntPtr ISteamMatchmaking, ulong steamIDLobby, string /*const char **/ pchKey ); diff --git a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.SteamMatchmaking.cs b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.SteamMatchmaking.cs index da740feb7..24dce1391 100644 --- a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.SteamMatchmaking.cs +++ b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.SteamMatchmaking.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.InteropServices; using System.Linq; +using System.Collections.Generic; +using System.Text; namespace SteamNative { @@ -139,7 +141,30 @@ namespace SteamNative { IntPtr string_pointer; string_pointer = platform.ISteamMatchmaking_GetLobbyData( steamIDLobby.Value, pchKey ); - return Marshal.PtrToStringAnsi( string_pointer ); + //steamworks documentation says this returns empty string, + //just gonna check for good measure + if (string_pointer == IntPtr.Zero) { return string.Empty; } + + //ugliest try-catch of my life, it's here because SOMETHING might still be up with this method! >:( + try + { + //also gonna do some manual marshalling here 'cause Mono's broken or something, idk + List bytes = new List(); + IntPtr seekPointer = string_pointer; + byte b = Marshal.ReadByte(seekPointer); + while (b != 0) + { + bytes.Add(b); + seekPointer = (IntPtr)(seekPointer.ToInt64() + sizeof(byte)); + b = Marshal.ReadByte(seekPointer); + } + + return Encoding.UTF8.GetString(bytes.ToArray()); + } + catch (Exception e) + { + throw new Exception("Failed to retrieve lobby data for key " + pchKey + ": " + e.Message + "\n" + e.StackTrace, e); + } } // bool @@ -269,12 +294,21 @@ namespace SteamNative // bool public bool SetLobbyData( CSteamID steamIDLobby /*class CSteamID*/, string pchKey /*const char **/, string pchValue /*const char **/ ) - { - return platform.ISteamMatchmaking_SetLobbyData( steamIDLobby.Value, pchKey, pchValue ); - } - - // void - public void SetLobbyGameServer( CSteamID steamIDLobby /*class CSteamID*/, uint unGameServerIP /*uint32*/, ushort unGameServerPort /*uint16*/, CSteamID steamIDGameServer /*class CSteamID*/ ) + { + byte[] pchKeyBytes = Encoding.UTF8.GetBytes(pchKey + "\0"); + byte[] pchValueBytes = Encoding.UTF8.GetBytes(pchValue + "\0"); + + GCHandle handleKey = GCHandle.Alloc(pchKeyBytes, GCHandleType.Pinned); + GCHandle handleValue = GCHandle.Alloc(pchValueBytes, GCHandleType.Pinned); + + bool retVal = platform.ISteamMatchmaking_SetLobbyData(steamIDLobby.Value, handleKey.AddrOfPinnedObject(), handleValue.AddrOfPinnedObject()); + handleKey.Free(); handleValue.Free(); + + return retVal; + } + + // void + public void SetLobbyGameServer( CSteamID steamIDLobby /*class CSteamID*/, uint unGameServerIP /*uint32*/, ushort unGameServerPort /*uint16*/, CSteamID steamIDGameServer /*class CSteamID*/ ) { platform.ISteamMatchmaking_SetLobbyGameServer( steamIDLobby.Value, unGameServerIP, unGameServerPort, steamIDGameServer.Value ); }