diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index c9c538081..a13d82f7f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -694,6 +694,7 @@ namespace Barotrauma AssignRelayToServer("simulatedlatency", false); AssignRelayToServer("simulatedloss", false); AssignRelayToServer("simulatedduplicateschance", false); + AssignRelayToServer("simulatedlongloadingtime", false); AssignRelayToServer("storeinfo", false); #endif diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs index aa17718c6..43b772d1c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement; namespace Barotrauma { @@ -21,6 +22,8 @@ namespace Barotrauma private GUIButton validateHiresButton; private GUIButton clearAllButton; + private PlayerBalanceElement? playerBalanceElement; + private List PendingHires => campaign.Map?.CurrentLocation?.HireManager?.PendingHires; private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign(ClientPermissions.ManageHires); @@ -157,23 +160,7 @@ namespace Barotrauma RelativeSpacing = 0.02f }; - var playerBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), pendingAndCrewMainGroup.RectTransform), childAnchor: Anchor.TopRight) - { - RelativeSpacing = 0.005f - }; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform), - TextManager.Get("campaignstore.balance"), font: GUIStyle.Font, textAlignment: Alignment.BottomRight) - { - AutoScaleVertical = true, - ForceUpperCase = ForceUpperCase.Yes - }; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform), - "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.TopRight) - { - AutoScaleVertical = true, - TextScale = 1.1f, - TextGetter = () => TextManager.FormatCurrency(campaign.GetBalance()) - }; + playerBalanceElement = CampaignUI.AddBalanceElement(pendingAndCrewMainGroup, new Vector2(1.0f, 0.75f / 14.0f)); var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center, parent: new GUIFrame(new RectTransform(new Vector2(1.0f, 13.25f / 14.0f), pendingAndCrewMainGroup.RectTransform) @@ -792,6 +779,10 @@ namespace Barotrauma CreateUI(); UpdateLocationView(campaign.Map.CurrentLocation, false); } + else + { + playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement); + } (GUIComponent highlightedFrame, CharacterInfo highlightedInfo) = FindHighlightedCharacter(GUI.MouseOn); if (highlightedFrame != null && highlightedInfo != null) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs index af00e06c9..4b2e186a8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs @@ -567,9 +567,9 @@ namespace Barotrauma GameMain.GameSession?.EventManager?.DrawPinnedEvent(spriteBatch); - if (HUDLayoutSettings.DebugDraw) HUDLayoutSettings.Draw(spriteBatch); + if (HUDLayoutSettings.DebugDraw) { HUDLayoutSettings.Draw(spriteBatch); } - if (GameMain.Client != null) GameMain.Client.Draw(spriteBatch); + GameMain.Client?.Draw(spriteBatch); if (Character.Controlled?.Inventory != null) { @@ -616,28 +616,46 @@ namespace Barotrauma } DrawSavingIndicator(spriteBatch); - - if (GameMain.WindowActive && !HideCursor) - { - spriteBatch.End(); - spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: SamplerStateClamp, rasterizerState: GameMain.ScissorTestEnable); - - if (GameMain.GameSession?.CrewManager is { DraggedOrderPrefab: { SymbolSprite: { } orderSprite, Color: var color }, DragOrder: true }) - { - float spriteSize = Math.Max(orderSprite.size.X, orderSprite.size.Y); - orderSprite.Draw(spriteBatch, PlayerInput.LatestMousePosition, color, orderSprite.size / 2f, scale: 32f / spriteSize * Scale); - } - - var sprite = MouseCursorSprites[MouseCursor] ?? MouseCursorSprites[CursorState.Default]; - sprite.Draw(spriteBatch, PlayerInput.LatestMousePosition, Color.White, sprite.Origin, 0f, Scale / 1.5f); - - spriteBatch.End(); - spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: SamplerState, rasterizerState: GameMain.ScissorTestEnable); - } + DrawCursor(spriteBatch); HideCursor = false; } } + public static void DrawMessageBoxesOnly(SpriteBatch spriteBatch) + { + bool anyDrawn = false; + foreach (var component in updateList) + { + component.DrawAuto(spriteBatch); + anyDrawn = true; + } + if (anyDrawn) + { + DrawCursor(spriteBatch); + } + } + + private static void DrawCursor(SpriteBatch spriteBatch) + { + if (GameMain.WindowActive && !HideCursor && MouseCursorSprites.Prefabs.Any()) + { + spriteBatch.End(); + spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: SamplerStateClamp, rasterizerState: GameMain.ScissorTestEnable); + + if (GameMain.GameSession?.CrewManager is { DraggedOrderPrefab: { SymbolSprite: { } orderSprite, Color: var color }, DragOrder: true }) + { + float spriteSize = Math.Max(orderSprite.size.X, orderSprite.size.Y); + orderSprite.Draw(spriteBatch, PlayerInput.LatestMousePosition, color, orderSprite.size / 2f, scale: 32f / spriteSize * Scale); + } + + var sprite = MouseCursorSprites[MouseCursor] ?? MouseCursorSprites[CursorState.Default]; + sprite.Draw(spriteBatch, PlayerInput.LatestMousePosition, Color.White, sprite.Origin, 0f, Scale / 1.5f); + + spriteBatch.End(); + spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: SamplerState, rasterizerState: GameMain.ScissorTestEnable); + } + } + public static void DrawBackgroundSprite(SpriteBatch spriteBatch, Sprite backgroundSprite, float aberrationStrength = 1.0f) { double aberrationT = (Timing.TotalTime * 0.5f); @@ -1204,6 +1222,17 @@ namespace Barotrauma } } + public static void UpdateGUIMessageBoxesOnly(float deltaTime) + { + GUIMessageBox.AddActiveToGUIUpdateList(); + RefreshUpdateList(); + UpdateMouseOn(); + foreach (var c in updateList) + { + c.UpdateAuto(deltaTime); + } + } + private static void UpdateMessages(float deltaTime) { lock (mutex) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIMessageBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIMessageBox.cs index 72084edbe..fb9756c54 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIMessageBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIMessageBox.cs @@ -36,6 +36,8 @@ namespace Barotrauma public string Tag { get; private set; } public bool Closed { get; private set; } + public bool DisplayInLoadingScreens; + public GUIImage Icon { get; @@ -451,6 +453,10 @@ namespace Barotrauma continue; } if (messageBox.type != type) { continue; } + if (!messageBox.DisplayInLoadingScreens && GameMain.Instance.LoadingScreenOpen) + { + continue; + } // These are handled separately in GUI.HandlePersistingElements() if (MessageBoxes[i].UserData as string == "verificationprompt") { continue; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs index 35bcab726..b8026a67c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs @@ -1,13 +1,11 @@ -using Microsoft.Xna.Framework; +using Barotrauma.Extensions; +using Barotrauma.Media; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System; using System.Collections.Generic; -using System.Xml.Linq; -using Barotrauma.Media; using System.Linq; -using Barotrauma.Extensions; -using System.Collections.Immutable; namespace Barotrauma { @@ -16,7 +14,7 @@ namespace Barotrauma private readonly Texture2D defaultBackgroundTexture, overlay; private readonly SpriteSheet decorativeGraph, decorativeMap; private Texture2D currentBackgroundTexture; - private Sprite noiseSprite; + private readonly Sprite noiseSprite; private string randText = ""; @@ -250,8 +248,8 @@ namespace Barotrauma } } } - } + GUI.DrawMessageBoxesOnly(spriteBatch); spriteBatch.End(); spriteBatch.Begin(blendState: BlendState.Additive); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs index 4cedb1806..211f69381 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Barotrauma.Extensions; using Microsoft.Xna.Framework; +using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement; namespace Barotrauma { @@ -180,6 +181,8 @@ namespace Barotrauma private const float refreshTimerMax = 3f; private float refreshTimer = 0; + private PlayerBalanceElement? playerBalanceElement; + public MedicalClinicUI(MedicalClinic clinic, GUIComponent parent) { medicalClinic = clinic; @@ -428,6 +431,7 @@ namespace Barotrauma { container.ClearChildren(); pendingHealList = null; + playerBalanceElement = null; int panelMaxWidth = (int)(GUI.xScale * (GUI.HorizontalAspectRatio < 1.4f ? 650 : 560)); GUIFrame paddedParent = new GUIFrame(new RectTransform(new Vector2(0.95f), container.RectTransform, Anchor.Center), style: null); @@ -458,19 +462,7 @@ namespace Barotrauma RelativeSpacing = 0.01f }; - GUILayoutGroup balanceLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), crewContent.RectTransform)); - GUITextBlock balanceLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), TextManager.Get("campaignstore.balance"), textAlignment: Alignment.BottomRight, font: GUIStyle.Font) - { - AutoScaleVertical = true, - ForceUpperCase = ForceUpperCase.Yes - }; - - GUITextBlock moneyLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), string.Empty, textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont) - { - TextGetter = () => TextManager.FormatCurrency(medicalClinic.GetBalance()), - AutoScaleVertical = true, - TextScale = 1.1f - }; + playerBalanceElement = CampaignUI.AddBalanceElement(crewContent, new Vector2(1f, 0.1f)); GUIFrame crewBackground = new GUIFrame(new RectTransform(Vector2.One, crewContent.RectTransform)); @@ -1050,6 +1042,10 @@ namespace Barotrauma { CreateUI(); } + else + { + playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement); + } refreshTimer += deltaTime; diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs index 9d5467427..e51c586c9 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; +using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement; namespace Barotrauma { @@ -66,6 +67,8 @@ namespace Barotrauma private Point resolutionWhenCreated; + private PlayerBalanceElement? playerBalanceElement; + private Dictionary OwnedItems { get; } = new Dictionary(); private Location.StoreInfo ActiveStore { get; set; } @@ -647,23 +650,7 @@ namespace Barotrauma }; // Player balance ------------------------------------------------ - var playerBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), shoppingCrateContent.RectTransform), childAnchor: Anchor.TopRight) - { - RelativeSpacing = 0.005f - }; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform), - TextManager.Get("campaignstore.balance"), font: GUIStyle.Font, textAlignment: Alignment.BottomRight) - { - AutoScaleVertical = true, - ForceUpperCase = ForceUpperCase.Yes - }; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform), - "", textColor: Color.White, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.TopRight) - { - AutoScaleVertical = true, - TextScale = 1.1f, - TextGetter = GetPlayerBalanceText - }; + playerBalanceElement = CampaignUI.AddBalanceElement(shoppingCrateContent, new Vector2(1.0f, 0.75f / 14.0f)); // Divider ------------------------------------------------ var dividerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f / 14.0f), shoppingCrateContent.RectTransform), style: null); @@ -695,7 +682,7 @@ namespace Barotrauma { CanBeFocused = false, TextScale = 1.1f, - TextGetter = () => IsBuying ? GetPlayerBalanceText() : GetMerchantBalanceText() + TextGetter = () => IsBuying ? CampaignUI.GetTotalBalance() : GetMerchantBalanceText() }; var totalContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true) @@ -744,8 +731,6 @@ namespace Barotrauma private LocalizedString GetMerchantBalanceText() => TextManager.FormatCurrency(ActiveStore?.Balance ?? 0); - private LocalizedString GetPlayerBalanceText() => TextManager.FormatCurrency(Balance); - private GUILayoutGroup CreateDealsGroup(GUIListBox parentList, int elementCount) { // Add 1 for the header @@ -2183,6 +2168,7 @@ namespace Barotrauma ActiveShoppingCrateList.Content.RectTransform.Children.Any(); } + private int prevBalance; private float ownedItemsUpdateTimer = 0.0f, sellableItemsFromSubUpdateTimer = 0.0f; private const float timerUpdateInterval = 1.5f; private readonly Stopwatch updateStopwatch = new Stopwatch(); @@ -2198,6 +2184,8 @@ namespace Barotrauma } else { + playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement); + // Update the owned items at short intervals and check if the interface should be refreshed ownedItemsUpdateTimer += deltaTime; if (ownedItemsUpdateTimer >= timerUpdateInterval) @@ -2234,6 +2222,16 @@ namespace Barotrauma } } } + // Refresh the interface if balance changes and the buy tab is open + if (activeTab == StoreTab.Buy) + { + int currBalance = Balance; + if (prevBalance != currBalance) + { + needsBuyingRefresh = true; + prevBalance = currBalance; + } + } if (needsItemsToSellRefresh) { RefreshItemsToSell(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs index cdba1431a..35ec743ca 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs @@ -5,6 +5,7 @@ using System.Linq; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System.Globalization; +using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement; namespace Barotrauma { @@ -45,6 +46,8 @@ namespace Barotrauma private static readonly Color indicatorColor = new Color(112, 149, 129); private Point createdForResolution; + private PlayerBalanceElement? playerBalanceElement; + private struct SubmarineDisplayContent { public GUIFrame background; @@ -125,10 +128,7 @@ namespace Barotrauma content = new GUILayoutGroup(new RectTransform(new Point(background.Rect.Width - HUDLayoutSettings.Padding * 4, background.Rect.Height - HUDLayoutSettings.Padding * 4), background.RectTransform, Anchor.Center)) { AbsoluteSpacing = (int)(HUDLayoutSettings.Padding * 1.5f) }; GUITextBlock header = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), content.RectTransform), transferService ? TextManager.Get("switchsubmarineheader") : TextManager.GetWithVariable("outpostshipyard", "[location]", GameMain.GameSession.Map.CurrentLocation.Name), font: GUIStyle.LargeFont); header.CalculateHeightFromText(0, true); - GUITextBlock credits = new GUITextBlock(new RectTransform(Vector2.One, header.RectTransform), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) - { - TextGetter = CampaignUI.GetMoney - }; + playerBalanceElement = CampaignUI.AddBalanceElement(header, new Vector2(1.0f, 1.5f)); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), content.RectTransform), style: "HorizontalLine"); @@ -256,6 +256,10 @@ namespace Barotrauma { RefreshSubmarineDisplay(true); } + else + { + playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement); + } // Input if (PlayerInput.KeyHit(Keys.Left)) @@ -270,9 +274,22 @@ namespace Barotrauma public void RefreshSubmarineDisplay(bool updateSubs) { - if (!initialized) Initialize(); - if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) CreateGUI(); - if (updateSubs) UpdateSubmarines(); + if (!initialized) + { + Initialize(); + } + if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) + { + CreateGUI(); + } + else + { + playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement); + } + if (updateSubs) + { + UpdateSubmarines(); + } if (pageIndicators != null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs index ed431aa6b..f694cb4d5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs @@ -11,6 +11,7 @@ using FarseerPhysics; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; +using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement; // ReSharper disable UnusedVariable @@ -75,6 +76,8 @@ namespace Barotrauma private bool needsRefresh = true; + private PlayerBalanceElement? playerBalanceElement; + /// /// While set to true any call to will cause the buy button to be disabled and to not update the prices. /// This is to prevent us from buying another upgrade before the server has given us the new prices and causing potential syncing issues. @@ -293,9 +296,14 @@ namespace Barotrauma * |---------------------------------------------------------------------------------------------------| */ GUILayoutGroup rightLayout = new GUILayoutGroup(rectT(0.5f, 1, topHeaderLayout), childAnchor: Anchor.TopRight); - GUILayoutGroup priceLayout = new GUILayoutGroup(rectT(1, 0.8f, rightLayout), childAnchor: Anchor.Center) { RelativeSpacing = 0.08f }; - new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.Get("CampaignStore.Balance"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right); - new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.FormatCurrency(PlayerBalance), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right) { TextGetter = () => TextManager.FormatCurrency(PlayerBalance) }; + playerBalanceElement = CampaignUI.AddBalanceElement(rightLayout, new Vector2(1.0f, 0.8f)); + if (playerBalanceElement is { } balanceElement) + { + balanceElement.TotalBalanceContainer.OnAddedToGUIUpdateList += (_) => + { + playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement); + }; + } new GUIFrame(rectT(0.5f, 0.1f, rightLayout, Anchor.BottomRight), style: "HorizontalLine") { IgnoreLayoutGroups = true }; repairButton.OnClicked = upgradeButton.OnClicked = (button, o) => diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index d980f20c5..9d9cb8ac5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -662,6 +662,8 @@ namespace Barotrauma if (!TitleScreen.PlayingSplashScreen) { SoundPlayer.Update((float)Timing.Step); + GUI.ClearUpdateList(); + GUI.UpdateGUIMessageBoxesOnly((float)Timing.Step); } if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs index e2df43900..42fc88545 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs @@ -584,19 +584,16 @@ namespace Barotrauma.Items.Components { availableCharge = 0.0f; availableCapacity = 0.0f; - if (item.Connections == null) { return; } - foreach (Connection c in item.Connections) + if (item.Connections == null || powerIn == null) { return; } + var recipients = powerIn.Recipients; + foreach (Connection recipient in recipients) { - var recipients = c.Recipients; - foreach (Connection recipient in recipients) - { - if (!recipient.IsPower || !recipient.IsOutput) { continue; } - var battery = recipient.Item?.GetComponent(); - if (battery == null) { continue; } - availableCharge += battery.Charge; - availableCapacity += battery.Capacity; - } - } + if (!recipient.IsPower || !recipient.IsOutput) { continue; } + var battery = recipient.Item?.GetComponent(); + if (battery == null || battery.Item.Condition <= 0.0f) { continue; } + availableCharge += battery.Charge; + availableCapacity += battery.Capacity; + } } /// diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs index 6224c628c..96c563ea2 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs @@ -273,10 +273,16 @@ namespace Barotrauma foreach (string tag in readTags) { string[] s = tag.Split(':'); - if (s[0] == "name") - idName = s[1]; - if (s[0] == "job") - idJob = s[1]; + switch (s[0]) + { + case "name": + idName = s[1]; + break; + case "job": + case "jobid": + idJob = s[1]; + break; + } } if (idName != null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/ItemAssemblyPrefab.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/ItemAssemblyPrefab.cs index ddc286e99..ed2f608d8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/ItemAssemblyPrefab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/ItemAssemblyPrefab.cs @@ -32,11 +32,10 @@ namespace Barotrauma base.DrawPlacing(spriteBatch, cam); foreach ((Identifier identifier, Rectangle rect) in DisplayEntities) { - var entityPrefab = MapEntityPrefab.Find(p => p.Identifier == identifier); + var entityPrefab = FindByIdentifier(identifier); + if (entityPrefab == null) { continue; } Rectangle drawRect = rect; - - drawRect.Location += placePosition != Vector2.Zero ? placePosition.ToPoint() : Submarine.MouseToWorldGrid(cam, Submarine.MainSub).ToPoint(); - + drawRect.Location += placePosition != Vector2.Zero ? placePosition.ToPoint() : Submarine.MouseToWorldGrid(cam, Submarine.MainSub).ToPoint(); entityPrefab.DrawPlacing(spriteBatch, drawRect, entityPrefab.Scale); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs index 4ebdb2c37..f9348813e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs @@ -78,7 +78,7 @@ namespace Barotrauma.Networking VoipSound = null; } - public void SetPermissions(ClientPermissions permissions, List permittedConsoleCommands) + public void SetPermissions(ClientPermissions permissions, IEnumerable permittedConsoleCommands) { List permittedCommands = new List(); foreach (string commandName in permittedConsoleCommands) @@ -92,14 +92,18 @@ namespace Barotrauma.Networking SetPermissions(permissions, permittedCommands); } - public void SetPermissions(ClientPermissions permissions, List permittedConsoleCommands) + public void SetPermissions(ClientPermissions permissions, IEnumerable permittedConsoleCommands) { if (GameMain.Client == null) { return; } Permissions = permissions; - PermittedConsoleCommands.Clear(); PermittedConsoleCommands.AddRange(permittedConsoleCommands); + PermittedConsoleCommands.Clear(); + foreach (var command in permittedConsoleCommands) + { + PermittedConsoleCommands.Add(command); + } } public void GivePermission(ClientPermissions permission) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/EntitySpawner.cs index 972c3f083..8ce5ecdea 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/EntitySpawner.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/EntitySpawner.cs @@ -1,10 +1,13 @@ using Barotrauma.Items.Components; using Barotrauma.Networking; +using System.Collections.Generic; namespace Barotrauma { partial class EntitySpawner : Entity, IServerSerializable { + public readonly List<(Entity entity, bool isRemoval)> receivedEvents = new List<(Entity entity, bool isRemoval)>(); + public void ClientEventRead(IReadMessage message, float sendingTime) { bool remove = message.ReadBoolean(); @@ -12,7 +15,6 @@ namespace Barotrauma if (remove) { ushort entityId = message.ReadUInt16(); - var entity = FindEntityByID(entityId); if (entity != null) { @@ -27,6 +29,7 @@ namespace Barotrauma { DebugConsole.Log("Received entity removal message for ID " + entityId + ". Entity with a matching ID not found."); } + receivedEvents.Add((entity, true)); } else { @@ -34,13 +37,29 @@ namespace Barotrauma { case (byte)SpawnableType.Item: var newItem = Item.ReadSpawnData(message, true); - if (newItem is Item item && item.Container?.GetComponent() != null) + if (newItem == null) { - GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none".ToIdentifier()) + ":" + item.Prefab.Identifier); + DebugConsole.ThrowError("Received an item spawn message, but spawning the item failed."); + } + else + { + if (newItem.Container?.GetComponent() != null) + { + GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none".ToIdentifier()) + ":" + newItem.Prefab.Identifier); + } + receivedEvents.Add((newItem, false)); } break; case (byte)SpawnableType.Character: - Character.ReadSpawnData(message); + var character = Character.ReadSpawnData(message); + if (character == null) + { + DebugConsole.ThrowError("Received character spawn message, but spawning the character failed."); + } + else + { + receivedEvents.Add((character, false)); + } break; default: DebugConsole.ThrowError("Received invalid entity spawn message (unknown spawnable type)"); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index 32bbbda1d..ce27b16ae 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -79,13 +79,14 @@ namespace Barotrauma.Networking Starting, WaitingForStartGameFinalize, Started, - TimedOut, Error, Interrupted } private RoundInitStatus roundInitStatus = RoundInitStatus.NotStarted; + public bool RoundStarting => roundInitStatus == RoundInitStatus.Starting || roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize; + private byte myID; private readonly List otherClients; @@ -690,11 +691,8 @@ namespace Barotrauma.Networking { ServerPacketHeader header = (ServerPacketHeader)inc.ReadByte(); - if (roundInitStatus != RoundInitStatus.Started && - roundInitStatus != RoundInitStatus.NotStarted && - roundInitStatus != RoundInitStatus.Error && - roundInitStatus != RoundInitStatus.Interrupted && - header != ServerPacketHeader.STARTGAMEFINALIZE && + if (roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize && + roundInitStatus == RoundInitStatus.Started && header != ServerPacketHeader.ENDGAME && header != ServerPacketHeader.PING_REQUEST && header != ServerPacketHeader.FILE_TRANSFER) @@ -1686,12 +1684,15 @@ namespace Barotrauma.Networking roundInitStatus = RoundInitStatus.WaitingForStartGameFinalize; DateTime? timeOut = null; + TimeSpan timeOutDuration = new TimeSpan(0, 0, seconds: 30); DateTime requestFinalizeTime = DateTime.Now; TimeSpan requestFinalizeInterval = new TimeSpan(0, 0, 2); IWriteMessage msg = new WriteOnlyMessage(); msg.Write((byte)ClientPacketHeader.REQUEST_STARTGAMEFINALIZE); clientPeer.Send(msg, DeliveryMethod.Unreliable); + GUIMessageBox interruptPrompt = null; + while (true) { try @@ -1705,11 +1706,30 @@ namespace Barotrauma.Networking clientPeer.Send(msg, DeliveryMethod.Unreliable); requestFinalizeTime = DateTime.Now + requestFinalizeInterval; } - if (DateTime.Now > timeOut) + if (DateTime.Now > timeOut && interruptPrompt == null) { - DebugConsole.ThrowError("Error while starting the round (did not receive STARTGAMEFINALIZE message from the server). Stopping the round..."); - roundInitStatus = RoundInitStatus.TimedOut; - break; + interruptPrompt = new GUIMessageBox(string.Empty, TextManager.Get("WaitingForStartGameFinalizeTakingTooLong"), + new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") }) + { + DisplayInLoadingScreens = true + }; + interruptPrompt.Buttons[0].OnClicked += (btn, userData) => + { + roundInitStatus = RoundInitStatus.Interrupted; + DebugConsole.ThrowError("Error while starting the round (did not receive STARTGAMEFINALIZE message from the server). Returning to the lobby..."); + gameStarted = true; + GameMain.NetLobbyScreen.Select(); + interruptPrompt.Close(); + interruptPrompt = null; + return true; + }; + interruptPrompt.Buttons[1].OnClicked += (btn, userData) => + { + timeOut = DateTime.Now + timeOutDuration; + interruptPrompt.Close(); + interruptPrompt = null; + return true; + }; } } else @@ -1721,7 +1741,7 @@ namespace Barotrauma.Networking } //wait for up to 30 seconds for the server to send the STARTGAMEFINALIZE message - timeOut = DateTime.Now + new TimeSpan(0, 0, seconds: 30); + timeOut = DateTime.Now + timeOutDuration; } if (!connected) @@ -1743,6 +1763,9 @@ namespace Barotrauma.Networking yield return CoroutineStatus.Running; } + interruptPrompt?.Close(); + interruptPrompt = null; + if (roundInitStatus != RoundInitStatus.Started) { if (roundInitStatus != RoundInitStatus.Interrupted) @@ -3093,7 +3116,31 @@ namespace Barotrauma.Networking protected GUIFrame inGameHUD; protected ChatBox chatBox; + public GUIButton ShowLogButton; //TODO: move to NetLobbyScreen + private bool hasPermissionToUseLogButton; + + public void UpdateLogButtonPermissions() + { + hasPermissionToUseLogButton = GameMain.Client.HasPermission(ClientPermissions.ServerLog); + UpdateLogButtonVisibility(); + } + + private void UpdateLogButtonVisibility() + { + if (ShowLogButton != null) + { + if (Screen.Selected != GameMain.GameScreen) + { + ShowLogButton.Visible = hasPermissionToUseLogButton; + } + else + { + var campaign = GameMain.GameSession?.Campaign; + ShowLogButton.Visible = hasPermissionToUseLogButton && (campaign == null || !campaign.ShowCampaignUI); + } + } + } public GUIFrame InGameHUD { @@ -3173,6 +3220,8 @@ namespace Barotrauma.Networking msgBox = GameMain.NetLobbyScreen.ChatInput; } + UpdateLogButtonVisibility(); + if (gameStarted && Screen.Selected == GameMain.GameScreen) { var controller = Character.Controlled?.SelectedConstruction?.GetComponent(); @@ -3648,6 +3697,19 @@ namespace Barotrauma.Networking errorLines.Add(e.ErrorLine); } + if (Entity.Spawner != null) + { + errorLines.Add(""); + errorLines.Add("EntitySpawner events:"); + foreach ((Entity entity, bool isRemoval) in Entity.Spawner.receivedEvents) + { + errorLines.Add( + (isRemoval ? "Remove " : "Create ") + + entity.ToString() + + " (" + entity.ID + ")"); + } + } + errorLines.Add(""); errorLines.Add("Last debug messages:"); for (int i = DebugConsole.Messages.Count - 1; i > 0 && i > DebugConsole.Messages.Count - 15; i--) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2PClientPeer.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2PClientPeer.cs index d6a96d556..2b72f5dfa 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2PClientPeer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2PClientPeer.cs @@ -153,7 +153,10 @@ namespace Barotrauma.Networking { if (!isActive) { return; } - timeout -= deltaTime; + if (GameMain.Client == null || !GameMain.Client.RoundStarting) + { + timeout -= deltaTime; + } heartbeatTimer -= deltaTime; if (initializationStep != ConnectionInitialization.Password && diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs index e3d4bff42..3a64bf0b7 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs @@ -739,9 +739,112 @@ namespace Barotrauma return TextManager.GetWithVariable("PlayerCredits", "[credits]", (GameMain.GameSession?.Campaign == null) ? "0" : string.Format(CultureInfo.InvariantCulture, "{0:N0}", GameMain.GameSession.Campaign.GetBalance())); } + public static LocalizedString GetTotalBalance() + { + return TextManager.FormatCurrency(GameMain.GameSession?.Campaign is { } campaign ? campaign.GetBalance() : 0); + } + + public static LocalizedString GetBankBalance() + { + return TextManager.FormatCurrency(GameMain.GameSession?.Campaign is { } campaign ? campaign.Bank.Balance : 0); + } + + public static LocalizedString GetWalletBalance() + { + return TextManager.FormatCurrency(GameMain.GameSession?.Campaign is { } campaign ? campaign.Wallet.Balance : 0); + } + private void UpdateMaxMissions(Location location) { hasMaxMissions = Campaign.NumberOfMissionsAtLocation(location) >= Campaign.Settings.TotalMaxMissionCount; } + + public readonly struct PlayerBalanceElement + { + public readonly bool DisplaySeparateBalances; + public readonly GUILayoutGroup ParentComponent; + public readonly GUILayoutGroup TotalBalanceContainer; + public readonly GUILayoutGroup BankBalanceContainer; + + public PlayerBalanceElement(bool displaySeparateBalances, GUILayoutGroup parentComponent, GUILayoutGroup totalBalanceContainer, GUILayoutGroup bankBalanceContainer) + { + DisplaySeparateBalances = displaySeparateBalances; + ParentComponent = parentComponent; + TotalBalanceContainer = totalBalanceContainer; + BankBalanceContainer = bankBalanceContainer; + } + + public PlayerBalanceElement(PlayerBalanceElement element, bool displaySeparateBalances) + { + DisplaySeparateBalances = displaySeparateBalances; + ParentComponent = element.ParentComponent; + TotalBalanceContainer = element.TotalBalanceContainer; + BankBalanceContainer = element.BankBalanceContainer; + } + } + + public static PlayerBalanceElement? AddBalanceElement(GUIComponent elementParent, Vector2 relativeSize) + { + var parent = new GUILayoutGroup(new RectTransform(relativeSize, elementParent.RectTransform), isHorizontal: true, childAnchor: Anchor.TopRight); + if (GameMain.IsSingleplayer) + { + AddBalance(parent, true, TextManager.Get("campaignstore.balance"), GetTotalBalance); + return null; + } + else + { + bool displaySeparateBalances = CampaignMode.AllowedToManageWallets(); + var totalBalanceContainer = AddBalance(parent, displaySeparateBalances, TextManager.Get("campaignstore.total"), GetTotalBalance); + var bankBalanceContainer = AddBalance(parent, displaySeparateBalances, TextManager.Get("crewwallet.bank"), GetBankBalance); + AddBalance(parent, true, TextManager.Get("crewwallet.wallet"), GetWalletBalance); + var playerBalanceElement = new PlayerBalanceElement(displaySeparateBalances, parent, totalBalanceContainer, bankBalanceContainer); + parent.Recalculate(); + return playerBalanceElement; + } + + static GUILayoutGroup AddBalance(GUIComponent parent, bool visible, LocalizedString text, GUITextBlock.TextGetterHandler textGetter) + { + float balanceContainerWidth = GameMain.IsSingleplayer ? 1 : 1 / 3f; + var rt = new RectTransform(new Vector2(balanceContainerWidth, 1.0f), parent.RectTransform) + { + MaxSize = new Point(120, int.MaxValue) + }; + var balanceContainer = new GUILayoutGroup(rt, childAnchor: Anchor.TopRight) + { + RelativeSpacing = 0.005f, + Visible = visible + }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), balanceContainer.RectTransform), text, + font: GUIStyle.Font, textAlignment: Alignment.BottomRight) + { + AutoScaleVertical = true, + ForceUpperCase = ForceUpperCase.Yes + }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), balanceContainer.RectTransform), "", + textColor: Color.White, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.TopRight) + { + AutoScaleVertical = true, + TextScale = 1.1f, + TextGetter = textGetter + }; + return balanceContainer; + } + } + + public static PlayerBalanceElement? UpdateBalanceElement(PlayerBalanceElement? playerBalanceElement) + { + if (playerBalanceElement is { } balanceElement) + { + bool displaySeparateBalances = CampaignMode.AllowedToManageWallets(); + if (displaySeparateBalances != balanceElement.DisplaySeparateBalances) + { + balanceElement.TotalBalanceContainer.Visible = displaySeparateBalances; + balanceElement.BankBalanceContainer.Visible = displaySeparateBalances; + playerBalanceElement = new PlayerBalanceElement(balanceElement, displaySeparateBalances); + balanceElement.ParentComponent.Recalculate(); + } + } + return playerBalanceElement; + } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs index 783e29c8a..2e9797dba 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs @@ -966,7 +966,15 @@ namespace Barotrauma { OnClicked = (_, __) => { - GameMain.Client?.RequestSelectMode(ModeList.Content.GetChildIndex(ModeList.Content.GetChildByUserData(GameModePreset.Sandbox))); + if (GameMain.Client == null) { return false; } + if (GameMain.Client.GameStarted) + { + GameMain.Client.RequestRoundEnd(save: false); + } + else + { + GameMain.Client.RequestSelectMode(ModeList.Content.GetChildIndex(ModeList.Content.GetChildByUserData(GameModePreset.Sandbox))); + } return true; } }; @@ -1344,9 +1352,9 @@ namespace Barotrauma shuttleTickBox.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings) && !GameMain.Client.GameStarted; SubList.Enabled = !CampaignFrame.Visible && (GameMain.Client.ServerSettings.AllowSubVoting || GameMain.Client.HasPermission(ClientPermissions.SelectSub)); ShuttleList.Enabled = ShuttleList.ButtonEnabled = GameMain.Client.HasPermission(ClientPermissions.SelectSub) && !GameMain.Client.GameStarted; - ModeList.Enabled = GameMain.Client.ServerSettings.AllowModeVoting || GameMain.Client.HasPermission(ClientPermissions.SelectMode); + ModeList.Enabled = !GameMain.Client.GameStarted && (GameMain.Client.ServerSettings.AllowModeVoting || GameMain.Client.HasPermission(ClientPermissions.SelectMode)); LogButtons.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog); - GameMain.Client.ShowLogButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog); + GameMain.Client.UpdateLogButtonPermissions(); roundControlsHolder.Children.ForEach(c => c.IgnoreLayoutGroups = !c.Visible); roundControlsHolder.Children.ForEach(c => c.RectTransform.RelativeSize = Vector2.One); roundControlsHolder.Recalculate(); diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 9844c624a..1a43498c0 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.13.0 + 0.17.14.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index 55f0440fc..dc20efccf 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.13.0 + 0.17.14.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 085574ed2..3dd67f325 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.13.0 + 0.17.14.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index a13c31686..9a566afcb 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.13.0 + 0.17.14.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 58cb9e5a4..229732195 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.13.0 + 0.17.14.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index ff2bc8a71..61c141015 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -2452,30 +2452,10 @@ namespace Barotrauma #if DEBUG commands.Add(new Command("spamevents", "A debug command that creates a ton of entity events.", (string[] args) => { - /*foreach (Item item in Item.ItemList) + foreach (Item item in Item.ItemList) { - foreach (ItemComponent component in item.Components) - { - if (component is IServerSerializable) - { - GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.GetComponentIndex(component) }); - } - var itemContainer = item.GetComponent(); - if (itemContainer != null) - { - GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.InventoryState, 0 }); - } - - GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Status }); - } - } - foreach (Character c in Character.CharacterList) - { - GameMain.Server.CreateEntityEvent(c, new object[] { NetEntityEvent.Type.Status }); - }*/ - foreach (Hull hull in Hull.HullList) - { - GameMain.Server.CreateEntityEvent(hull); + item.TryCreateServerEventSpam(); + item.CreateStatusEvent(); } foreach (Structure wall in Structure.WallList) { diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Machines/OutpostTerminal.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Machines/OutpostTerminal.cs deleted file mode 100644 index 266398e7e..000000000 --- a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Machines/OutpostTerminal.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Barotrauma.Networking; - -namespace Barotrauma.Items.Components -{ - partial class OutpostTerminal : ItemComponent, IClientSerializable, IServerSerializable - { - public void ServerEventRead(IReadMessage msg, Client c) - { - - } - - public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null) - { - - } - } -} diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Item.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Item.cs index fec0329b1..32f261856 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Items/Item.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Items/Item.cs @@ -373,5 +373,20 @@ namespace Barotrauma if (!ic.ValidateEventData(eventData)) { throw new Exception($"Component event creation failed: {typeof(T).Name}.{nameof(ItemComponent.ValidateEventData)} returned false"); } GameMain.Server.CreateEntityEvent(this, eventData); } + +#if DEBUG + public void TryCreateServerEventSpam() + { + if (GameMain.Server == null) { return; } + + foreach (ItemComponent ic in components) + { + if (!(ic is IServerSerializable)) { continue; } + var eventData = new ComponentStateEventData(ic, ic.ServerGetEventData()); + if (!ic.ValidateEventData(eventData)) { continue; } + GameMain.Server.CreateEntityEvent(this, eventData); + } + } +#endif } } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Client.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Client.cs index b9daadc66..5a31dc4cf 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Client.cs @@ -160,10 +160,14 @@ namespace Barotrauma.Networking return Connection.EndpointMatches(endPoint); } - public void SetPermissions(ClientPermissions permissions, List permittedConsoleCommands) + public void SetPermissions(ClientPermissions permissions, IEnumerable permittedConsoleCommands) { this.Permissions = permissions; - this.PermittedConsoleCommands = new List(permittedConsoleCommands); + this.PermittedConsoleCommands.Clear(); + foreach (var command in permittedConsoleCommands) + { + this.PermittedConsoleCommands.Add(command); + } } public void GivePermission(ClientPermissions permission) diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/EntitySpawner.cs index 266600615..146ae81c0 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/EntitySpawner.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/EntitySpawner.cs @@ -15,11 +15,14 @@ namespace Barotrauma if (GameMain.Server == null || spawnOrRemove?.Entity == null) { return; } GameMain.Server.CreateEntityEvent(this, spawnOrRemove); - if (spawnOrRemove.Entity is Character { Info: { } } character) + if (spawnOrRemove is SpawnEntity) { - foreach (var statKey in character.Info.SavedStatValues.Keys) + if (spawnOrRemove.Entity is Character { Info: { } } character && !character.Removed) { - GameMain.NetworkMember.CreateEntityEvent(character, new Character.UpdatePermanentStatsEventData(statKey)); + foreach (var statKey in character.Info.SavedStatValues.Keys) + { + GameMain.NetworkMember.CreateEntityEvent(character, new Character.UpdatePermanentStatsEventData(statKey)); + } } } } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index cf0f29d68..28d5adb13 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -282,7 +282,10 @@ namespace Barotrauma.Networking if (newClient.Connection == OwnerConnection && OwnerConnection != null) { newClient.GivePermission(ClientPermissions.All); - newClient.PermittedConsoleCommands.AddRange(DebugConsole.Commands); + foreach (var command in DebugConsole.Commands) + { + newClient.PermittedConsoleCommands.Add(command); + } SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient); } @@ -1226,16 +1229,6 @@ namespace Barotrauma.Networking } } - #warning TODO: remove this later - /*private IEnumerable RoundRestartLoop() - { - yield return new WaitForSeconds(8.0f); - EndGame(); - yield return new WaitForSeconds(8.0f); - StartGame(); - yield return CoroutineStatus.Success; - }*/ - private void ReadCrewMessage(IReadMessage inc, Client sender) { if (GameMain.GameSession?.Campaign is MultiPlayerCampaign mpCampaign) @@ -1394,10 +1387,16 @@ namespace Barotrauma.Networking bool continueCampaign = inc.ReadBoolean(); if (mpCampaign != null && mpCampaign.GameOver || continueCampaign) { - if (mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign) || mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageMap)) + if (gameStarted) + { + SendDirectChatMessage("Cannot continue the campaign from the previous save (round already running).", sender, ChatMessageType.Error); + break; + } + else if (mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign) || mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageMap)) { MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath); } + } else if (!gameStarted && !initiatedStartGame) { diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs index 16cfd6683..6e52af5c4 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs @@ -351,6 +351,13 @@ namespace Barotrauma.Networking break; } +#if DEBUG + netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Server.SimulatedDuplicatesChance; + netPeerConfiguration.SimulatedMinimumLatency = GameMain.Server.SimulatedMinimumLatency; + netPeerConfiguration.SimulatedRandomLatency = GameMain.Server.SimulatedRandomLatency; + netPeerConfiguration.SimulatedLoss = GameMain.Server.SimulatedLoss; +#endif + NetOutgoingMessage lidgrenMsg = netServer.CreateMessage(); byte[] msgData = new byte[msg.LengthBytes]; msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs index c9f9ea020..bc841e5a9 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs @@ -466,7 +466,7 @@ namespace Barotrauma.Networking } ClientPermissions permissions = Networking.ClientPermissions.None; - List permittedCommands = new List(); + HashSet permittedCommands = new HashSet(); if (clientElement.Attribute("preset") == null) { @@ -496,7 +496,7 @@ namespace Barotrauma.Networking else { permissions = preset.Permissions; - permittedCommands = preset.PermittedCommands.ToList(); + permittedCommands = preset.PermittedCommands.ToHashSet(); } } @@ -560,15 +560,15 @@ namespace Barotrauma.Networking foreach (string line in lines) { string[] separatedLine = line.Split('|'); - if (separatedLine.Length < 3) continue; + if (separatedLine.Length < 3) { continue; } string name = string.Join("|", separatedLine.Take(separatedLine.Length - 2)); string ip = separatedLine[separatedLine.Length - 2]; - ClientPermissions permissions = Networking.ClientPermissions.None; + ClientPermissions permissions; if (Enum.TryParse(separatedLine.Last(), out permissions)) { - ClientPermissions.Add(new SavedClientPermission(name, ip, permissions, new List())); + ClientPermissions.Add(new SavedClientPermission(name, ip, permissions, new HashSet())); } } } diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index 367f7785c..291a754c2 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.13.0 + 0.17.14.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs index 41fed0163..61790a3b2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs @@ -130,15 +130,15 @@ namespace Barotrauma head = value; HeadSprite = null; AttachmentSprites = null; - IsMale = value.Preset?.TagSet?.Contains("Male".ToIdentifier()) ?? false; - IsFemale = value.Preset?.TagSet?.Contains("Female".ToIdentifier()) ?? false; } } } - public bool IsMale { get; private set; } + private readonly Identifier maleIdentifier = "Male".ToIdentifier(); + private readonly Identifier femaleIdentifier = "Female".ToIdentifier(); - public bool IsFemale { get; private set; } + public bool IsMale { get { return head?.Preset?.TagSet?.Contains(maleIdentifier) ?? false; } } + public bool IsFemale { get { return head?.Preset?.TagSet?.Contains(femaleIdentifier) ?? false; } } public CharacterInfoPrefab Prefab => CharacterPrefab.Prefabs[SpeciesName].CharacterInfoPrefab; public class HeadPreset : ISerializableEntity diff --git a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs index f1beaffcc..afa6c9758 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs @@ -1746,20 +1746,12 @@ namespace Barotrauma ThrowError(args[1] + " is not a valid latency value."); return; } -#if CLIENT - if (GameMain.Client != null) + if (GameMain.NetworkMember != null) { - GameMain.Client.SimulatedMinimumLatency = minimumLatency; - GameMain.Client.SimulatedRandomLatency = randomLatency; + GameMain.NetworkMember.SimulatedMinimumLatency = minimumLatency; + GameMain.NetworkMember.SimulatedRandomLatency = randomLatency; } -#elif SERVER - if (GameMain.Server != null) - { - GameMain.Server.SimulatedMinimumLatency = minimumLatency; - GameMain.Server.SimulatedRandomLatency = randomLatency; - } -#endif - NewMessage("Set simulated minimum latency to " + minimumLatency + " and random latency to " + randomLatency + ".", Color.White); + NewMessage("Set simulated minimum latency to " + minimumLatency.ToString(CultureInfo.InvariantCulture) + " and random latency to " + randomLatency.ToString(CultureInfo.InvariantCulture) + ".", Color.White); })); commands.Add(new Command("simulatedloss", "simulatedloss [lossratio]: applies simulated packet loss to network messages. For example, a value of 0.1 would mean 10% of the packets are dropped. Useful for simulating real network conditions when testing the multiplayer locally.", (string[] args) => @@ -1770,17 +1762,10 @@ namespace Barotrauma ThrowError(args[0] + " is not a valid loss ratio."); return; } -#if CLIENT - if (GameMain.Client != null) + if (GameMain.NetworkMember != null) { - GameMain.Client.SimulatedLoss = loss; + GameMain.NetworkMember.SimulatedLoss = loss; } -#elif SERVER - if (GameMain.Server != null) - { - GameMain.Server.SimulatedLoss = loss; - } -#endif NewMessage("Set simulated packet loss to " + (int)(loss * 100) + "%.", Color.White); })); commands.Add(new Command("simulatedduplicateschance", "simulatedduplicateschance [duplicateratio]: simulates packet duplication in network messages. For example, a value of 0.1 would mean there's a 10% chance a packet gets sent twice. Useful for simulating real network conditions when testing the multiplayer locally.", (string[] args) => @@ -1791,21 +1776,27 @@ namespace Barotrauma ThrowError(args[0] + " is not a valid duplicate ratio."); return; } -#if CLIENT - if (GameMain.Client != null) + if (GameMain.NetworkMember != null) { - GameMain.Client.SimulatedDuplicatesChance = duplicates; + GameMain.NetworkMember.SimulatedDuplicatesChance = duplicates; } -#elif SERVER - if (GameMain.Server != null) - { - GameMain.Server.SimulatedDuplicatesChance = duplicates; - } -#endif NewMessage("Set packet duplication to " + (int)(duplicates * 100) + "%.", Color.White); })); #if DEBUG + + commands.Add(new Command("simulatedlongloadingtime", "simulatedlongloadingtime [minimum loading time]: forces loading a round to take at least the specified amount of seconds.", (string[] args) => + { + if (args.Count() < 1 || (GameMain.NetworkMember == null)) return; + if (!float.TryParse(args[0], NumberStyles.Any, CultureInfo.InvariantCulture, out float time)) + { + ThrowError(args[0] + " is not a valid duration ratio."); + return; + } + GameSession.MinimumLoadingTime = time; + NewMessage("Set minimum loading time to " + time + " seconds.", Color.White); + })); + commands.Add(new Command("storeinfo", "", (string[] args) => { if (GameMain.GameSession?.Map?.CurrentLocation is Location location) diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs index 4704bf3e3..a7b79214f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs @@ -14,6 +14,10 @@ namespace Barotrauma { partial class GameSession { +#if DEBUG + public static float MinimumLoadingTime; +#endif + public enum InfoFrameTab { Crew, Mission, MyCharacter, Traitor }; public readonly EventManager EventManager; @@ -355,6 +359,9 @@ namespace Barotrauma public void StartRound(LevelData? levelData, bool mirrorLevel = false, SubmarineInfo? startOutpost = null, SubmarineInfo? endOutpost = null) { +#if DEBUG + DateTime startTime = DateTime.Now; +#endif AfflictionPrefab.LoadAllEffects(); MirrorLevel = mirrorLevel; @@ -485,6 +492,15 @@ namespace Barotrauma } } +#if DEBUG + double startDuration = (DateTime.Now - startTime).TotalSeconds; + if (startDuration < MinimumLoadingTime) + { + int sleepTime = (int)((MinimumLoadingTime - startDuration) * 1000); + DebugConsole.NewMessage($"Stalling round start by {sleepTime / 1000.0f} s (minimum loading time set to {MinimumLoadingTime})...", Color.Magenta); + System.Threading.Thread.Sleep(sleepTime); + } +#endif #if CLIENT if (GameMode is CampaignMode && levelData != null) { SteamAchievementManager.OnBiomeDiscovered(levelData.Biome); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/IdCard.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/IdCard.cs index 796bcf372..eb57d4ba2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/IdCard.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/IdCard.cs @@ -33,9 +33,6 @@ namespace Barotrauma.Items.Components set; } - private JobPrefab cachedJobPrefab; - private string cachedName; - public ImmutableHashSet OwnerTagSet { get; set; } [Serialize("", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)] diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/OutpostTerminal.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/OutpostTerminal.cs index a9439661a..d72756115 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/OutpostTerminal.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/OutpostTerminal.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; +using System.Xml.Linq; namespace Barotrauma.Items.Components { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/Powered.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/Powered.cs index c016836b0..d22c343b5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/Powered.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/Powered.cs @@ -677,21 +677,18 @@ namespace Barotrauma.Items.Components /// protected float GetAvailableInstantaneousBatteryPower() { - if (item.Connections == null) { return 0.0f; } + if (item.Connections == null || powerIn == null) { return 0.0f; } float availablePower = 0.0f; - foreach (Connection c in item.Connections) + var recipients = powerIn.Recipients; + foreach (Connection recipient in recipients) { - var recipients = c.Recipients; - foreach (Connection recipient in recipients) - { - if (!recipient.IsPower || !recipient.IsOutput) { continue; } - var battery = recipient.Item?.GetComponent(); - if (battery == null) { continue; } - float maxOutputPerFrame = battery.MaxOutPut / 60.0f; - float framesPerMinute = 3600.0f; - availablePower += Math.Min(battery.Charge * framesPerMinute, maxOutputPerFrame); - } - } + if (!recipient.IsPower || !recipient.IsOutput) { continue; } + var battery = recipient.Item?.GetComponent(); + if (battery == null || battery.Item.Condition <= 0.0f) { continue; } + float maxOutputPerFrame = battery.MaxOutPut / 60.0f; + float framesPerMinute = 3600.0f; + availablePower += Math.Min(battery.Charge * framesPerMinute, maxOutputPerFrame); + } return availablePower; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs index cbd83caea..45d5d3ae4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs @@ -597,7 +597,7 @@ namespace Barotrauma.Items.Components else if (ic is PowerTransfer pt) { //power transfer items (junction boxes, relays) don't deteriorate if they're no carrying any power - if (Math.Abs(pt.CurrPowerConsumption) > 0.1f) { return true; } + if (pt.Voltage > 0.1f) { return true; } } else if (ic is PowerContainer pc) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs index 2be6ddb93..d6a3357f0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs @@ -51,11 +51,26 @@ namespace Barotrauma.MapCreatures.Behavior private bool inflate; private float pulseDelay = Rand.Range(0f, 3f); - public readonly BallastFloraBranch? ParentBranch; + private BallastFloraBranch? parentBranch; + public BallastFloraBranch? ParentBranch + { + get { return parentBranch; } + set + { + if (value != parentBranch) + { + parentBranch = value; + if (parentBranch != null) + { + BranchDepth = parentBranch.BranchDepth + 1; + } + } + } + } /// /// How far from the root this branch is /// - public readonly int BranchDepth; + public int BranchDepth { get; private set; } public float AccumulatedDamage; public float DamageVisualizationTimer; @@ -71,10 +86,6 @@ namespace Barotrauma.MapCreatures.Behavior { ParentBranch = parentBranch; ParentBallastFlora = parent; - if (parentBranch != null) - { - BranchDepth = parentBranch.BranchDepth + 1; - } } public void UpdateHealth() @@ -319,6 +330,7 @@ namespace Barotrauma.MapCreatures.Behavior foreach (BallastFloraBranch branch in Branches) { + SetHull(branch); if (branch.ClaimedItemId > -1) { if (Entity.FindEntityByID((ushort)branch.ClaimedItemId) is Item item) @@ -422,6 +434,7 @@ namespace Barotrauma.MapCreatures.Behavior public void LoadSave(XElement element, IdRemap idRemap) { + List<(BallastFloraBranch branch, int parentBranchId)> branches = new List<(BallastFloraBranch branch, int parentBranchId)>(); SerializableProperties = SerializableProperty.DeserializeProperties(this, element); Offset = element.GetAttributeVector2("offset", Vector2.Zero); foreach (var subElement in element.Elements()) @@ -442,6 +455,14 @@ namespace Barotrauma.MapCreatures.Behavior } } + foreach ((BallastFloraBranch branch, int parentBranchId) in branches) + { + if (parentBranchId > -1 && parentBranchId < Branches.Count) + { + branch.ParentBranch = Branches[parentBranchId]; + } + } + void LoadBranch(XElement branchElement, IdRemap idRemap) { Vector2 pos = branchElement.GetAttributeVector2("pos", Vector2.Zero); @@ -456,13 +477,7 @@ namespace Barotrauma.MapCreatures.Behavior int claimedId = branchElement.GetAttributeInt("claimed", -1); int parentBranchId = branchElement.GetAttributeInt("parentbranch", -1); - BallastFloraBranch? parentBranch = null; - if (parentBranchId > -1) - { - parentBranch = Branches[parentBranchId]; - } - - BallastFloraBranch newBranch = new BallastFloraBranch(this, parentBranch, pos, VineTileType.CrossJunction, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafconfig)) + BallastFloraBranch newBranch = new BallastFloraBranch(this, null, pos, VineTileType.CrossJunction, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafconfig)) { ID = id, Health = health, @@ -471,6 +486,8 @@ namespace Barotrauma.MapCreatures.Behavior BlockedSides = (TileSide) blockedSides, IsRoot = isRoot }; + branches.Add((newBranch, parentBranchId)); + if (newBranch.IsRoot) { root = newBranch; } if (claimedId > -1) @@ -731,7 +748,7 @@ namespace Barotrauma.MapCreatures.Behavior } // could probably be moved to the branch constructor - private void SetHull(BallastFloraBranch branch) + public void SetHull(BallastFloraBranch branch) { branch.CurrentHull = Hull.FindHull(GetWorldPosition() + branch.Position, Parent, true); } @@ -1204,7 +1221,7 @@ namespace Barotrauma.MapCreatures.Behavior _entityList.Remove(this); #if SERVER - CreateNetworkMessage(new KillEventData()); + CreateNetworkMessage(new RemoveEventData()); #endif } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraEventData.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraEventData.cs index 40c174e1a..ee6c91737 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraEventData.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraEventData.cs @@ -19,6 +19,11 @@ namespace Barotrauma.MapCreatures.Behavior public NetworkHeader NetworkHeader => NetworkHeader.Kill; } + private readonly struct RemoveEventData : IEventData + { + public NetworkHeader NetworkHeader => NetworkHeader.Remove; + } + private readonly struct BranchCreateEventData : IEventData { public NetworkHeader NetworkHeader => NetworkHeader.BranchCreate; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs index 75ead9148..f6f1802ae 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs @@ -164,18 +164,14 @@ namespace Barotrauma.Networking } public bool HasSpawned; //has the client spawned as a character during the current round - private List kickVoters; + private readonly List kickVoters; public HashSet GivenAchievements = new HashSet(); public ClientPermissions Permissions = ClientPermissions.None; - public List PermittedConsoleCommands - { - get; - private set; - } + public readonly HashSet PermittedConsoleCommands = new HashSet(); - private object[] votes; + private readonly object[] votes; public int KickVoteCount { @@ -195,7 +191,6 @@ namespace Barotrauma.Networking this.Name = name; this.ID = ID; - PermittedConsoleCommands = new List(); kickVoters = new List(); votes = new object[Enum.GetNames(typeof(VoteType)).Length]; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/ClientPermissions.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/ClientPermissions.cs index d18e54eef..051c2b1f7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/ClientPermissions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/ClientPermissions.cs @@ -37,7 +37,7 @@ namespace Barotrauma.Networking public readonly LocalizedString Name; public readonly LocalizedString Description; public readonly ClientPermissions Permissions; - public readonly List PermittedCommands; + public readonly HashSet PermittedCommands; public PermissionPreset(XElement element) { @@ -51,7 +51,7 @@ namespace Barotrauma.Networking DebugConsole.ThrowError("Error in permission preset \"" + Name + "\" - " + permissionsStr + " is not a valid permission!"); } - PermittedCommands = new List(); + PermittedCommands = new HashSet(); if (Permissions.HasFlag(ClientPermissions.ConsoleCommands)) { foreach (var subElement in element.Elements()) @@ -87,7 +87,7 @@ namespace Barotrauma.Networking } } - public bool MatchesPermissions(ClientPermissions permissions, List permittedConsoleCommands) + public bool MatchesPermissions(ClientPermissions permissions, HashSet permittedConsoleCommands) { return permissions == this.Permissions && PermittedCommands.SequenceEqual(permittedConsoleCommands); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs index 8865bf8dc..12be53f66 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs @@ -443,6 +443,9 @@ namespace Barotrauma { removeQueue.Clear(); spawnQueue.Clear(); +#if CLIENT + receivedEvents.Clear(); +#endif } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs index c182cfdf5..736099f64 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs @@ -72,18 +72,18 @@ namespace Barotrauma.Networking public readonly string EndPoint; public readonly ulong SteamID; public readonly string Name; - public List PermittedCommands; + public HashSet PermittedCommands; public ClientPermissions Permissions; - public SavedClientPermission(string name, string endpoint, ClientPermissions permissions, List permittedCommands) + public SavedClientPermission(string name, string endpoint, ClientPermissions permissions, HashSet permittedCommands) { this.Name = name; this.EndPoint = endpoint; this.Permissions = permissions; this.PermittedCommands = permittedCommands; } - public SavedClientPermission(string name, ulong steamID, ClientPermissions permissions, List permittedCommands) + public SavedClientPermission(string name, ulong steamID, ClientPermissions permissions, HashSet permittedCommands) { this.Name = name; this.SteamID = steamID; diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 4de5432a3..10715e447 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,26 @@ +--------------------------------------------------------------------------------------------------------- +v0.17.14.0 +--------------------------------------------------------------------------------------------------------- + +Changes: +- Display both wallet and bank balance on campaign interfaces when the player has access to the bank funds. + +Fixes: +- Hopefully fixed the frequent "SteamP2P connection timed out" errors during loading screens. +- Fixed "missing entity" error when a character who's stats have been modified by a talent gets removed (e.g. eaten by a monster, despawning). +- If starting a multiplayer round takes a long time, instead of throwing the "did not receive STARTGAMEFINALIZE message" error, you're asked whether you want to keep waiting or return to the lobby. +- Fixed "failed to parse the string 'COLOR.GUI.GREEN' to Color" errors when using the submarine upgrade interface in Spanish. +- Fixed junction boxes not deteriorating over time. +- Fixed turrets being able to fire without consuming power when the power is wired to some other connection than power_in. +- Fixed broken supercapacitors providing unlimited power to turrets. +- Fixed IsMale/IsFemale properties resetting when saving and reloading (not used by the vanilla game). +- Fixed haloperidol not healing psychosis. +- Fixed ballast flora sometimes becoming unkillable client-side when entering a new level. +- Fixed the Server Log button overlapping campaign interfaces by hiding it whenever a campaign interface is open. +- Fixed an inconsistency in the assault rifle mag recipe. +- Fixed job not showing up in ID card description. +- Fixed store interface not being updated when the player balance changes. + --------------------------------------------------------------------------------------------------------- v0.17.13.0 ---------------------------------------------------------------------------------------------------------