Sync with upstream
* Update bug-reports.yml * Fix modifyChatMessage hook * Add LuaCsSetup.Lua back for compatibility * Fix Game.AssignOnExecute having command arguments be passed as varargs instead of a table * Actually use the PackageId const everywhere we need to refer to our content package * Load languages files even if the package is disabled * Fix Hook.Remove not being implemented properly * - Changed event aliases to be case insensitive. * - Fixed assembly logging style. - Fixed double logging during execution. * Fix garbage network data being read by the game when reading LuaCs network messages * PackageId -> PackageName * Added caching toggle to PluginManagementService * Fix LuaCs initializing too late for singleplayer campaigns and rework the C# prompt to only show when enabling mods/joining server * Oops, fix NRE crash * Fix hide username in logs config not doing anything * Fix Cs prompt showing up more than one between rounds * Fix server host being prompted twice with the C# popup * Ignore our workshop packages from the game's dependency thing since it doesn't really make sense * Load console commands after executing and possible fix for the not console command permitted * Added fallback friendly name resolution for ModConfig assembly contents. * Register Voronoi2 stuff * Added configinfo null check to SettingBase.cs * Add safety check so this stops crashing when we look at it the wrong way * Fixed "Folder" attribute files not being found. * Keep the LuaCsConfig class laying around for compatibility, not sure anywhere in our code base (and shouldn't be) * Added fallback compilation for UseInternalsAwareAssembly if the publicized script compilation fails. * Added legacy overload of AddCommand for mod compat. * Added LoggerService to Lua env. Made ILoggerService compliant with LuaCsLogger API. * Changed csharp script compilation algorithm to be best effort. * Added "RunUnrestricted" mode for lua scripts that need to run outside of sandbox. * - Fixed networking sync vars failing to sync initially. - Fixed lua failing to differentiate overloads ISettingBase. * Add alias for human.CPRSuccess and human.CPRFailed * - Fixed up the settings menu. - Made SettingEntry throw an error if "Value" attribute is not found in XML. - Fixed saved values for settings sometimes not reloading after disabling and re-enabling a package. * Fix LuaCs net messages received during connection initialization to be read incorrectly, happened because we would reset the BitPosition in our harmony patch which would cause the message to be read incorrectly later * Allow reloadlua to force the state to running * New icon for settings and make the top left text more user friendly * Fix client.packages hook sending normal packages * Fixed OnUpdate() not passing in deltaTime instead of totalTime. * Missing diffs frombb21a09244* Added networking tests for configs. * Added missing diffs forf61f852a25. * Some tweaks to the text * Remove missing Value error, it should just use the default value if it's not specified * Fix UseInternalAccessName * Always purge cashes for plugin content on unloading. * Fix texture not multiple of 4 * v1.12.7.0 (Spring Update 2026 Hotfix 1) --------- Co-authored-by: Joonas Rikkonen <poe.regalis@gmail.com> Co-authored-by: Evil Factory <36804725+evilfactory@users.noreply.github.com> Co-authored-by: MapleWheels <njainanan@hotmail.com>
This commit is contained in:
2
.github/DISCUSSION_TEMPLATE/bug-reports.yml
vendored
2
.github/DISCUSSION_TEMPLATE/bug-reports.yml
vendored
@@ -73,7 +73,7 @@ body:
|
||||
label: Version
|
||||
description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu.
|
||||
options:
|
||||
- v1.11.5.0 (Winter Update 2025 Hotfix 1)
|
||||
- v1.12.6.2 (Spring Update 2026)
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -24,7 +24,7 @@ public sealed class SettingControl : SettingBase, ISettingControl
|
||||
public SettingControl(IConfigInfo configInfo, Func<OneOf<string, XElement, object>, bool> valueChangePredicate) : base(configInfo)
|
||||
{
|
||||
_valueChangePredicate = valueChangePredicate;
|
||||
TrySetValue(configInfo.Element);
|
||||
TrySetSerializedValue(configInfo.Element);
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
@@ -37,7 +37,7 @@ public sealed class SettingControl : SettingBase, ISettingControl
|
||||
public override string GetStringValue() => Value.ToString();
|
||||
public override string GetDefaultStringValue() => new KeyOrMouse(Keys.NumLock).ToString();
|
||||
|
||||
public override bool TrySetValue(OneOf<string, XElement> value)
|
||||
public override bool TrySetSerializedValue(OneOf<string, XElement> value)
|
||||
{
|
||||
var newVal = value.Match<KeyOrMouse>(
|
||||
(string v) => GetKeyOrMouse(v),
|
||||
|
||||
@@ -1,106 +1,47 @@
|
||||
using System;
|
||||
using Barotrauma.CharacterEditor;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Barotrauma.CharacterEditor;
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using static System.Collections.Specialized.BitVector32;
|
||||
|
||||
// ReSharper disable ObjectCreationAsStatement
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class LuaCsSetup
|
||||
{
|
||||
private bool _isClientPromptActive;
|
||||
private bool _isCsEnabledForSession = false;
|
||||
|
||||
public void CheckRunConditionalHostingCsEnabled(Action onReadyToRun)
|
||||
{
|
||||
public void PromptCSharpMods(Action<bool> onSelection, bool joiningServer)
|
||||
{
|
||||
var res = ReadyToRunNoPrompt();
|
||||
if (res.ShouldRun)
|
||||
{
|
||||
onReadyToRun?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayCsModsPromptClient(res.Item2, (selectedYes) =>
|
||||
{
|
||||
if (selectedYes)
|
||||
{
|
||||
onReadyToRun?.Invoke();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private (bool ShouldRun, ImmutableArray<ContentPackage> PromptPackages) ReadyToRunNoPrompt()
|
||||
{
|
||||
if (this.IsCsEnabled)
|
||||
{
|
||||
return (true, ImmutableArray<ContentPackage>.Empty);
|
||||
}
|
||||
|
||||
if (!ShouldPromptForCs)
|
||||
{
|
||||
return (true, ImmutableArray<ContentPackage>.Empty);
|
||||
}
|
||||
|
||||
ImmutableArray<ContentPackage> contentPackages = PackageManagementService.GetLoadedAssemblyPackages()
|
||||
.Where(p => p.Name != PackageId)
|
||||
ImmutableArray<ContentPackage> contentPackages = PackageManagementService.GetLoadedUnrestrictedPackages()
|
||||
.Where(p => p.Name != PackageName)
|
||||
.ToImmutableArray();
|
||||
|
||||
return (contentPackages.IsEmpty, contentPackages);
|
||||
}
|
||||
|
||||
partial void CheckReadyToRun(Action onReadyToRun)
|
||||
{
|
||||
var res = ReadyToRunNoPrompt();
|
||||
if (res.ShouldRun)
|
||||
if (_csRunPolicy?.Value is "Enabled")
|
||||
{
|
||||
onReadyToRun?.Invoke();
|
||||
IsCsEnabledForSession = true;
|
||||
onSelection(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.Client?.ClientPeer is P2POwnerPeer)
|
||||
else if (_csRunPolicy?.Value is "Disabled")
|
||||
{
|
||||
SetCsPolicyAndContinue(true);
|
||||
IsCsEnabledForSession = false;
|
||||
onSelection(false);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayCsModsPromptClient(res.PromptPackages, (selectedYes) =>
|
||||
if (contentPackages.None())
|
||||
{
|
||||
SetCsPolicyAndContinue(selectedYes);
|
||||
onSelection(true);
|
||||
return;
|
||||
});
|
||||
|
||||
void SetCsPolicyAndContinue(bool csSessionExecutionPolicy)
|
||||
{
|
||||
var prevRunState = this.CurrentRunState;
|
||||
if (CurrentRunState >= RunState.Running)
|
||||
{
|
||||
SetRunState(RunState.LoadedNoExec);
|
||||
}
|
||||
this._isCsEnabledForSession = csSessionExecutionPolicy;
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (CurrentRunState != prevRunState)
|
||||
{
|
||||
SetRunState(prevRunState);
|
||||
}
|
||||
onReadyToRun?.Invoke();
|
||||
}, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayCsModsPromptClient(ImmutableArray<ContentPackage> contentPackages, Action<bool> onSelection)
|
||||
{
|
||||
if (_isClientPromptActive) { return; }
|
||||
|
||||
_isClientPromptActive = true;
|
||||
|
||||
GUIMessageBox messageBox = new GUIMessageBox(
|
||||
TextManager.Get("warning"),
|
||||
@@ -115,7 +56,7 @@ namespace Barotrauma
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgBoxLayout.RectTransform), "The following mods contain CSharp code",
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgBoxLayout.RectTransform), "The following mods contain CSharp code OR Unsandboxed Lua Code",
|
||||
font: GUIStyle.SubHeadingFont, wrap: true, textAlignment: Alignment.Center);
|
||||
|
||||
GUIListBox packageListBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), msgBoxLayout.RectTransform))
|
||||
@@ -126,22 +67,39 @@ namespace Barotrauma
|
||||
foreach (ContentPackage package in contentPackages)
|
||||
{
|
||||
GUIFrame packageFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), packageListBox.Content.RectTransform), style: "ListBoxElement");
|
||||
new GUITextBlock(new RectTransform(new Vector2(1f, 1f), packageFrame.RectTransform), package.Name);
|
||||
GUILayoutGroup packageLayout = new GUILayoutGroup(new RectTransform(Vector2.One, packageFrame.RectTransform), true, Anchor.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.7f, 1f), packageLayout.RectTransform), package.Name);
|
||||
new GUIButton(new RectTransform(new Vector2(0.3f, 1f), packageLayout.RectTransform, Anchor.CenterRight), "Open Folder", style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (GUIButton button, object obj) =>
|
||||
{
|
||||
string directory = package.Dir;
|
||||
if (string.IsNullOrEmpty(directory)) { return false; }
|
||||
|
||||
ToolBox.OpenFileWithShell(directory);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0f), msgBoxLayout.RectTransform), "C# mods are not sandboxed, meaning that they have unrestrictive access to your computer, please make sure you trust these mods before you continue. If you are not hosting a server, selecting cancel will only run Lua mods.", wrap: true)
|
||||
string bodyText =
|
||||
joiningServer ?
|
||||
"You are joining a server that includes mods with C# code OR unrestricted Lua code. These mods are not sandboxed and may access your computer without restrictions. If you trust these mods, select 'Enable C# for this session'. Otherwise, select 'Cancel' to run only Lua mods."
|
||||
: "You have enabled mods that include C# code. These mods are not sandboxed and may access your computer without restrictions. If you trust these mods, select 'Enable C# for this session'. Otherwise, select 'Cancel' to run only Sandboxed Lua mods.";
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0f), msgBoxLayout.RectTransform), bodyText, wrap: true)
|
||||
{
|
||||
Wrap = true
|
||||
};
|
||||
|
||||
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), messageBox.Content.RectTransform, Anchor.BottomCenter), isHorizontal: false, childAnchor: Anchor.TopCenter);
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.8f, 0.0f), buttonLayout.RectTransform), "Continue")
|
||||
new GUIButton(new RectTransform(new Vector2(0.8f, 0.0f), buttonLayout.RectTransform), "Enable C# for this session")
|
||||
{
|
||||
TextBlock = { AutoScaleHorizontal = true },
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
_isClientPromptActive = false;
|
||||
IsCsEnabledForSession = true;
|
||||
onSelection(true);
|
||||
messageBox.Close();
|
||||
return true;
|
||||
@@ -152,7 +110,7 @@ namespace Barotrauma
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
_isClientPromptActive = false;
|
||||
IsCsEnabledForSession = false;
|
||||
onSelection(false);
|
||||
messageBox.Close();
|
||||
return true;
|
||||
@@ -201,10 +159,18 @@ namespace Barotrauma
|
||||
case SpriteEditorScreen:
|
||||
case SubEditorScreen:
|
||||
case TestScreen: // notes: TestScreen is a Linux edge case editor screen and is deprecated.
|
||||
CheckReadyToRun(() =>
|
||||
|
||||
if (screen is NetLobbyScreen && CurrentRunState != RunState.Running && GameMain.Client?.ClientPeer is not P2POwnerPeer)
|
||||
{
|
||||
PromptCSharpMods(selection =>
|
||||
{
|
||||
SetRunState(RunState.Running);
|
||||
}, joiningServer: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRunState(RunState.Running);
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Logger.LogError(
|
||||
|
||||
@@ -32,11 +32,11 @@ partial class NetworkingService : INetworkingService, IEventServerConnected, IEv
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReceivedServerNetMessage(IReadMessage netMessage, ServerPacketHeader serverPacketHeader)
|
||||
public bool? OnReceivedServerNetMessage(IReadMessage netMessage, ServerPacketHeader serverPacketHeader)
|
||||
{
|
||||
if (serverPacketHeader != ServerHeader)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerToClient luaCsHeader = (ServerToClient)netMessage.ReadByte();
|
||||
@@ -55,6 +55,8 @@ partial class NetworkingService : INetworkingService, IEventServerConnected, IEv
|
||||
ReadIds(netMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SendSyncMessage()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Linq;
|
||||
@@ -20,23 +21,87 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
private string _selectedSearchQuery = string.Empty;
|
||||
private ContentPackage _selectedContentPackage;
|
||||
private string _selectedCategory = string.Empty;
|
||||
private ImmutableArray<ISettingBase> _currentlyDisplayedSettings;
|
||||
private ILoggerService _loggerService;
|
||||
|
||||
private bool _promptOpen = false;
|
||||
|
||||
|
||||
// Note: "static" instead of "const" for Hot Reload and to allow changing at runtime.
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Local
|
||||
|
||||
// --- UI controls ---
|
||||
private static float MenuTitleHeight = 0.06f; // (ContentDisplayAreaHeightContainer + MenuTitleHeight) < 1f
|
||||
private static float ContentDisplayAreaHeightContainer = 0.93f;
|
||||
private static float ContentDisplayAreaHeightInnerCategories = 0.99f;
|
||||
private static float ContentDisplayAreaHeightInnerSettings = 0.97f;
|
||||
private static float ContentLeftRightSplitPosition = 0.3f;
|
||||
|
||||
// Search Bar
|
||||
private static float SearchBarLayoutHeight = 0.06f;
|
||||
private static float SearchBarLabelWidth = 0.1f;
|
||||
private static float SearchBarLabelBoxSpacing = 0.05f;
|
||||
|
||||
private static float SearchBarTextBoxWidth = 1f - SearchBarLabelWidth - SearchBarLabelBoxSpacing;
|
||||
|
||||
// Categories, Packages Display Area
|
||||
private static float CategoriesDisplayListHeight = 0.945f;
|
||||
private static float CategoryButtonHeightRelative = 0.122f;
|
||||
private static float PackageSelectionButtonHeight = 0.07f;
|
||||
|
||||
private static Color CategoryButtonHoverSelectColor = new Color(50, 50, 50, 255);
|
||||
private static Color CategoryButtonTextColor = Color.PeachPuff;
|
||||
private static Color CategoryButtonTextColorSelected = Color.White;
|
||||
private static Color CategoryButtonColorPressed = Color.TransparentBlack;
|
||||
|
||||
// Settings Display Area
|
||||
private static float SettingLabelWidth = 0.6f;
|
||||
private static float SettingControlWidth = 0.4f;
|
||||
private static float SettingHeight = 0.05625f/ContentDisplayAreaHeightContainer/ContentDisplayAreaHeightInnerSettings;
|
||||
private static Color SettingEntryLabelTextColor = Color.PeachPuff;
|
||||
private static string SettingGUIFrameStyle = "";
|
||||
private static Color? SettingGUIFrameColor = null;
|
||||
|
||||
// settings reset
|
||||
private static Vector2 SettingsResetButtonTopSpacer = new Vector2(0f, 0.02f);
|
||||
private static Vector2 SettingsResetButtonDimensions = new Vector2(0.3f, 0.05f);
|
||||
private static string SettingsResetButtonStyle = "GUIButtonSmall";
|
||||
private static Color SettingsResetButtonColor = Color.DarkOliveGreen;
|
||||
private static Color SettingsResetButtonHoverColor = Color.Olive;
|
||||
private static Color SettingsResetButtonTextColor = Color.PeachPuff;
|
||||
private static Color SettingsResetButtonTextColorSelected = Color.White;
|
||||
|
||||
private static Vector2 ResetConfirmationPromptDimensions = new Vector2(0.15f, 0.2f);
|
||||
|
||||
|
||||
// ReSharper restore FieldCanBeMadeReadOnly.Local
|
||||
private const string SettingsResetButtonText = "LuaCsForBarotrauma.SettingsMenu.ResetVisibleSettings";
|
||||
private const string SettingsResetPromptTitle = "LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Title";
|
||||
private const string SettingsResetPromptContents = "LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Message";
|
||||
private const string SettingsResetPromptYesText = "LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Yes";
|
||||
private const string SettingsResetPromptNoText = "LuaCsForBarotrauma.SettingsMenu.ResetPrompt.No";
|
||||
|
||||
|
||||
private event Action OnApplyInstalledModsChanges;
|
||||
|
||||
public ModsGameplaySettingsMenu(GUIFrame contentFrame,
|
||||
IPackageManagementService packageManagementService,
|
||||
IConfigService configService,
|
||||
ILoggerService loggerService,
|
||||
SettingsMenu settingsMenuInstance) : base(contentFrame, packageManagementService, configService, settingsMenuInstance)
|
||||
{
|
||||
_settingsInstancesGameplay = configService.GetDisplayableConfigs()
|
||||
.ToImmutableArray();
|
||||
|
||||
|
||||
_loggerService = loggerService;
|
||||
|
||||
var mainLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), contentFrame.RectTransform, Anchor.Center), false, Anchor.TopLeft);
|
||||
// page title
|
||||
var menuTitleLayoutGroup = new GUILayoutGroup(
|
||||
new RectTransform(new Vector2(1f, 0.06f), mainLayoutGroup.RectTransform, Anchor.TopLeft), true, Anchor.TopLeft);
|
||||
GUIUtil.Label(menuTitleLayoutGroup, "Mods Gameplay Settings", GUIStyle.LargeFont, new Vector2(1f, 1f));
|
||||
new RectTransform(new Vector2(1f, MenuTitleHeight), mainLayoutGroup.RectTransform, Anchor.TopLeft), true, Anchor.TopLeft);
|
||||
GUIUtil.Label(menuTitleLayoutGroup,
|
||||
GetLocalizedString("LuaCsForBarotrauma.SettingsMenu.ModGameplayButton", "Mod Gameplay Settings"),
|
||||
GUIStyle.LargeFont, new Vector2(1f, 1f));
|
||||
|
||||
// page contents
|
||||
var contentAreaLayoutGroup = new GUILayoutGroup(
|
||||
@@ -44,10 +109,10 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
Anchor.TopLeft);
|
||||
|
||||
var searchBarLayoutGroup = new GUILayoutGroup(
|
||||
new RectTransform(new Vector2(1f, 0.06f), contentAreaLayoutGroup.RectTransform, Anchor.TopCenter), true, Anchor.CenterLeft);
|
||||
GUIUtil.Label(searchBarLayoutGroup, "Search: ", GUIStyle.SubHeadingFont, new Vector2(0.1f, 1f));
|
||||
new RectTransform(new Vector2(1f, SearchBarLayoutHeight), contentAreaLayoutGroup.RectTransform, Anchor.TopCenter), true, Anchor.CenterLeft);
|
||||
GUIUtil.Label(searchBarLayoutGroup, "Search: ", GUIStyle.SubHeadingFont, new Vector2(SearchBarLabelWidth, 1f));
|
||||
var searchBar = new GUITextBox(
|
||||
new RectTransform(new Vector2(0.85f, 0.1f), searchBarLayoutGroup.RectTransform, Anchor.TopLeft),
|
||||
new RectTransform(new Vector2(SearchBarTextBoxWidth, 0.1f), searchBarLayoutGroup.RectTransform, Anchor.TopLeft),
|
||||
createClearButton: true)
|
||||
{
|
||||
OnTextChangedDelegate = (btn, txt) =>
|
||||
@@ -56,12 +121,13 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// main display area
|
||||
var settingsContentAreaGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.90f), contentAreaLayoutGroup.RectTransform, Anchor.BottomCenter));
|
||||
var settingsContentAreaGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, ContentDisplayAreaHeightContainer), contentAreaLayoutGroup.RectTransform, Anchor.BottomCenter));
|
||||
GUIUtil.Spacer(settingsContentAreaGroup, Vector2.One);
|
||||
(_modCategoryDisplayGroup, _settingsDisplayGroup) = GUIUtil.CreateSidebars(settingsContentAreaGroup, true);
|
||||
_modCategoryDisplayGroup.RectTransform.RelativeSize = new Vector2(0.3f, 1f);
|
||||
_settingsDisplayGroup.RectTransform.RelativeSize = new Vector2(0.7f, 1f);
|
||||
_modCategoryDisplayGroup.RectTransform.RelativeSize = new Vector2(ContentLeftRightSplitPosition, ContentDisplayAreaHeightInnerCategories);
|
||||
_settingsDisplayGroup.RectTransform.RelativeSize = new Vector2(1f-ContentLeftRightSplitPosition, ContentDisplayAreaHeightInnerSettings);
|
||||
|
||||
// default category
|
||||
_selectedCategory = "All";
|
||||
@@ -202,10 +268,11 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
_selectedCategory = string.Empty;
|
||||
GenerateCategoryListDisplay(_modCategoryDisplayGroup, GetTargetPackagesList(), GetDisplayCategoriesList());
|
||||
GenerateSettingsListDisplay(_settingsDisplayGroup, GetDisplaySettingsList());
|
||||
}, new Vector2(1f, 0.07f));
|
||||
var containerBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.945f), layoutGroup.RectTransform));
|
||||
const float entryHeight = 0.122f;
|
||||
float sizeY = MathF.Max(categories.Length * entryHeight, 1f);
|
||||
}, new Vector2(1f, PackageSelectionButtonHeight));
|
||||
var containerBox = new GUIListBox(new RectTransform(new Vector2(1f, CategoriesDisplayListHeight), layoutGroup.RectTransform));
|
||||
|
||||
|
||||
float sizeY = MathF.Max(categories.Length * CategoryButtonHeightRelative, 1f);
|
||||
var displayedCategoriesFrame = new GUIFrame(new RectTransform(new Vector2(1f, sizeY), containerBox.Content.RectTransform), style: null, color: Color.Black)
|
||||
{
|
||||
CanBeFocused = false
|
||||
@@ -214,17 +281,18 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
|
||||
foreach (var category in categories)
|
||||
{
|
||||
var btn = new GUIButton(new RectTransform(new Vector2(1f, entryHeight), displayCategoriesLayout.RectTransform),
|
||||
var btn = new GUIButton(new RectTransform(new Vector2(1f, CategoryButtonHeightRelative), displayCategoriesLayout.RectTransform),
|
||||
text: category, color: Color.TransparentBlack)
|
||||
{
|
||||
CanBeFocused = true,
|
||||
CanBeSelected = true,
|
||||
TextColor = Color.PeachPuff,
|
||||
HoverColor = new Color(50, 50, 50, 255),
|
||||
HoverTextColor = Color.White,
|
||||
SelectedColor = new Color(50, 50, 50, 255),
|
||||
SelectedTextColor = Color.White,
|
||||
OnPressed = () =>
|
||||
TextColor = CategoryButtonTextColor,
|
||||
HoverColor = CategoryButtonHoverSelectColor,
|
||||
HoverTextColor = CategoryButtonTextColorSelected,
|
||||
PressedColor = CategoryButtonColorPressed,
|
||||
SelectedColor = CategoryButtonHoverSelectColor,
|
||||
SelectedTextColor = CategoryButtonHoverSelectColor,
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
_selectedCategory = category;
|
||||
GenerateSettingsListDisplay(_settingsDisplayGroup, GetDisplaySettingsList());
|
||||
@@ -237,26 +305,47 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
void GenerateSettingsListDisplay(GUILayoutGroup layoutGroup, ImmutableArray<ISettingBase> settings)
|
||||
{
|
||||
layoutGroup.ClearChildren();
|
||||
const float settingHeight = 0.0625f;
|
||||
_currentlyDisplayedSettings = settings;
|
||||
|
||||
var containerBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f), layoutGroup.RectTransform));
|
||||
var containerBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f-SettingsResetButtonDimensions.Y), layoutGroup.RectTransform));
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
var entry = AddSettingToDisplay(
|
||||
setting,
|
||||
containerBox.Content.RectTransform,
|
||||
settingHeight: settingHeight,
|
||||
labelSize: new Vector2(0.6f, 1f),
|
||||
controlSize: new Vector2(0.4f, 1f));
|
||||
|
||||
|
||||
settingHeight: SettingHeight,
|
||||
labelSize: new Vector2(SettingLabelWidth, 1f),
|
||||
controlSize: new Vector2(SettingControlWidth, 1f));
|
||||
}
|
||||
}
|
||||
|
||||
(GUIFrame entryFrame, GUILayoutGroup entryLayoutGroup) AddSettingToDisplay(ISettingBase setting,
|
||||
RectTransform parent, float settingHeight, Vector2 labelSize, Vector2 controlSize)
|
||||
var spacer = new GUIFrame(new RectTransform(SettingsResetButtonTopSpacer, layoutGroup.RectTransform),
|
||||
style: null, color: Color.TransparentBlack);
|
||||
|
||||
var resetSettingsButton = new GUIButton(
|
||||
new RectTransform(SettingsResetButtonDimensions, layoutGroup.RectTransform),
|
||||
GetLocalizedString(SettingsResetButtonText, "Reset Visible Settings"),
|
||||
style: SettingsResetButtonStyle)
|
||||
{
|
||||
CanBeSelected = true,
|
||||
CanBeFocused = true,
|
||||
Color = SettingsResetButtonColor,
|
||||
HoverColor = SettingsResetButtonHoverColor,
|
||||
SelectedColor = SettingsResetButtonHoverColor,
|
||||
SelectedTextColor = SettingsResetButtonTextColorSelected,
|
||||
TextColor = SettingsResetButtonTextColor,
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
DisplayResetConfirmationPrompt(settings);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
(GUIFrame entryFrame, GUILayoutGroup entryLayoutGroup)
|
||||
AddSettingToDisplay(ISettingBase setting, RectTransform parent, float settingHeight, Vector2 labelSize, Vector2 controlSize)
|
||||
{
|
||||
GUIFrame entryFrame = new GUIFrame(new RectTransform(new Vector2(1f, settingHeight), parent))
|
||||
GUIFrame entryFrame = new GUIFrame(new RectTransform(new Vector2(1f, settingHeight), parent),
|
||||
style: SettingGUIFrameStyle, color: SettingGUIFrameColor)
|
||||
{
|
||||
Color = Color.DarkGray
|
||||
};
|
||||
@@ -266,9 +355,10 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
new GUIFrame(new RectTransform(new Vector2(0.02f, 1f), entryLayoutGroup.RectTransform),
|
||||
color: Color.TransparentBlack);
|
||||
|
||||
// setting label
|
||||
new GUITextBlock(new RectTransform(labelSize - new Vector2(0.05f, 0f), entryLayoutGroup.RectTransform),
|
||||
GetLocalizedString(setting.GetDisplayInfo().DisplayName, setting.GetDisplayInfo().DisplayName),
|
||||
textColor: Color.PeachPuff,
|
||||
textColor: SettingEntryLabelTextColor,
|
||||
font: GUIStyle.SmallFont,
|
||||
textAlignment: Alignment.Left)
|
||||
{
|
||||
@@ -281,6 +371,58 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
});
|
||||
return (entryFrame, entryLayoutGroup);
|
||||
}
|
||||
|
||||
void DisplayResetConfirmationPrompt(ImmutableArray<ISettingBase> settings)
|
||||
{
|
||||
if (_promptOpen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_promptOpen = true;
|
||||
|
||||
var msgBox = new GUIMessageBox(GetLocalizedString(SettingsResetPromptTitle, "Reset Visible Settings"),
|
||||
GetLocalizedString(SettingsResetPromptContents,
|
||||
"Are you sure you want to reset the values for currently displayed settings?"),
|
||||
new LocalizedString[]
|
||||
{
|
||||
GetLocalizedString(SettingsResetPromptYesText, "Yes"),
|
||||
GetLocalizedString(SettingsResetPromptNoText, "No")
|
||||
}, ResetConfirmationPromptDimensions);
|
||||
msgBox.Buttons[0].OnClicked = (btn, obj) =>
|
||||
{
|
||||
ResetValuesForDisplayedSettings(settings);
|
||||
btn.Visible = false;
|
||||
_promptOpen = false;
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[1].OnClicked = (btn, obj) =>
|
||||
{
|
||||
btn.Visible = false;
|
||||
_promptOpen = false;
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
void ResetValuesForDisplayedSettings(ImmutableArray<ISettingBase> settings)
|
||||
{
|
||||
if (settings.IsDefaultOrEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NewValuesCache.Clear();
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
var str = setting.GetDefaultStringValue();
|
||||
NewValuesCache[setting] = str;
|
||||
loggerService.LogDebug($"Resetting value for {setting.InternalName} to '{str}'");
|
||||
}
|
||||
|
||||
ApplyInstalledModChanges();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -303,8 +445,12 @@ internal sealed class ModsGameplaySettingsMenu : ModsSettingsMenuBase
|
||||
continue;
|
||||
}
|
||||
|
||||
kvp.Key.TrySetValue(kvp.Value);
|
||||
ConfigService.SaveConfigValue(kvp.Key);
|
||||
var success = kvp.Key.TrySetSerializedValue(kvp.Value);
|
||||
if (success)
|
||||
{
|
||||
ConfigService.SaveConfigValue(kvp.Key);
|
||||
_loggerService.LogDebug($"Applied save value for {kvp.Key.InternalName} of {kvp.Value.ToString()}");
|
||||
}
|
||||
}
|
||||
NewValuesCache.Clear();
|
||||
OnApplyInstalledModsChanges?.Invoke();
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs;
|
||||
|
||||
@@ -12,7 +14,7 @@ internal abstract class ModsSettingsMenuBase : IDisposable
|
||||
protected IPackageManagementService PackageManagementService { get; private set; }
|
||||
protected IConfigService ConfigService { get; private set; }
|
||||
protected SettingsMenu SettingsMenuInstance { get; private set; }
|
||||
protected readonly ConcurrentDictionary<ISettingBase, string> NewValuesCache = new();
|
||||
protected readonly ConcurrentDictionary<ISettingBase, OneOf<string, XElement>> NewValuesCache = new();
|
||||
|
||||
protected ModsSettingsMenuBase(GUIFrame contentFrame,
|
||||
IPackageManagementService packageManagementService,
|
||||
|
||||
@@ -18,12 +18,14 @@ public class SettingsMenuSystem : ISettingsMenuSystem
|
||||
private readonly Harmony _harmony;
|
||||
private readonly IPackageManagementService _packageManagementService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly ILoggerService _loggerService;
|
||||
private static SettingsMenuSystem SystemInstance;
|
||||
|
||||
public SettingsMenuSystem(IPackageManagementService packageManagementService, IConfigService configService)
|
||||
public SettingsMenuSystem(IPackageManagementService packageManagementService, IConfigService configService, ILoggerService loggerService)
|
||||
{
|
||||
_packageManagementService = packageManagementService;
|
||||
_configService = configService;
|
||||
_loggerService = loggerService;
|
||||
SystemInstance = this;
|
||||
_harmony = Harmony.CreateAndPatchAll(typeof(SettingsMenuSystem));
|
||||
}
|
||||
@@ -43,13 +45,14 @@ public class SettingsMenuSystem : ISettingsMenuSystem
|
||||
var tabGameplayIndex = (SettingsMenu.Tab)tabCount;
|
||||
var tabControlsIndex = (SettingsMenu.Tab)tabCount+1;
|
||||
|
||||
_gameplayContentFrame = CreateNewContentTab(tabGameplayIndex, __instance,
|
||||
"SettingsMenuTab.Mods", "LuaCsForBarotrauma.SettingsMenu.ModGameplayButton");
|
||||
_gameplayContentFrame = CreateNewContentTab(tabGameplayIndex, __instance,
|
||||
GUIStyle.ComponentStyles.ContainsKey("SettingsMenuTab.LuaCsSettings") ? "SettingsMenuTab.LuaCsSettings" : "SettingsMenuTab.Mods",
|
||||
"LuaCsForBarotrauma.SettingsMenu.ModGameplayButton");
|
||||
/*_controlsContentFrame = CreateNewContentTab(tabControlsIndex, __instance,
|
||||
"SettingsMenuTab.Controls", "LuaCsForBarotrauma.SettingsMenu.ModControlsButton");
|
||||
*/
|
||||
|
||||
_gameplayMenuInstance = new ModsGameplaySettingsMenu(_gameplayContentFrame, _packageManagementService, _configService, __instance);
|
||||
_gameplayMenuInstance = new ModsGameplaySettingsMenu(_gameplayContentFrame, _packageManagementService, _configService, _loggerService, __instance);
|
||||
//_controlsMenuInstance = new ModsControlsSettingsMenu(_controlsContentFrame, _packageManagementService, _configService, __instance);
|
||||
}
|
||||
|
||||
|
||||
@@ -1015,25 +1015,20 @@ namespace Barotrauma
|
||||
|
||||
private void TryStartServer()
|
||||
{
|
||||
LuaCsSetup.Instance.CheckRunConditionalHostingCsEnabled(() =>
|
||||
if (SubmarineInfo.SavedSubmarines.Any(s => s.CalculatingHash))
|
||||
{
|
||||
if (SubmarineInfo.SavedSubmarines.Any(s => s.CalculatingHash))
|
||||
var waitBox = new GUIMessageBox(TextManager.Get("pleasewait"), TextManager.Get("waitforsubmarinehashcalculations"), new LocalizedString[] { TextManager.Get("cancel") });
|
||||
var waitCoroutine = CoroutineManager.StartCoroutine(WaitForSubmarineHashCalculations(waitBox), "WaitForSubmarineHashCalculations");
|
||||
waitBox.Buttons[0].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
var waitBox = new GUIMessageBox(TextManager.Get("pleasewait"), TextManager.Get("waitforsubmarinehashcalculations"), new LocalizedString[] { TextManager.Get("cancel") });
|
||||
var waitCoroutine = CoroutineManager.StartCoroutine(WaitForSubmarineHashCalculations(waitBox), "WaitForSubmarineHashCalculations");
|
||||
waitBox.Buttons[0].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
CoroutineManager.StopCoroutines(waitCoroutine);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
StartServer();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
CoroutineManager.StopCoroutines(waitCoroutine);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
StartServer();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
|
||||
|
||||
@@ -174,9 +174,9 @@ namespace Barotrauma
|
||||
public static RectTransform NewItemRectT(GUILayoutGroup parent)
|
||||
=> new RectTransform((1.0f, 0.06f), parent.RectTransform, Anchor.CenterLeft);
|
||||
|
||||
public static void Spacer(GUILayoutGroup parent)
|
||||
public static void Spacer(GUILayoutGroup parent, float height = 0.03f)
|
||||
{
|
||||
new GUIFrame(new RectTransform((1.0f, 0.03f), parent.RectTransform, Anchor.CenterLeft), style: null);
|
||||
new GUIFrame(new RectTransform((1.0f, height), parent.RectTransform, Anchor.CenterLeft), style: null);
|
||||
}
|
||||
|
||||
public static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font)
|
||||
@@ -507,6 +507,47 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#if OSX
|
||||
Spacer(voiceChat, 0.003f);
|
||||
|
||||
// On macOS, microphone permission can apparently sometimes end up in a broken state when the app binary changes (eg. after a Steam update).
|
||||
// The device seems to be there, but won't receive anything, even if the mic permission is fine.
|
||||
// This button lets the user reset it and reboot the game, so the mic permission check will be retriggered on next run.
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), voiceChat.RectTransform),
|
||||
text: TextManager.Get("MacResetMicPermissions"),
|
||||
style: "GUIButtonSmall")
|
||||
{
|
||||
ToolTip = TextManager.Get("MacResetMicPermissionsToolTip"),
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
var confirmBox = new GUIMessageBox(
|
||||
TextManager.Get("MacResetMicPermissions"),
|
||||
TextManager.Get("MacResetMicPermissionsConfirm"),
|
||||
[TextManager.Get("OK"), TextManager.Get("Cancel")]);
|
||||
confirmBox.Buttons[0].OnClicked = (_, _) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "tccutil",
|
||||
Arguments = "reset Microphone com.FakeFish.Barotrauma",
|
||||
UseShellExecute = false
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.NewMessage($"Failed to reset microphone permission: {e.Message}", Color.Orange);
|
||||
}
|
||||
GameMain.Instance.Exit();
|
||||
confirmBox.Close();
|
||||
return true;
|
||||
};
|
||||
confirmBox.Buttons[1].OnClicked = confirmBox.Close;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
Spacer(voiceChat);
|
||||
|
||||
Label(voiceChat, TextManager.Get("VCInputMode"), GUIStyle.SubHeadingFont);
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
public const int MaxThumbnailSize = 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Tags the players can choose for their workshop items. These must match the ones defined in the Steamworks backend. They're case insensitive, but must otherwise match exactly for the tag filtering to work correctly.
|
||||
/// The localized names for these are fetched from the loca files with the identifier "workshop.contenttag.{tag.RemoveWhitespace()}".
|
||||
/// </summary>
|
||||
public static readonly ImmutableArray<Identifier> Tags = new []
|
||||
{
|
||||
"submarine",
|
||||
@@ -25,7 +29,7 @@ namespace Barotrauma.Steam
|
||||
"monster",
|
||||
"mission",
|
||||
"outpost",
|
||||
"beaconstation",
|
||||
"beacon station",
|
||||
"wreck",
|
||||
"ruin",
|
||||
"weapons",
|
||||
@@ -34,14 +38,14 @@ namespace Barotrauma.Steam
|
||||
"art",
|
||||
"event set",
|
||||
"total conversion",
|
||||
"gamemode",
|
||||
"gameplaymechanics",
|
||||
"game mode",
|
||||
"gameplay mechanics",
|
||||
"environment",
|
||||
"item assembly",
|
||||
"language",
|
||||
"qol",
|
||||
"clientside",
|
||||
"serverside",
|
||||
"client-side",
|
||||
"server-side",
|
||||
"outdated",
|
||||
"library"
|
||||
}.ToIdentifiers().ToImmutableArray();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.12.6.2</Version>
|
||||
<Version>1.12.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.12.6.2</Version>
|
||||
<Version>1.12.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.12.6.2</Version>
|
||||
<Version>1.12.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.12.6.2</Version>
|
||||
<Version>1.12.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.12.6.2</Version>
|
||||
<Version>1.12.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Barotrauma.LuaCs;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
|
||||
@@ -35,11 +35,11 @@ partial class NetworkingService : INetworkingService, IEventClientRawNetMessageR
|
||||
return message;
|
||||
}
|
||||
|
||||
public void OnReceivedClientNetMessage(IReadMessage netMessage, ClientPacketHeader clientPacketHeader, NetworkConnection sender)
|
||||
public bool? OnReceivedClientNetMessage(IReadMessage netMessage, ClientPacketHeader clientPacketHeader, NetworkConnection sender)
|
||||
{
|
||||
if (clientPacketHeader != ClientHeader)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
Client client = GameMain.Server.ConnectedClients.First(c => c.Connection == sender);
|
||||
@@ -64,6 +64,8 @@ partial class NetworkingService : INetworkingService, IEventClientRawNetMessageR
|
||||
RequestIdSingle(netMessage, client);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleNetMessageId(IReadMessage netMessage, Client client = null)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.12.6.2</Version>
|
||||
<Version>1.12.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
</Values>
|
||||
</Setting>
|
||||
<Setting Name="UseCaching" Type="bool" Value="true" AllowChangesWhileExecuting="false"/>
|
||||
<Setting Name="CsRunPolicySession" Type="bool" AllowChangesWhileExecuting="false" ShowInMenus="false"/>
|
||||
<Setting Name="IsCsEnabledForSession" Type="bool" AllowChangesWhileExecuting="false" ShowInMenus="false" Value="false"/>
|
||||
</Settings>
|
||||
</Configuration>
|
||||
|
||||
@@ -35,44 +35,4 @@ if not CSActive then
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
Networking.Receive("_luastart", function (message, client)
|
||||
local num = message.ReadUInt16()
|
||||
|
||||
local packages = {}
|
||||
|
||||
for i = 1, num, 1 do
|
||||
table.insert(packages, {
|
||||
Name = message.ReadString(),
|
||||
Version = message.ReadString(),
|
||||
Id = message.ReadUInt64(),
|
||||
Hash = message.ReadString()
|
||||
})
|
||||
end
|
||||
|
||||
Hook.Call("client.packages", client, packages)
|
||||
end)
|
||||
elseif Game.IsMultiplayer then
|
||||
local message = Networking.Start("_luastart")
|
||||
|
||||
local packageCount = 0
|
||||
for package in ContentPackageManager.EnabledPackages.All do packageCount = packageCount + 1 end
|
||||
|
||||
message.WriteUInt16(packageCount)
|
||||
|
||||
for package in ContentPackageManager.EnabledPackages.All do
|
||||
local id = package.UgcId
|
||||
local hash = package.Hash and package.Hash.StringRepresentation or ""
|
||||
|
||||
if id == nil then id = 0 end
|
||||
|
||||
message.WriteString(package.Name)
|
||||
message.WriteString(package.ModVersion)
|
||||
message.WriteUInt64(UInt64(id))
|
||||
message.WriteString(hash)
|
||||
end
|
||||
|
||||
Networking.Send(message)
|
||||
end
|
||||
|
||||
LuaSetup = nil
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<style>
|
||||
<SettingsMenuTab.LuaCsSettings color="169,212,187,255" hovercolor="220,220,220,255" selectedcolor="255,255,255,255" pressedcolor="100,100,100,255" disabledcolor="125,125,125,125">
|
||||
<Sprite name="LuaCsSettings" texture="%ModDir%/LuaCsSettingsIcon.png" sourcerect="0,0,64,64" tile="false" maintainaspectratio="true" origin="0.5,0.5"/>
|
||||
</SettingsMenuTab.LuaCsSettings>
|
||||
</style>
|
||||
@@ -2,6 +2,11 @@
|
||||
<infotexts language="English" nowhitespace="false" translatedname="English">
|
||||
<LuaCsForBarotrauma.SettingsMenu.ModControlsButton>Mod Controls Settings</LuaCsForBarotrauma.SettingsMenu.ModControlsButton>
|
||||
<LuaCsForBarotrauma.SettingsMenu.ModGameplayButton>Mod Gameplay Settings</LuaCsForBarotrauma.SettingsMenu.ModGameplayButton>
|
||||
<LuaCsForBarotrauma.SettingsMenu.ResetVisibleSettings>Reset Displayed Settings</LuaCsForBarotrauma.SettingsMenu.ResetVisibleSettings>
|
||||
<LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Title>Reset Visible Settings</LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Title>
|
||||
<LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Message>Are you sure you want to reset the values for currently displayed settings?</LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Message>
|
||||
<LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Yes>Yes</LuaCsForBarotrauma.SettingsMenu.ResetPrompt.Yes>
|
||||
<LuaCsForBarotrauma.SettingsMenu.ResetPrompt.No>No</LuaCsForBarotrauma.SettingsMenu.ResetPrompt.No>
|
||||
<!-- Settings -->
|
||||
<!-- Is Cs Enabled-->
|
||||
<LuaCsForBarotrauma.CsRunPolicy.DisplayName>Are C# Mods Allowed</LuaCsForBarotrauma.CsRunPolicy.DisplayName>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<contentpackage name="LuaCsForBarotrauma">
|
||||
<Text file="%ModDir%/Texts/English.xml"/>
|
||||
<UIStyle file="%ModDir%/Style.xml" />
|
||||
<!--<Text file="%ModDir%/Texts/Portuguese.xml"/>-->
|
||||
</contentpackage>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
AttachedByDefault="true" DisallowAttachingOverTags="container,planter,refuelableitem" DisallowAttachingOverSize="115,130">
|
||||
</Holdable>
|
||||
|
||||
<ItemContainer hideitems="false" drawinventory="true" ItemsUseInventoryPlacement="true" capacity="1" maxstacksize="1" canbeselected="true" itempos="-25,-20" iteminterval="0,0" itemrotation="0" msg="ItemMsgOxygenRefill" containedspritedepth="0.1">
|
||||
<ItemContainer hideitems="false" drawinventory="true" ItemsUseInventoryPlacement="true" capacity="1" maxstacksize="1" canbeselected="true" itempos="32,-83" iteminterval="0,0" itemrotation="0" msg="ItemMsgOxygenRefill" containedspritedepth="0.1">
|
||||
<GuiFrame relativesize="0.2,0.25" anchor="Center" minsize="140,170" maxsize="280,280" style="ItemUI" />
|
||||
<SlotIcon slotindex="0" texture="Content/UI/StatusMonitorUI.png" sourcerect="64,448,64,64" origin="0.5,0.5" />
|
||||
<Containable items="oxygensource" />
|
||||
|
||||
Binary file not shown.
@@ -24,29 +24,52 @@ Hook.Add("missionsEnded", "test", function()
|
||||
print("missionsEnded")
|
||||
end)
|
||||
|
||||
-- cfg tests
|
||||
local str = "CLIENT: "
|
||||
|
||||
if SERVER then
|
||||
str = "SERVER: "
|
||||
end
|
||||
|
||||
function OnChanged(cfg)
|
||||
print(str, "cfg value for ", cfg.InternalName, " changed to ", cfg.Value)
|
||||
end
|
||||
|
||||
local failed, package = trygetpackage("[DebugOnlyTest]TestLuaMod")
|
||||
|
||||
print("packageFailed=", failed)
|
||||
print("package", package.Name)
|
||||
|
||||
local success, config = ConfigService.TryGetConfig(SettingBase.Single, package, "TestFloat")
|
||||
local success, config = ConfigService.TryGetConfig(SettingBase.Int32, package, "TestSynchroServer")
|
||||
local success2, config2 = ConfigService.TryGetConfig(SettingBase.Int32, package, "TestSynchroClient")
|
||||
|
||||
local success2, config2 = ConfigService.TryGetConfig(SettingBase.Int32, package, "TestSynchroServer")
|
||||
local success3, config3 = ConfigService.TryGetConfig(SettingBase.Int32, package, "TestSynchroClient")
|
||||
if not success or not success2 then
|
||||
print("Failed to get configs.")
|
||||
return
|
||||
end
|
||||
|
||||
print("config ", success, " ", config.Value)
|
||||
print("config testsynchrosrv", success2, " ", config2.Value)
|
||||
print("config testsynchrocli", success3, " ", config3.Value)
|
||||
config.OnValueChanged.add(OnChanged)
|
||||
config2.OnValueChanged.add(OnChanged)
|
||||
|
||||
local lastTime = 0
|
||||
print(str, " testsynchroclient=", config2.Value)
|
||||
print(str, " testsynchroserver=", config.Value)
|
||||
|
||||
-- The server should keep updating the value and it should show up on the client.
|
||||
-- The client should try updating and it should fail.
|
||||
|
||||
local lastTime = Timer.Time + 30 -- give time to join
|
||||
|
||||
Hook.Add("think", "printconfig", function()
|
||||
if lastTime > Timer.Time then return end
|
||||
if lastTime > Timer.Time then return end
|
||||
lastTime = Timer.Time + 10
|
||||
|
||||
lastTime = Timer.Time + 10
|
||||
print(config.Value)
|
||||
|
||||
if SERVER then
|
||||
config.TrySetValue(config.Value + 1)
|
||||
end
|
||||
if SERVER then
|
||||
local succ = config.TrySetValue(config.Value + 1)
|
||||
print("Success of setting value on server for '", config.InternalName,"': ", succ)
|
||||
end
|
||||
if CLIENT then
|
||||
local succ = config.TrySetValue(config.Value + 1)
|
||||
print("Success of setting value on client for '", config.InternalName,"': ", succ, " | This should fail if permissions are not set for client.")
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
<ModConfig>
|
||||
<Lua File="%ModDir%/Lua/init.lua" IsAutorun="true" />
|
||||
<Config File="%ModDir%/Settings.xml"/>
|
||||
<Config File="%ModDir%/SettingsClient.xml" Target="Client"/>
|
||||
<Config File="%ModDir%/SettingsServer.xml" Target="Server"/>
|
||||
</ModConfig>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Configuration>
|
||||
<Settings>
|
||||
<Setting Name="TestTickbox" Type="bool"/>
|
||||
<Setting Name="TestSynchroClient" Type="int" NetSync="TwoWay" />
|
||||
<Setting Name="TestSynchroServer" Type="int" NetSync="ServerAuthority" />
|
||||
<Setting Name="TestFloat" Type="float" NetSync="ServerAuthority" />
|
||||
<Setting Name="TestHidden" Type="bool" ShowInMenus="false"/>
|
||||
<Setting Name="TestRangeFloat" Type="rangeFloat" Min="0" Max="25" Steps="11"/>
|
||||
<Setting Name="TestRangeInt" Type="rangeInt" Min="0" Max="10" Steps="11"/>
|
||||
<Setting Name="TestString" Type="string" />
|
||||
<Setting Name="TestTickbox" Type="bool" Value="true"/>
|
||||
<Setting Name="TestSynchroClient" Type="int" NetSync="TwoWay" Value="40"/>
|
||||
<Setting Name="TestSynchroServer" Type="int" NetSync="ServerAuthority" Value="25"/>
|
||||
<Setting Name="TestFloat" Type="float" Value="3498"/>
|
||||
<Setting Name="TestHidden" Type="bool" ShowInMenus="false" Value="false"/>
|
||||
<Setting Name="TestRangeFloat" Type="rangeFloat" Min="0" Max="25" Steps="11" Value="4.5"/>
|
||||
<Setting Name="TestRangeInt" Type="rangeInt" Min="0" Max="10" Steps="11" Value="4"/>
|
||||
<Setting Name="TestString" Type="string" Value="ok"/>
|
||||
<Setting Name="TestControl" Type="control" Value="A"/>
|
||||
<Setting Name="TestDropdownList" Type="listString" Value="Hi">
|
||||
<Values>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Configuration>
|
||||
<!-- Should match 'SettingsServer'. We define these on the client and server separately to give different values. -->
|
||||
<Settings>
|
||||
<Setting Name="TestSynchroClient" Type="int" NetSync="TwoWay" Value="3545" ShowInMenus="false"/>
|
||||
<Setting Name="TestSynchroServer" Type="int" NetSync="ServerAuthority" Value="577" ShowInMenus="false"/>
|
||||
</Settings>
|
||||
</Configuration>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Configuration>
|
||||
<!-- Should match 'SettingsClient'. We define these on the client and server separately to give different values. -->
|
||||
<Settings>
|
||||
<Setting Name="TestSynchroClient" Type="int" NetSync="TwoWay" Value="40" ShowInMenus="false"/>
|
||||
<Setting Name="TestSynchroServer" Type="int" NetSync="ServerAuthority" Value="25" ShowInMenus="false"/>
|
||||
</Settings>
|
||||
</Configuration>
|
||||
@@ -242,10 +242,13 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The monster won't try to damage these submarines
|
||||
/// The monster won't try to damage these submarines. Applies to hulls, structures and static items (items without a physics body) belonging to these submarines. Does not apply to non-static items, e.g. flares or other provocative items.
|
||||
/// </summary>
|
||||
private readonly HashSet<Submarine> unattackableSubmarines = [];
|
||||
|
||||
/// <summary>
|
||||
/// Set the submarine(s) the monster won't attack. Applies to hulls, structures and static items (items without a physics body) belonging to these submarines. Does not apply to non-static items, e.g. flares or other provocative items.
|
||||
/// </summary>
|
||||
private readonly HashSet<Submarine> unattackableSubmarines = new HashSet<Submarine>();
|
||||
|
||||
public void SetUnattackableSubmarines(Submarine submarine, bool includeOwnSub = true, bool includeConnectedSubs = true, bool clearExisting = true)
|
||||
{
|
||||
if (clearExisting)
|
||||
@@ -3075,11 +3078,17 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore all structures, items, and hulls inside these subs.
|
||||
if (aiTarget.Entity.Submarine != null)
|
||||
if (aiTarget.Entity.Submarine != null)
|
||||
{
|
||||
//ignore all items, structures and hulls in wrecks and beacon stations
|
||||
//(we don't want monsters to be distracted by them during missions,
|
||||
//nor have monsters inside them attack "their home" rather than the player)
|
||||
if (aiTarget.Entity.Submarine.Info.IsWreck ||
|
||||
aiTarget.Entity.Submarine.Info.IsBeacon ||
|
||||
aiTarget.Entity.Submarine.Info.IsBeacon)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (aiTarget.Entity is Structure or Hull or Item { body: null } &&
|
||||
unattackableSubmarines.Contains(aiTarget.Entity.Submarine))
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -590,6 +590,11 @@ namespace Barotrauma
|
||||
package.UgcId.TryUnwrap(out var ugcId) && ugcId is SteamWorkshopId workshopId && workshopId.Value == childUgcItemId.Value));
|
||||
foreach (var missingChild in missingChildren)
|
||||
{
|
||||
if (missingChild.ToString() == "2559634234" ||
|
||||
missingChild.ToString() == "2795927223")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
enabledPackage.AddMissingDependency(missingChild);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position where the contained items get drawn at. In the case of items with a physics body, the offset is from the center of the body, on items without one from the top-left corner of the sprite. In pixels.")]
|
||||
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position where the contained items get drawn at (offset from the upper left corner of the sprite in pixels).")]
|
||||
public Vector2 ItemPos { get; set; }
|
||||
|
||||
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The interval at which the contained items are spaced apart from each other (in pixels).")]
|
||||
@@ -1093,25 +1093,21 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (item.body == null)
|
||||
{
|
||||
//if the item is a holdable item currently attached to a wall (i.e. normally has a physics body, but the body is now disabled),
|
||||
//we must position the contained items using the center as the origin since the item positions have been configured with the assumption the item has a body
|
||||
bool isAttachedHoldable = item.GetComponent<Holdable>() is { Attached: true };
|
||||
bool useCenterAsOrigin = isAttachedHoldable;
|
||||
if (flippedX)
|
||||
{
|
||||
transformedItemPos.X = -transformedItemPos.X;
|
||||
if (!useCenterAsOrigin) { transformedItemPos.X += item.Rect.Width; }
|
||||
transformedItemPos.X += item.Rect.Width;
|
||||
transformedItemInterval.X = -transformedItemInterval.X;
|
||||
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
|
||||
}
|
||||
if (flippedY)
|
||||
{
|
||||
transformedItemPos.Y = -transformedItemPos.Y;
|
||||
if (!useCenterAsOrigin) { transformedItemPos.Y -= item.Rect.Height; }
|
||||
transformedItemPos.Y -= item.Rect.Height;
|
||||
transformedItemInterval.Y = -transformedItemInterval.Y;
|
||||
transformedItemIntervalVertical.Y = -transformedItemIntervalVertical.Y;
|
||||
}
|
||||
transformedItemPos += useCenterAsOrigin ? item.Position : new Vector2(item.Rect.X, item.Rect.Y);
|
||||
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
|
||||
if (drawPosition)
|
||||
{
|
||||
if (item.Submarine != null) { transformedItemPos += item.Submarine.DrawPosition; }
|
||||
@@ -1129,6 +1125,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.GetComponent<Holdable>() is { Attachable: true })
|
||||
{
|
||||
//if the item is attachable to walls, we need a bit of special logic because the item can either
|
||||
//have or not have a body depending on whether it's attached.
|
||||
|
||||
//since it seems previously the contained item positions have always been configured as if the item had no body (using the top-left corner as the origin),
|
||||
//let's modify the position here to position the items correctly even when the body is active (moving the origin from the center of the body to the top-left corner)
|
||||
transformedItemPos -= item.Rect.Size.FlipY().ToVector2() / 2;
|
||||
}
|
||||
|
||||
Matrix transform = Matrix.CreateRotationZ(drawPosition ? item.body.DrawRotation : item.body.Rotation);
|
||||
if (bodyFlipped)
|
||||
{
|
||||
|
||||
@@ -862,10 +862,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (CanBeSelectedByCharacters)
|
||||
{
|
||||
#if SERVER
|
||||
GameServer.Log($"{GameServer.CharacterLogName(activator)} entered {item.Name}", ServerLog.MessageType.ItemInteraction);
|
||||
#endif
|
||||
|
||||
activator.DeselectCharacter();
|
||||
|
||||
User = activator;
|
||||
|
||||
@@ -11,6 +11,7 @@ public interface ILuaCsHook : ILuaPatcher, ILuaCsShim
|
||||
void Add(string eventName, string identifier, LuaCsFunc callback, object owner = null);
|
||||
[Obsolete("ACsMod is deprecated. Use ILuaEventService.Add() instead.")]
|
||||
void Add(string eventName, LuaCsFunc callback, object owner = null);
|
||||
void Remove(string eventName, string identifier);
|
||||
// Does anyone use this? TODO: Analyze old Lua mods for usage scenarios.
|
||||
//bool Exists(string eventName, string identifier);
|
||||
object Call(string eventName, params object[] args);
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
using Barotrauma;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
class LuaCsConfig
|
||||
{
|
||||
private enum ValueType
|
||||
{
|
||||
None,
|
||||
Text,
|
||||
Integer,
|
||||
Decimal,
|
||||
Boolean,
|
||||
Collection,
|
||||
Object,
|
||||
Enum
|
||||
}
|
||||
|
||||
private static Type[] LoadDocTypes(XElement typesElem)
|
||||
{
|
||||
var result = new List<Type>();
|
||||
var loadedTypes = AssemblyLoadContext.All
|
||||
.Where(alc => alc != AssemblyLoadContext.Default)
|
||||
.SelectMany(alc => alc.Assemblies)
|
||||
.SelectMany(asm => asm.GetTypes())
|
||||
.ToImmutableArray();
|
||||
|
||||
foreach (var elem in typesElem.Elements())
|
||||
{
|
||||
var typesFound = loadedTypes.Where(t => t.FullName?.EndsWith(elem.Value) ?? false).ToImmutableList();
|
||||
if (!typesFound.Any())
|
||||
{
|
||||
ModUtils.Logging.PrintError(
|
||||
$"{nameof(LuaCsConfig)}::{nameof(LoadDocTypes)}() | Unable to find a matching type for {elem.Value}");
|
||||
continue;
|
||||
}
|
||||
result.AddRange(typesFound);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static IEnumerable<XElement> SaveDocTypes(IEnumerable<Type> types)
|
||||
{
|
||||
return types.Select(t => new XElement("Type", t.ToString()));
|
||||
}
|
||||
|
||||
private static Type GetTypeAttr(Type[] types, XElement elem)
|
||||
{
|
||||
var idx = elem.GetAttributeInt("Type", -1);
|
||||
if (idx < 0 || idx >= types.Length) throw new Exception($"Type index '{idx}' is outside of saved types bounds");
|
||||
return types[idx];
|
||||
}
|
||||
private static ValueType GetValueType(XElement elem)
|
||||
{
|
||||
Enum.TryParse(typeof(ValueType), elem.Attribute("Value")?.Value, out object result);
|
||||
if (result != null) return (ValueType)result;
|
||||
else return ValueType.None;
|
||||
}
|
||||
private static object ParseValue(Type[] types, XElement elem)
|
||||
{
|
||||
var type = GetValueType(elem);
|
||||
|
||||
if (elem.IsEmpty) return null;
|
||||
if (type == ValueType.Enum)
|
||||
{
|
||||
var tType = GetTypeAttr(types, elem);
|
||||
if (tType == null || !tType.IsSubclassOf(typeof(Enum))) return null;
|
||||
if (Enum.TryParse(tType, elem.Value, out object result)) return result;
|
||||
else return null;
|
||||
}
|
||||
if (type == ValueType.Collection)
|
||||
{
|
||||
var tType = GetTypeAttr(types, elem);
|
||||
var tInt = tType.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||
var gArg = tInt.GetGenericArguments()[0];
|
||||
if (tType == null || !tType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))) return null;
|
||||
|
||||
object result = null;
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c =>
|
||||
{
|
||||
var param = c.GetParameters();
|
||||
return param.Count() == 1 && param.Any(p => p.ParameterType.IsGenericType && p.ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||
});
|
||||
if (ctor != null)
|
||||
{
|
||||
var elements = elem.Elements().Select(x => ParseValue(types, x));
|
||||
var castElems = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(gArg).Invoke(elements, new object[] { elements });
|
||||
result = ctor.Invoke(new object[] { castElems });
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c => c.GetParameters().Count() == 0);
|
||||
var addMethod = tType.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(m =>
|
||||
{
|
||||
if (m.Name != "Add") return false;
|
||||
var param = m.GetParameters();
|
||||
return param.Count() == 1 && param[0].ParameterType == gArg;
|
||||
});
|
||||
if (ctor != null && addMethod != null)
|
||||
{
|
||||
var elements = elem.Elements().Select(x => ParseValue(types, x));
|
||||
result = ctor.Invoke(null);
|
||||
foreach (var el in elements) addMethod.Invoke(result, new object[] { el });
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault();
|
||||
var setMethod = tType.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(m =>
|
||||
{
|
||||
if (m.Name != "Set") return false;
|
||||
var param = m.GetParameters();
|
||||
return param.Count() == 2 && param[0].ParameterType == typeof(int) && param[1].ParameterType == gArg;
|
||||
});
|
||||
if (ctor != null || setMethod != null)
|
||||
{
|
||||
var elements = elem.Elements().Select(x => ParseValue(types, x));
|
||||
result = ctor.Invoke(new object[] { elements.Count() });
|
||||
int i = 0;
|
||||
foreach (var el in elements)
|
||||
{
|
||||
setMethod.Invoke(result, new object[] { i, el });
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else if (type == ValueType.Text) return elem.Value;
|
||||
else if (type == ValueType.Integer)
|
||||
{
|
||||
int.TryParse(elem.Value, out var num);
|
||||
return num;
|
||||
}
|
||||
else if (type == ValueType.Decimal)
|
||||
{
|
||||
float.TryParse(elem.Value, out var num);
|
||||
return num;
|
||||
}
|
||||
else if (type == ValueType.Boolean)
|
||||
{
|
||||
bool.TryParse(elem.Value, out var boolean);
|
||||
return boolean;
|
||||
}
|
||||
else if (type == ValueType.Object)
|
||||
{
|
||||
var tType = GetTypeAttr(types, elem);
|
||||
if (tType == null) return null;
|
||||
|
||||
IEnumerable<FieldInfo> fields = tType.GetFields(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Concat(tType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic));
|
||||
IEnumerable<PropertyInfo> properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.GetSetMethod() != null)
|
||||
.Concat(tType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic).Where(p => p.GetSetMethod() != null));
|
||||
|
||||
object result = null;
|
||||
var ctor = tType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c => c.GetParameters().Count() == 0);
|
||||
if (ctor == null)
|
||||
{
|
||||
if (!tType.IsValueType) return null;
|
||||
result = Activator.CreateInstance(tType);
|
||||
}
|
||||
else result = ctor.Invoke(null);
|
||||
|
||||
foreach (var el in elem.Elements())
|
||||
{
|
||||
var value = ParseValue(types, el);
|
||||
|
||||
var field = fields.FirstOrDefault(f => f.Name == el.Name.LocalName);
|
||||
if (field != null) field.SetValue(result, value);
|
||||
var property = properties.FirstOrDefault(p => p.Name == el.Name.LocalName);
|
||||
if (property != null) property.SetValue(result, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else return elem.Value;
|
||||
|
||||
}
|
||||
|
||||
private static void AddTypeAttr(List<Type> types, Type type, XElement elem)
|
||||
{
|
||||
if (!types.Contains(type)) types.Add(type);
|
||||
elem.SetAttributeValue("Type", types.IndexOf(type));
|
||||
}
|
||||
|
||||
private static XElement ParseObject(List<Type> types, string name, object value)
|
||||
{
|
||||
XElement result = new XElement(name);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
var tType = value.GetType();
|
||||
|
||||
if (tType.IsEnum)
|
||||
{
|
||||
result.SetAttributeValue("Value", ValueType.Enum);
|
||||
AddTypeAttr(types, tType, result);
|
||||
|
||||
result.Value = Enum.GetName(tType, value) ?? "";
|
||||
}
|
||||
else if (value is string str)
|
||||
{
|
||||
result.SetAttributeValue("Value", ValueType.Text);
|
||||
result.Value = str;
|
||||
}
|
||||
else if (value is int integer)
|
||||
{
|
||||
result.SetAttributeValue("Value", ValueType.Integer);
|
||||
result.Value = integer.ToString();
|
||||
}
|
||||
else if (value is float || value is double)
|
||||
{
|
||||
result.SetAttributeValue("Value", ValueType.Decimal);
|
||||
result.Value = value.ToString();
|
||||
}
|
||||
else if (value is bool boolean)
|
||||
{
|
||||
result.SetAttributeValue("Value", ValueType.Boolean);
|
||||
result.Value = boolean.ToString();
|
||||
}
|
||||
else if (tType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
|
||||
{
|
||||
result.SetAttributeValue("Value", ValueType.Collection);
|
||||
AddTypeAttr(types, tType, result);
|
||||
|
||||
var enumerator = (IEnumerator)tType.GetMethod("GetEnumerator").Invoke(value, null);
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var elVal = ParseObject(types, "Item", enumerator.Current);
|
||||
result.Add(elVal);
|
||||
}
|
||||
}
|
||||
else if (tType.IsClass || tType.IsValueType)
|
||||
{
|
||||
result.SetAttributeValue("Value", ValueType.Object);
|
||||
AddTypeAttr(types, tType, result);
|
||||
|
||||
IEnumerable<FieldInfo> fields = tType.GetFields(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Concat(tType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic));
|
||||
IEnumerable<PropertyInfo> properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.GetSetMethod() != null)
|
||||
.Concat(tType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic).Where(p => p.GetSetMethod() != null));
|
||||
|
||||
foreach (var field in fields) result.Add(ParseObject(types, field.Name, field.GetValue(value)));
|
||||
foreach (var property in properties) result.Add(ParseObject(types, property.Name, property.GetValue(value)));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.SetAttributeValue("Value", ValueType.None);
|
||||
result.Value = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static T Load<T>(FileStream file)
|
||||
{
|
||||
var doc = XDocument.Load(file);
|
||||
|
||||
var rootElems = doc.Root.Elements().ToArray();
|
||||
var types = rootElems[0];
|
||||
var elem = rootElems[1];
|
||||
|
||||
var dict = ParseValue(LoadDocTypes(types), elem);
|
||||
if (dict.GetType() == typeof(T)) return (T)dict;
|
||||
else throw new Exception($"Loaded configuration is not of the type '{typeof(T).Name}'");
|
||||
}
|
||||
|
||||
public static void Save(FileStream file, object obj)
|
||||
{
|
||||
var types = new List<Type>();
|
||||
var elem = ParseObject(types, "Root", obj);
|
||||
var root = new XElement("Configuration", new XElement("Types", SaveDocTypes(types)), elem);
|
||||
|
||||
var doc = new XDocument(root);
|
||||
doc.Save(file);
|
||||
}
|
||||
|
||||
public static T Load<T>(string path)
|
||||
{
|
||||
using (var file = LuaCsFile.OpenRead(path)) return Load<T>(file);
|
||||
}
|
||||
|
||||
public static void Save(string path, object obj)
|
||||
{
|
||||
using (var file = LuaCsFile.OpenWrite(path)) Save(file, obj);
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ public record ConfigResourceInfo : BaseResourceInfo, IConfigResourceInfo {}
|
||||
public record LuaScriptsResourceInfo : BaseResourceInfo, ILuaScriptResourceInfo
|
||||
{
|
||||
public bool IsAutorun { get; init; }
|
||||
public bool RunUnrestricted { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -21,6 +21,12 @@ public interface ILuaScriptResourceInfo : IBaseResourceInfo
|
||||
/// </summary>
|
||||
[XmlAttribute("IsAutorun")]
|
||||
public bool IsAutorun { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this lua resources needs to run outside sandbox/requires unrestricted access.
|
||||
/// </summary>
|
||||
[XmlAttribute("RunUnrestricted")]
|
||||
public bool RunUnrestricted { get; }
|
||||
}
|
||||
|
||||
public interface IAssemblyResourceInfo : IBaseResourceInfo
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.Networking;
|
||||
using OneOf;
|
||||
|
||||
namespace Barotrauma.LuaCs.Data;
|
||||
|
||||
@@ -34,8 +35,8 @@ public partial interface ISettingBase : IDataInfo, IEquatable<ISettingBase>, IDi
|
||||
Type GetValueType();
|
||||
string GetStringValue();
|
||||
string GetDefaultStringValue();
|
||||
bool TrySetValue(OneOf.OneOf<string, XElement> value);
|
||||
event Action<ISettingBase> OnValueChanged;
|
||||
bool TrySetSerializedValue(OneOf<string, XElement> value);
|
||||
event Action<ISettingBase> OnValueChanged;
|
||||
OneOf.OneOf<string, XElement> GetSerializableValue();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OneOf;
|
||||
|
||||
@@ -10,6 +12,7 @@ public abstract class SettingBase : ISettingBase
|
||||
{
|
||||
protected SettingBase(IConfigInfo configInfo)
|
||||
{
|
||||
Guard.IsNotNull(configInfo, nameof(configInfo));
|
||||
ConfigInfo = configInfo;
|
||||
}
|
||||
|
||||
@@ -57,8 +60,8 @@ public abstract class SettingBase : ISettingBase
|
||||
public abstract Type GetValueType();
|
||||
public abstract string GetStringValue();
|
||||
public abstract string GetDefaultStringValue();
|
||||
public abstract bool TrySetSerializedValue(OneOf<string, XElement> value);
|
||||
|
||||
public abstract bool TrySetValue(OneOf<string, XElement> value);
|
||||
public abstract event Action<ISettingBase> OnValueChanged;
|
||||
public abstract OneOf<string, XElement> GetSerializableValue();
|
||||
#if CLIENT
|
||||
|
||||
@@ -147,12 +147,10 @@ public partial class SettingEntry<T> : SettingBase, ISettingBase<T>, INetworkSyn
|
||||
}
|
||||
|
||||
public override Type GetValueType() => typeof(T);
|
||||
|
||||
public override string GetStringValue() => Value?.ToString() ?? string.Empty;
|
||||
|
||||
public override string GetDefaultStringValue() => DefaultValue.ToString();
|
||||
public override string GetDefaultStringValue() => DefaultValue?.ToString() ?? string.Empty;
|
||||
|
||||
public override bool TrySetValue(OneOf<string, XElement> value)
|
||||
public override bool TrySetSerializedValue(OneOf<string, XElement> value)
|
||||
{
|
||||
bool isFailed = false;
|
||||
var typeConvertedValue = value.Match<T>(
|
||||
@@ -181,7 +179,6 @@ public partial class SettingEntry<T> : SettingBase, ISettingBase<T>, INetworkSyn
|
||||
return default(T);
|
||||
}
|
||||
});
|
||||
|
||||
return !isFailed && TrySetValue(typeConvertedValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,13 @@ internal interface IEventModifyChatMessage : IEvent<IEventModifyChatMessage>
|
||||
/// <returns>Whether to <b><i>reject</i></b> the message.</returns>
|
||||
public bool? OnModifyMessagePredicate(ChatMessage message, WifiComponent senderRadio)
|
||||
{
|
||||
return (bool?)LuaFuncs[nameof(OnModifyMessagePredicate)](message, senderRadio);
|
||||
object result = LuaFuncs[nameof(OnModifyMessagePredicate)](message, senderRadio);
|
||||
if (result is DynValue dynValue && dynValue.Type == DataType.Boolean)
|
||||
{
|
||||
return dynValue.Boolean;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -953,7 +959,7 @@ interface IEventInventoryItemSwap : IEvent<IEventInventoryItemSwap>
|
||||
#if SERVER
|
||||
public interface IEventClientRawNetMessageReceived : IEvent<IEventClientRawNetMessageReceived>
|
||||
{
|
||||
void OnReceivedClientNetMessage(IReadMessage netMessage, ClientPacketHeader clientPacketHeader, NetworkConnection sender);
|
||||
bool? OnReceivedClientNetMessage(IReadMessage netMessage, ClientPacketHeader clientPacketHeader, NetworkConnection sender);
|
||||
|
||||
static IEventClientRawNetMessageReceived IEvent<IEventClientRawNetMessageReceived>.GetLuaRunner(IDictionary<string, LuaCsFunc> luaFunc)
|
||||
=> new LuaWrapper(luaFunc);
|
||||
@@ -964,15 +970,22 @@ public interface IEventClientRawNetMessageReceived : IEvent<IEventClientRawNetMe
|
||||
{
|
||||
}
|
||||
|
||||
public void OnReceivedClientNetMessage(IReadMessage netMessage, ClientPacketHeader clientPacketHeader, NetworkConnection sender)
|
||||
public bool? OnReceivedClientNetMessage(IReadMessage netMessage, ClientPacketHeader clientPacketHeader, NetworkConnection sender)
|
||||
{
|
||||
if (GameMain.Server == null) { return; }
|
||||
if (GameMain.Server == null) { return null; }
|
||||
|
||||
Client client = GameMain.Server.ConnectedClients.FirstOrDefault(c => c.Connection == sender);
|
||||
|
||||
if (client == null) { return; }
|
||||
if (client == null) { return null; }
|
||||
|
||||
LuaFuncs[nameof(OnReceivedClientNetMessage)](netMessage, clientPacketHeader, client);
|
||||
var result = LuaFuncs[nameof(OnReceivedClientNetMessage)](netMessage, clientPacketHeader, client);
|
||||
|
||||
if (result is DynValue dynValue && dynValue.Type == DataType.Boolean)
|
||||
{
|
||||
return dynValue.Boolean;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1063,7 +1076,7 @@ interface IEventJobsAssigned : IEvent<IEventJobsAssigned>
|
||||
|
||||
public interface IEventServerRawNetMessageReceived : IEvent<IEventServerRawNetMessageReceived>
|
||||
{
|
||||
void OnReceivedServerNetMessage(IReadMessage netMessage, ServerPacketHeader serverPacketHeader);
|
||||
bool? OnReceivedServerNetMessage(IReadMessage netMessage, ServerPacketHeader serverPacketHeader);
|
||||
|
||||
static IEventServerRawNetMessageReceived IEvent<IEventServerRawNetMessageReceived>.GetLuaRunner(IDictionary<string, LuaCsFunc> luaFunc)
|
||||
=> new LuaWrapper(luaFunc);
|
||||
@@ -1074,9 +1087,15 @@ public interface IEventServerRawNetMessageReceived : IEvent<IEventServerRawNetMe
|
||||
{
|
||||
}
|
||||
|
||||
public void OnReceivedServerNetMessage(IReadMessage netMessage, ServerPacketHeader serverPacketHeader)
|
||||
public bool? OnReceivedServerNetMessage(IReadMessage netMessage, ServerPacketHeader serverPacketHeader)
|
||||
{
|
||||
LuaFuncs[nameof(OnReceivedServerNetMessage)](netMessage, serverPacketHeader);
|
||||
var result = LuaFuncs[nameof(OnReceivedServerNetMessage)](netMessage, serverPacketHeader);
|
||||
if (result is DynValue dynValue && dynValue.Type == DataType.Boolean)
|
||||
{
|
||||
return dynValue.Boolean;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,16 @@ namespace Barotrauma
|
||||
partial class LuaCsSetup : IDisposable, IEventScreenSelected, IEventEnabledPackageListChanged,
|
||||
IEventReloadAllPackages
|
||||
{
|
||||
public const string PackageId = "LuaCsForBarotrauma";
|
||||
public const string PackageName = "LuaCsForBarotrauma";
|
||||
|
||||
private static LuaCsSetup _luaCsSetup;
|
||||
public static LuaCsSetup Instance => _luaCsSetup ??= new LuaCsSetup();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The index of the last Vanilla command.
|
||||
/// </summary>
|
||||
public static int DebugConsoleCommandVanillaIndex { get; private set; }
|
||||
|
||||
private LuaCsSetup()
|
||||
{
|
||||
if (_luaCsSetup != null)
|
||||
@@ -37,6 +42,8 @@ namespace Barotrauma
|
||||
throw new Exception("Tried to create another LuaCsSetup instance");
|
||||
}
|
||||
|
||||
DebugConsoleCommandVanillaIndex = DebugConsole.Commands.Count;
|
||||
|
||||
// == startup
|
||||
_servicesProvider = SetupServicesProvider();
|
||||
_runStateMachine = SetupStateMachine();
|
||||
@@ -83,7 +90,26 @@ namespace Barotrauma
|
||||
// hotpath performance ref cache
|
||||
private LuaGame _game;
|
||||
public LuaGame Game => _game ??= _servicesProvider.GetService<LuaGame>();
|
||||
public Script Lua => LuaScriptManagementService.InternalScript;
|
||||
|
||||
private ISettingBase<bool> _isCsEnabledForSession;
|
||||
public bool IsCsEnabledForSession
|
||||
{
|
||||
get => _isCsEnabledForSession?.Value ?? false;
|
||||
internal set
|
||||
{
|
||||
_isCsEnabledForSession?.TrySetValue(value);
|
||||
if (_isCsEnabledForSession != null)
|
||||
{
|
||||
if (_isCsEnabledForSession.GetConfigInfo() == null)
|
||||
{
|
||||
Logger.LogError($"Config info was nil while trying to save {IsCsEnabledForSession}");
|
||||
return;
|
||||
}
|
||||
ConfigService.SaveConfigValue(_isCsEnabledForSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether C# plugin code is enabled.
|
||||
@@ -91,15 +117,17 @@ namespace Barotrauma
|
||||
public bool IsCsEnabled
|
||||
{
|
||||
#if CLIENT
|
||||
get => _csRunPolicy?.Value == "Enabled" || _isCsEnabledForSession;
|
||||
get => _csRunPolicy?.Value == "Enabled" || IsCsEnabledForSession;
|
||||
#elif SERVER
|
||||
// cs settings cannot be changed on the server after launch
|
||||
get => _csRunPolicy?.Value is "Enabled" or "Prompt";
|
||||
#endif
|
||||
}
|
||||
|
||||
private ISettingList<string> _csRunPolicy;
|
||||
private bool ShouldPromptForCs => _csRunPolicy?.Value is "Prompt";
|
||||
|
||||
|
||||
public string CsRunPolicyValue => _csRunPolicy?.Value ?? "Prompt";
|
||||
|
||||
/// <summary>
|
||||
/// Whether usernames are anonymized or show in logs.
|
||||
/// </summary>
|
||||
@@ -118,9 +146,9 @@ namespace Barotrauma
|
||||
|
||||
public static ContentPackage GetLuaCsPackage()
|
||||
{
|
||||
return ContentPackageManager.EnabledPackages.Regular.FirstOrDefault(cp => cp.NameMatches(PackageId), null)
|
||||
?? ContentPackageManager.LocalPackages.FirstOrDefault(cp => cp.NameMatches(PackageId))
|
||||
?? ContentPackageManager.WorkshopPackages.FirstOrDefault(cp => cp.NameMatches(PackageId));
|
||||
return ContentPackageManager.EnabledPackages.Regular.FirstOrDefault(cp => cp.NameMatches(PackageName), null)
|
||||
?? ContentPackageManager.LocalPackages.FirstOrDefault(cp => cp.NameMatches(PackageName))
|
||||
?? ContentPackageManager.WorkshopPackages.FirstOrDefault(cp => cp.NameMatches(PackageName));
|
||||
}
|
||||
|
||||
void LoadLuaCsConfig()
|
||||
@@ -139,6 +167,17 @@ namespace Barotrauma
|
||||
ConfigService.TryGetConfig<ISettingBase<bool>>(luaCsPackage, "UseCaching", out var val5)
|
||||
? val5
|
||||
: null;
|
||||
_isCsEnabledForSession =
|
||||
ConfigService.TryGetConfig<ISettingBase<bool>>(luaCsPackage, "IsCsEnabledForSession", out var val6)
|
||||
? val6
|
||||
: null;
|
||||
|
||||
if (!ContentPackageManager.EnabledPackages.All.Contains(luaCsPackage))
|
||||
{
|
||||
// sorry perfidius (not sorry)
|
||||
luaCsPackage.UnloadFilesOfType<TextFile>();
|
||||
luaCsPackage.LoadFilesOfType<TextFile>();
|
||||
}
|
||||
}
|
||||
|
||||
private IServicesProvider SetupServicesProvider()
|
||||
@@ -223,22 +262,13 @@ namespace Barotrauma
|
||||
|
||||
public void OnReloadAllPackages()
|
||||
{
|
||||
if (CurrentRunState <= RunState.Unloaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
#if CLIENT
|
||||
bool prevCsEnabled = _isCsEnabledForSession;
|
||||
#endif
|
||||
var state = CurrentRunState;
|
||||
SetRunState(RunState.Unloaded);
|
||||
#if CLIENT
|
||||
_isCsEnabledForSession = prevCsEnabled;
|
||||
#endif
|
||||
SetRunState(state);
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
SetRunState(RunState.Running);
|
||||
},0.25f);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -256,10 +286,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
this.Logger.LogResults(PackageManagementService.SyncLoadedPackagesList(GetLuaCsEnabledPackagesList(packages)));
|
||||
ConfigService.LoadSavedConfigsValues();
|
||||
SetRunState(state); // restore
|
||||
}
|
||||
|
||||
private void SetRunState(RunState targetRunState)
|
||||
public void SetRunState(RunState targetRunState)
|
||||
{
|
||||
if (CurrentRunState == targetRunState)
|
||||
{
|
||||
@@ -274,17 +305,13 @@ namespace Barotrauma
|
||||
|
||||
private ImmutableArray<ContentPackage> GetLuaCsEnabledPackagesList(ImmutableArray<ContentPackage> enabledRegular)
|
||||
{
|
||||
if (!enabledRegular.Any(
|
||||
p => p.Name.Equals("LuaCsForBarotrauma", StringComparison.InvariantCultureIgnoreCase)
|
||||
|| p.Name.Equals("Lua for Barotrauma", StringComparison.InvariantCultureIgnoreCase)))
|
||||
if (!enabledRegular.Any(p => p.Name.Equals(PackageName, StringComparison.InvariantCultureIgnoreCase)))
|
||||
{
|
||||
var luaCs = ContentPackageManager.AllPackages.FirstOrDefault(
|
||||
p => p.Name.Equals("LuaCsForBarotrauma", StringComparison.InvariantCultureIgnoreCase)
|
||||
|| p.Name.Equals("Lua For Barotrauma", StringComparison.InvariantCultureIgnoreCase));
|
||||
var luaCs = ContentPackageManager.AllPackages.FirstOrDefault(p => p.Name.Equals(PackageName, StringComparison.InvariantCultureIgnoreCase));
|
||||
if (luaCs is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"The 'LuaCsForBarotrauma' mod could not be found. Please subscribe to it and add it to the EnabledPackages List!",
|
||||
new NullReferenceException($"The 'LuaCsForBarotrauma' mod could not be found. Please subscribe to it and add it to the EnabledPackages List!"),
|
||||
DebugConsole.ThrowError($"The '{PackageName}' mod could not be found. Please subscribe to it and add it to the EnabledPackages List!",
|
||||
new NullReferenceException($"The '{PackageName}' mod could not be found. Please subscribe to it and add it to the EnabledPackages List!"),
|
||||
createMessageBox: true);
|
||||
return enabledRegular;
|
||||
}
|
||||
@@ -383,6 +410,11 @@ namespace Barotrauma
|
||||
EventService.PublishEvent<IEventServerConnected>(static p => p.OnServerConnected());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SERVER
|
||||
GameMain.Server.ServerSettings.LoadClientPermissions();
|
||||
#endif
|
||||
|
||||
CurrentRunState = RunState.Running;
|
||||
}
|
||||
|
||||
@@ -390,9 +422,6 @@ namespace Barotrauma
|
||||
void RunStateRunning_OnExit(State<RunState> currentState)
|
||||
{
|
||||
EventService.Call("stop");
|
||||
#if CLIENT
|
||||
_isCsEnabledForSession = false;
|
||||
#endif
|
||||
Logger.LogResults(PackageManagementService.StopRunningPackages());
|
||||
Logger.LogMessage("LuaCs running state exited");
|
||||
}
|
||||
|
||||
@@ -315,24 +315,16 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var resultDiagnostic in result.Diagnostics)
|
||||
{
|
||||
if (resultDiagnostic.Severity != DiagnosticSeverity.Error)
|
||||
if (resultDiagnostic.IsWarningAsError || resultDiagnostic.Severity == DiagnosticSeverity.Error)
|
||||
{
|
||||
continue;
|
||||
//sb.AppendLine($">>> {resultDiagnostic.GetMessage()} | Location: {resultDiagnostic.Location.SourceTree?.GetLineSpan(resultDiagnostic.Location.SourceSpan)} ");
|
||||
sb.AppendLine($"\n{resultDiagnostic}");
|
||||
}
|
||||
sb.AppendLine($">>> {resultDiagnostic.GetMessage()} | Location: {resultDiagnostic.Location.SourceTree?.GetLineSpan(resultDiagnostic.Location.SourceSpan)} ");
|
||||
}
|
||||
var res = new FluentResults.Result().WithError(
|
||||
new Error($"Package Error: {OwnerPackage.Name}: Compilation failed for assembly {assemblyName}! {sb.ToString()}\n")
|
||||
new Error($"Package Error: {OwnerPackage.Name}: Compilation failed for assembly {assemblyName}!\n {sb.ToString()}\n")
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.RootObject, syntaxTrees));
|
||||
var failuresDiag =
|
||||
result.Diagnostics.Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error);
|
||||
foreach (var diag in failuresDiag)
|
||||
{
|
||||
res = res.WithError(new Error(diag.GetMessage())
|
||||
.WithMetadata(MetadataType.ExceptionObject, this)
|
||||
.WithMetadata(MetadataType.ExceptionDetails, diag.Descriptor.Description));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ public sealed partial class ConfigService : IConfigService
|
||||
return;
|
||||
}
|
||||
|
||||
if (setting.TrySetValue(valueString))
|
||||
if (setting.TrySetSerializedValue(valueString))
|
||||
{
|
||||
_logger.LogMessage($"Set config {internalName} value to {valueString}", Color.Green);
|
||||
if (SaveConfigValue(setting) is { IsFailed: true } res)
|
||||
@@ -445,7 +445,7 @@ public sealed partial class ConfigService : IConfigService
|
||||
{
|
||||
if (_settingsInstances.TryGetValue((info.OwnerPackage, value.SettingName), out var instance))
|
||||
{
|
||||
instance.TrySetValue(value.Element);
|
||||
instance.TrySetSerializedValue(value.Element);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,7 +495,7 @@ public sealed partial class ConfigService : IConfigService
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
|
||||
return FluentResults.Result.OkIf(setting.TrySetValue(cfgValueElement), new Error($"Failed to set value for [{setting.OwnerPackage.Name}.{setting.InternalName}]"));
|
||||
return FluentResults.Result.OkIf(setting.TrySetSerializedValue(cfgValueElement), new Error($"Failed to set value for [{setting.OwnerPackage.Name}.{setting.InternalName}]"));
|
||||
}
|
||||
|
||||
public FluentResults.Result LoadSavedConfigsValues()
|
||||
@@ -544,7 +544,7 @@ public sealed partial class ConfigService : IConfigService
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!instance.TrySetValue(profileValue.Element))
|
||||
if (!instance.TrySetSerializedValue(profileValue.Element))
|
||||
{
|
||||
result.WithError(new Error($"{nameof(ApplyConfigProfile)}: Failed to set value for [{profileValue.SettingName}]."));
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@ public partial class EventService : IEventService
|
||||
public TypeStringKey(Type type)
|
||||
{
|
||||
Type = type ?? throw new ArgumentNullException(nameof(type));
|
||||
TypeName = type.Name;
|
||||
TypeName = type.Name.ToLowerInvariant();
|
||||
HashCode = TypeName.GetHashCode();
|
||||
}
|
||||
|
||||
public TypeStringKey(string typeName)
|
||||
{
|
||||
Type = null;
|
||||
TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName));
|
||||
TypeName = typeName?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(typeName));
|
||||
HashCode = TypeName.GetHashCode();
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public partial class EventService : IEventService
|
||||
private readonly AsyncReaderWriterLock _operationsLock = new();
|
||||
private readonly ConcurrentDictionary<TypeStringKey, ConcurrentDictionary<OneOf<IEvent, string>, IEvent>> _subscribers = new();
|
||||
private readonly ConcurrentDictionary<TypeStringKey, (TypeStringKey Event, Func<LuaCsFunc, IEvent> RunnerFactory)> _luaAliasEventFactory = new();
|
||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, LuaCsFunc>> _luaLegacyEventsSubscribers = new();
|
||||
private readonly ConcurrentDictionary<TypeStringKey, ConcurrentDictionary<TypeStringKey, LuaCsFunc>> _luaLegacyEventsSubscribers = new();
|
||||
private readonly ConcurrentDictionary<IEventService, IEventService> _subscribedEventDispatchers = new();
|
||||
|
||||
#region LifeCycle
|
||||
@@ -118,7 +118,7 @@ public partial class EventService : IEventService
|
||||
}
|
||||
else
|
||||
{
|
||||
var eventSubs = _luaLegacyEventsSubscribers.GetOrAdd(eventName, key => new ConcurrentDictionary<string, LuaCsFunc>());
|
||||
var eventSubs = _luaLegacyEventsSubscribers.GetOrAdd(eventName, key => new ConcurrentDictionary<TypeStringKey, LuaCsFunc>());
|
||||
eventSubs[identifier] = callback;
|
||||
}
|
||||
}
|
||||
@@ -200,6 +200,29 @@ public partial class EventService : IEventService
|
||||
}
|
||||
|
||||
public void Remove(string eventName, string identifier)
|
||||
{
|
||||
Guard.IsNotNullOrWhiteSpace(eventName, nameof(eventName));
|
||||
Guard.IsNotNullOrWhiteSpace(identifier, nameof(identifier));
|
||||
|
||||
using var lck = _operationsLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
IService.CheckDisposed(this);
|
||||
|
||||
if (_luaAliasEventFactory.TryGetValue(eventName, out var eventFunc))
|
||||
{
|
||||
if (_subscribers.TryGetValue(eventFunc.Event, out var eventSubs))
|
||||
{
|
||||
eventSubs.TryRemove(identifier, out _);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_luaLegacyEventsSubscribers.TryGetValue(eventName, out var eventSubs))
|
||||
{
|
||||
eventSubs.TryRemove(identifier, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Unsubscribe(string eventName, string identifier)
|
||||
{
|
||||
Guard.IsNotNullOrWhiteSpace(eventName, nameof(eventName));
|
||||
Guard.IsNotNullOrWhiteSpace(identifier, nameof(identifier));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Steam;
|
||||
using HarmonyLib;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -27,16 +28,12 @@ internal class HarmonyEventPatchesService : ISystem
|
||||
private static ILoggerService _loggerService;
|
||||
private readonly Harmony Harmony;
|
||||
|
||||
private static int debugConsoleCommandVanillaIndex;
|
||||
|
||||
public HarmonyEventPatchesService(IEventService eventService, ILoggerService loggerService)
|
||||
{
|
||||
_eventService = eventService;
|
||||
_loggerService = loggerService;
|
||||
Harmony = new Harmony("LuaCsForBarotrauma.Events");
|
||||
Patch();
|
||||
|
||||
debugConsoleCommandVanillaIndex = DebugConsole.Commands.Count;
|
||||
}
|
||||
|
||||
private void Patch()
|
||||
@@ -56,7 +53,7 @@ internal class HarmonyEventPatchesService : ISystem
|
||||
[HarmonyPatch(typeof(CoroutineManager), nameof(CoroutineManager.Update)), HarmonyPostfix]
|
||||
public static void CoroutineManager_Update_Post()
|
||||
{
|
||||
_eventService.PublishEvent<IEventUpdate>(x => x.OnUpdate(Timing.TotalTime));
|
||||
_eventService.PublishEvent<IEventUpdate>(x => x.OnUpdate(CoroutineManager.DeltaTime));
|
||||
_loggerService.ProcessLogs();
|
||||
}
|
||||
|
||||
@@ -95,6 +92,27 @@ internal class HarmonyEventPatchesService : ISystem
|
||||
_eventService.PublishEvent<IEventScreenSelected>(x => x.OnScreenSelected(__instance));
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
[HarmonyPatch(typeof(MainMenuScreen), "StartGame"), HarmonyPostfix]
|
||||
public static void MainMenuScreen_StartGame_Pre(Screen __instance)
|
||||
{
|
||||
LuaCsSetup.Instance.SetRunState(RunState.Running);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MainMenuScreen), "LoadGame"), HarmonyPostfix]
|
||||
public static void MainMenuScreen_LoadGame_Pre(Screen __instance)
|
||||
{
|
||||
LuaCsSetup.Instance.SetRunState(RunState.Running);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MutableWorkshopMenu), nameof(MutableWorkshopMenu.Apply)), HarmonyPostfix]
|
||||
public static void MutableWorkshopMenu_Apply_Post(Screen __instance)
|
||||
{
|
||||
LuaCsSetup.Instance.PromptCSharpMods(selection => { }, joiningServer: false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
[HarmonyPatch(typeof(ContentPackageManager.PackageSource), nameof(ContentPackageManager.PackageSource.Refresh)), HarmonyPostfix]
|
||||
public static void PackageSource_Refresh_Post()
|
||||
{
|
||||
@@ -122,11 +140,20 @@ internal class HarmonyEventPatchesService : ISystem
|
||||
|
||||
#if CLIENT
|
||||
[HarmonyPatch(typeof(GameClient), "ReadDataMessage"), HarmonyPrefix]
|
||||
public static void GameClient_ReadDataMessage_Pre(IReadMessage inc)
|
||||
public static bool GameClient_ReadDataMessage_Pre(IReadMessage inc)
|
||||
{
|
||||
int prevBitPosition = inc.BitPosition;
|
||||
ServerPacketHeader header = (ServerPacketHeader)inc.ReadByte();
|
||||
_eventService.PublishEvent<IEventServerRawNetMessageReceived>(x => x.OnReceivedServerNetMessage(inc, header));
|
||||
inc.BitPosition -= 8; // rewind so the game can read the message
|
||||
bool? skip = null;
|
||||
_eventService.PublishEvent<IEventServerRawNetMessageReceived>(x => skip = x.OnReceivedServerNetMessage(inc, header) ?? skip);
|
||||
|
||||
if (skip == true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inc.BitPosition = prevBitPosition; // rewind so the game can read the message
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(SubEditorScreen), nameof(SubEditorScreen.Select), new Type[] { }), HarmonyPostfix]
|
||||
@@ -146,7 +173,7 @@ internal class HarmonyEventPatchesService : ISystem
|
||||
{
|
||||
DebugConsole.Command c = DebugConsole.FindCommand(command.Value);
|
||||
|
||||
if (DebugConsole.Commands.IndexOf(c) >= debugConsoleCommandVanillaIndex)
|
||||
if (DebugConsole.Commands.IndexOf(c) >= LuaCsSetup.DebugConsoleCommandVanillaIndex)
|
||||
{
|
||||
__result = true;
|
||||
return false;
|
||||
@@ -158,11 +185,21 @@ internal class HarmonyEventPatchesService : ISystem
|
||||
|
||||
#elif SERVER
|
||||
[HarmonyPatch(typeof(GameServer), "ReadDataMessage"), HarmonyPrefix]
|
||||
public static void GameServer_ReadDataMessage_Pre(NetworkConnection sender, IReadMessage inc)
|
||||
public static bool GameServer_ReadDataMessage_Pre(NetworkConnection sender, IReadMessage inc)
|
||||
{
|
||||
int prevBitPosition = inc.BitPosition;
|
||||
ClientPacketHeader header = (ClientPacketHeader)inc.ReadByte();
|
||||
_eventService.PublishEvent<IEventClientRawNetMessageReceived>(x => x.OnReceivedClientNetMessage(inc, header, sender));
|
||||
inc.BitPosition -= 8; // rewind so the game can read the message
|
||||
|
||||
bool? skip = null;
|
||||
_eventService.PublishEvent<IEventClientRawNetMessageReceived>(x => skip = x.OnReceivedClientNetMessage(inc, header, sender) ?? skip);
|
||||
|
||||
if (skip == true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inc.BitPosition = prevBitPosition; // rewind so the game can read the message
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameServer), "OnInitializationComplete"), HarmonyPostfix]
|
||||
|
||||
@@ -12,8 +12,6 @@ namespace Barotrauma.LuaCs;
|
||||
|
||||
public partial class LoggerService : ILoggerService
|
||||
{
|
||||
public bool HideUserNames = true;
|
||||
|
||||
private List<ILoggerSubscriber> logSubscribers = [];
|
||||
private ConcurrentQueue<PendingLog> logQueue = [];
|
||||
|
||||
@@ -94,7 +92,7 @@ public partial class LoggerService : ILoggerService
|
||||
|
||||
public void Log(string message, Color? color = null, ServerLog.MessageType messageType = ServerLog.MessageType.ServerMessage)
|
||||
{
|
||||
if (HideUserNames && !Environment.UserName.IsNullOrEmpty())
|
||||
if (LuaCsSetup.Instance.HideUserNamesInLogs && !Environment.UserName.IsNullOrEmpty())
|
||||
{
|
||||
message = message.Replace(Environment.UserName, "USERNAME");
|
||||
}
|
||||
@@ -186,14 +184,13 @@ public partial class LoggerService : ILoggerService
|
||||
else
|
||||
{
|
||||
LogError($"FluentResults::IError: {error.Message}");
|
||||
}
|
||||
|
||||
if (error.Reasons != null)
|
||||
{
|
||||
foreach (var reason in error.Reasons)
|
||||
/*if (error.Reasons != null)
|
||||
{
|
||||
LogError($" - {reason.Message}");
|
||||
}
|
||||
foreach (var reason in error.Reasons)
|
||||
{
|
||||
LogError($" - {reason.Message}");
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ public sealed class LuaCsInfoProvider : ILuaCsInfoProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return ContentPackageManager.EnabledPackages.Regular.FirstOrDefault(cp => cp.NameMatches(LuaCsSetup.PackageId), null)
|
||||
?? ContentPackageManager.LocalPackages.FirstOrDefault(cp => cp.NameMatches(LuaCsSetup.PackageId))
|
||||
?? ContentPackageManager.WorkshopPackages.FirstOrDefault(cp => cp.NameMatches(LuaCsSetup.PackageId));
|
||||
return ContentPackageManager.EnabledPackages.Regular.FirstOrDefault(cp => cp.NameMatches(LuaCsSetup.PackageName), null)
|
||||
?? ContentPackageManager.LocalPackages.FirstOrDefault(cp => cp.NameMatches(LuaCsSetup.PackageName))
|
||||
?? ContentPackageManager.WorkshopPackages.FirstOrDefault(cp => cp.NameMatches(LuaCsSetup.PackageName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,19 @@
|
||||
#nullable enable
|
||||
|
||||
using Barotrauma.LuaCs;
|
||||
using Barotrauma.LuaCs.Compatibility;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
using Barotrauma.LuaCs.Events;
|
||||
using Barotrauma.Networking;
|
||||
using FluentResults;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.Toolkit.Diagnostics;
|
||||
using MonoMod.RuntimeDetour;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
using MoonSharp.Interpreter.Loaders;
|
||||
using RestSharp.Validation;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
@@ -285,6 +277,8 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService,
|
||||
_eventService.RegisterLuaEventAlias<IEventGiveCharacterJobItems>("character.giveJobItems", nameof(IEventGiveCharacterJobItems.OnGiveCharacterJobItems));
|
||||
_eventService.RegisterLuaEventAlias<IEventHumanCPRSuccess>("character.CPRSuccess", nameof(IEventHumanCPRSuccess.OnCharacterCPRSuccess));
|
||||
_eventService.RegisterLuaEventAlias<IEventHumanCPRFailed>("character.CPRFailed", nameof(IEventHumanCPRFailed.OnCharacterCPRFailed));
|
||||
_eventService.RegisterLuaEventAlias<IEventHumanCPRSuccess>("human.CPRSuccess", nameof(IEventHumanCPRSuccess.OnCharacterCPRSuccess));
|
||||
_eventService.RegisterLuaEventAlias<IEventHumanCPRFailed>("human.CPRFailed", nameof(IEventHumanCPRFailed.OnCharacterCPRFailed));
|
||||
_eventService.RegisterLuaEventAlias<IEventCharacterApplyDamage>("character.applyDamage", nameof(IEventCharacterApplyDamage.OnCharacterApplyDamage));
|
||||
_eventService.RegisterLuaEventAlias<IEventCharacterApplyAffliction>("character.applyAffliction", nameof(IEventCharacterApplyAffliction.OnCharacterApplyAffliction));
|
||||
|
||||
@@ -368,8 +362,10 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService,
|
||||
UserData.RegisterType(typeof(IUserDataDescriptor));
|
||||
UserData.RegisterType(typeof(INetworkingService));
|
||||
UserData.RegisterType(typeof(ILuaConfigService));
|
||||
UserData.RegisterType(typeof(ILoggerService));
|
||||
|
||||
//UserData.RegisterType(typeof(ISettingBase));
|
||||
UserData.RegisterType(typeof(ISettingBase));
|
||||
UserData.RegisterType(typeof(IDataInfo));
|
||||
|
||||
Type[] settingBaseTypes = [
|
||||
typeof(ISettingBase<bool>),
|
||||
@@ -451,6 +447,7 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService,
|
||||
_script.Globals["Networking"] = _networkingService;
|
||||
_script.Globals["trygetpackage"] = (string name, out ContentPackage package) =>
|
||||
_packageManagementService.Value.TryGetLoadedPackageByName(name, out package);
|
||||
_script.Globals["Logger"] = _loggerService;
|
||||
//_script.Globals["Steam"] = Steam;
|
||||
|
||||
if (enableSandbox)
|
||||
@@ -517,6 +514,61 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService,
|
||||
Table package = (Table)_script.Globals["package"];
|
||||
package.Set("path", DynValue.FromObject(_script, packages));
|
||||
|
||||
#if CLIENT
|
||||
if (GameMain.NetworkMember is { IsClient: true })
|
||||
{
|
||||
var startMessage = _networkingService.Start("_luastart");
|
||||
|
||||
var packagesToReport = ContentPackageManager.EnabledPackages.All
|
||||
.Where(p => _packageManagementService.Value.PackageContainsAnyRunnableResource(p))
|
||||
.Where(p => !p.NameMatches(LuaCsSetup.PackageName))
|
||||
.ToList();
|
||||
|
||||
startMessage.WriteUInt16((UInt16)packagesToReport.Count());
|
||||
|
||||
foreach (var enabledPackage in packagesToReport)
|
||||
{
|
||||
var id = enabledPackage.UgcId;
|
||||
string hash = enabledPackage.Hash.StringRepresentation ?? "";
|
||||
|
||||
startMessage.WriteString(enabledPackage.Name);
|
||||
startMessage.WriteString(enabledPackage.ModVersion);
|
||||
if (id.TryUnwrap(out ContentPackageId? packageId) && packageId is SteamWorkshopId steamId)
|
||||
{
|
||||
startMessage.WriteUInt64(steamId.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
startMessage.WriteUInt64(0);
|
||||
}
|
||||
startMessage.WriteString(hash);
|
||||
}
|
||||
|
||||
_networkingService.Send(startMessage);
|
||||
}
|
||||
#elif SERVER
|
||||
_networkingService.Receive("_luastart", (message, client) =>
|
||||
{
|
||||
var num = message.ReadUInt16();
|
||||
List<Table> packages = new List<Table>();
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
Table table = new Table(_script);
|
||||
|
||||
table.Set("Name", DynValue.NewString(message.ReadString()));
|
||||
table.Set("Version", DynValue.NewString(message.ReadString()));
|
||||
table.Set("Id", DynValue.NewString(message.ReadUInt64().ToString()));
|
||||
table.Set("Hash", DynValue.NewString(message.ReadString()));
|
||||
|
||||
packages.Add(table);
|
||||
}
|
||||
|
||||
_eventService.Call("client.packages", client, packages);
|
||||
});
|
||||
#endif
|
||||
|
||||
|
||||
foreach (ILuaScriptResourceInfo resource in executionOrder.Where(l => l.IsAutorun))
|
||||
{
|
||||
foreach (ContentPath filePath in resource.FilePaths)
|
||||
|
||||
@@ -43,11 +43,32 @@ internal class MainMenuPatch : ISystem, IEventScreenSelected
|
||||
{
|
||||
if (mainMenuUIAdded) { return; }
|
||||
|
||||
new GUITextBlock(new RectTransform(new Point(300, 30), screen.Frame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(10, 10) }, $"Using LuaCsForBarotrauma revision {AssemblyInfo.GitRevision}", Color.Red)
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Point(300, 30), screen.Frame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(10, 10) }, "", Color.Red)
|
||||
{
|
||||
IgnoreLayoutGroups = false
|
||||
};
|
||||
|
||||
textBlock.OnAddedToGUIUpdateList = (GUIComponent component) =>
|
||||
{
|
||||
string mode = LuaCsSetup.Instance.CsRunPolicyValue;
|
||||
|
||||
if (mode is "Prompt")
|
||||
{
|
||||
string sessionState = LuaCsSetup.Instance.IsCsEnabledForSession ? "yes" : "no";
|
||||
mode = $"enabled (prompt mode, allowed for this session: {sessionState})";
|
||||
}
|
||||
else if (mode is "Enabled")
|
||||
{
|
||||
mode = "always enabled";
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = "disabled";
|
||||
}
|
||||
|
||||
textBlock.Text = $"LuaCsForBarotrauma active (revision {AssemblyInfo.GitRevision}), C# is currently {mode}\nNew settings available in the game settings menu.";
|
||||
};
|
||||
|
||||
mainMenuUIAdded = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.LuaCs.Data;
|
||||
@@ -60,8 +61,9 @@ public sealed partial class ModConfigFileParserService :
|
||||
if (CheckThrowNullRefs(src, "Assembly") is { IsFailed: true } fail)
|
||||
return fail;
|
||||
|
||||
var isScript = src.Element.GetAttributeBool("IsScript", false);
|
||||
var runtimeEnv = GetRuntimeEnvironment(src.Element);
|
||||
var fileResults = await UnsafeGetCheckedFiles(src.Element, src.Owner, ".dll");
|
||||
var fileResults = await UnsafeGetCheckedFiles(src.Element, src.Owner, isScript ? ".cs" : ".dll");
|
||||
|
||||
if (fileResults.IsFailed)
|
||||
return FluentResults.Result.Fail(fileResults.Errors);
|
||||
@@ -78,11 +80,31 @@ public sealed partial class ModConfigFileParserService :
|
||||
RequiredPackages = src.Required,
|
||||
IncompatiblePackages = src.Incompatible,
|
||||
// Type Specific
|
||||
FriendlyName = src.Element.GetAttributeString("FriendlyName", string.Empty),
|
||||
IsScript = src.Element.GetAttributeBool("IsScript", false),
|
||||
FriendlyName = src.Element.GetAttributeString("FriendlyName", GetFallbackCompliantAssemblyName(src.Owner)),
|
||||
IsScript = isScript,
|
||||
UseInternalAccessName = src.Element.GetAttributeBool("UseInternalAccessName", false),
|
||||
IsReferenceModeOnly = src.Element.GetAttributeBool("IsReferenceModeOnly", false)
|
||||
};
|
||||
|
||||
|
||||
// helper methods
|
||||
string GetFallbackCompliantAssemblyName(ContentPackage package)
|
||||
{
|
||||
if (package.Name.IsNullOrWhiteSpace())
|
||||
{
|
||||
return "FallbackAssemblyName";
|
||||
}
|
||||
|
||||
// replace non az chars with '_'
|
||||
var sanitizedPackageName = Regex.Replace(package.Name, @"[^a-zA-Z0-9_]", "_");
|
||||
if (char.IsDigit(sanitizedPackageName[0]))
|
||||
{
|
||||
sanitizedPackageName = "ASM" + sanitizedPackageName;
|
||||
}
|
||||
|
||||
// replace consecutive '_'
|
||||
return Regex.Replace(sanitizedPackageName, @"[_.]{2,}", "_");
|
||||
}
|
||||
}
|
||||
|
||||
async Task<ImmutableArray<Result<IAssemblyResourceInfo>>> IParserServiceAsync<ResourceParserInfo, IAssemblyResourceInfo>.TryParseResourcesAsync(IEnumerable<ResourceParserInfo> sources)
|
||||
@@ -152,7 +174,8 @@ public sealed partial class ModConfigFileParserService :
|
||||
RequiredPackages = src.Required,
|
||||
IncompatiblePackages = src.Incompatible,
|
||||
// Type Specific
|
||||
IsAutorun = src.Element.GetAttributeBool("IsAutorun", false)
|
||||
IsAutorun = src.Element.GetAttributeBool("IsAutorun", false),
|
||||
RunUnrestricted = src.Element.GetAttributeBool("RunUnrestricted", false)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -184,7 +207,7 @@ public sealed partial class ModConfigFileParserService :
|
||||
|
||||
var res = new FluentResults.Result<ImmutableArray<ContentPath>>();
|
||||
|
||||
if (!filePath.IsNullOrWhiteSpace())
|
||||
if ((!filePath?.Value.IsNullOrWhiteSpace()) ?? false)
|
||||
{
|
||||
if (_storageService.FileExists(filePath.FullPath) is { IsSuccess: true, Value: true })
|
||||
{
|
||||
@@ -203,11 +226,12 @@ public sealed partial class ModConfigFileParserService :
|
||||
}
|
||||
}
|
||||
|
||||
if (!folderPath.IsNullOrWhiteSpace())
|
||||
if ((!folderPath?.Value.IsNullOrWhiteSpace()) ?? false)
|
||||
{
|
||||
if (_storageService.DirectoryExists(folderPath.FullPath) is { IsSuccess: true, Value: true })
|
||||
{
|
||||
var files = _storageService.FindFilesInPackage(srcOwner, folderPath.Value, fileExtension, true);
|
||||
var searchLocation = System.IO.Path.GetRelativePath(srcOwner.Dir, folderPath.Value);
|
||||
var files = _storageService.FindFilesInPackage(srcOwner, searchLocation, "*"+fileExtension, true);
|
||||
if (files.IsFailed)
|
||||
{
|
||||
res.WithError($"{srcOwner.Name}: Failed to load files from {folderPath}!");
|
||||
|
||||
@@ -335,10 +335,10 @@ public sealed class ModConfigService : IModConfigService
|
||||
.Select(fp => ContentPath.FromRaw(srcPackage,
|
||||
$"%ModDir%/{Path.GetRelativePath(srcPackage.Dir, fp)}".CleanUpPathCrossPlatform()))
|
||||
.Concat(sharedFiles).ToImmutableArray(),
|
||||
FriendlyName = IAssemblyLoaderService.InternalsAwareAssemblyName,
|
||||
FriendlyName = IAssemblyLoaderService.InternalsAwareAssemblyName, // give the best chance of success (InternalsAware + Publicizer)
|
||||
IncompatiblePackages = ImmutableArray<Identifier>.Empty,
|
||||
RequiredPackages = ImmutableArray<Identifier>.Empty,
|
||||
UseInternalAccessName = true,
|
||||
UseInternalAccessName = false, //compile as public and then fallback to internals
|
||||
IsScript = true,
|
||||
IsReferenceModeOnly = false
|
||||
});
|
||||
@@ -357,7 +357,7 @@ public sealed class ModConfigService : IModConfigService
|
||||
FriendlyName = IAssemblyLoaderService.InternalsAwareAssemblyName,
|
||||
IncompatiblePackages = ImmutableArray<Identifier>.Empty,
|
||||
RequiredPackages = ImmutableArray<Identifier>.Empty,
|
||||
UseInternalAccessName = true,
|
||||
UseInternalAccessName = false,
|
||||
IsScript = true,
|
||||
IsReferenceModeOnly = false
|
||||
});
|
||||
@@ -405,6 +405,7 @@ public sealed class ModConfigService : IModConfigService
|
||||
IncompatiblePackages = ImmutableArray<Identifier>.Empty,
|
||||
RequiredPackages = ImmutableArray<Identifier>.Empty,
|
||||
IsAutorun = true,
|
||||
RunUnrestricted = false
|
||||
});
|
||||
|
||||
builder.Add(new LuaScriptsResourceInfo()
|
||||
@@ -418,6 +419,7 @@ public sealed class ModConfigService : IModConfigService
|
||||
IncompatiblePackages = ImmutableArray<Identifier>.Empty,
|
||||
RequiredPackages = ImmutableArray<Identifier>.Empty,
|
||||
IsAutorun = false,
|
||||
RunUnrestricted = false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -345,6 +345,8 @@ public sealed class PackageManagementService : IPackageManagementService
|
||||
|
||||
//lua scripts
|
||||
var luaScripts = SelectCompatible(loadingOrderedPackages
|
||||
.Where(pkg => executeCsAssemblies
|
||||
|| !pkg.Value.LuaScripts.Any(scr => scr.RunUnrestricted))
|
||||
.SelectMany(pkg => pkg.Value.LuaScripts)
|
||||
.ToImmutableArray(), toLoadPackagesIndents, loadOrderByPackage);
|
||||
|
||||
@@ -357,11 +359,7 @@ public sealed class PackageManagementService : IPackageManagementService
|
||||
{
|
||||
_runningPackages[package.Key] = package.Value;
|
||||
}
|
||||
|
||||
if (result.IsFailed)
|
||||
{
|
||||
_logger.LogResults(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -517,14 +515,44 @@ public sealed class PackageManagementService : IPackageManagementService
|
||||
return !_runningPackages.IsEmpty;
|
||||
}
|
||||
|
||||
public ImmutableArray<ContentPackage> GetLoadedAssemblyPackages()
|
||||
public ImmutableArray<ContentPackage> GetLoadedUnrestrictedPackages()
|
||||
{
|
||||
using var lck = _operationsLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
IService.CheckDisposed(this);
|
||||
if (_loadedPackages.IsEmpty)
|
||||
return ImmutableArray<ContentPackage>.Empty;
|
||||
return [.._loadedPackages.Values
|
||||
.Where(cfg => !cfg.Assemblies.IsDefaultOrEmpty)
|
||||
.Where(cfg => !cfg.Assemblies.IsDefaultOrEmpty || cfg.LuaScripts.Any(scr => scr.RunUnrestricted))
|
||||
.Select(cfg => cfg.Package)];
|
||||
}
|
||||
|
||||
public bool PackageContainsAnyRunnableResource(ContentPackage package)
|
||||
{
|
||||
using var lck = _operationsLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
IService.CheckDisposed(this);
|
||||
|
||||
var result = GetModConfigForPackage(package);
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
return result.Value.Assemblies.Any() || result.Value.LuaScripts.Any();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Result<IModConfigInfo> GetModConfigForPackage(ContentPackage package)
|
||||
{
|
||||
using var lck = _operationsLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
IService.CheckDisposed(this);
|
||||
|
||||
if (!_loadedPackages.TryGetValue(package, out var modConfig))
|
||||
{
|
||||
return FluentResults.Result.Fail($"Failed to find mod config for package {package.Name}");
|
||||
}
|
||||
|
||||
return new FluentResults.Result<IModConfigInfo>().WithValue(modConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,9 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
ScriptParseOptions);
|
||||
|
||||
private ImmutableArray<MetadataReference> _baseMetadataReferences = ImmutableArray<MetadataReference>.Empty;
|
||||
private ImmutableArray<MetadataReference> _baseMetadataReferencesNonPublicized = ImmutableArray<MetadataReference>.Empty;
|
||||
|
||||
|
||||
private IEnumerable<MetadataReference> BaseMetadataReferences
|
||||
{
|
||||
get
|
||||
@@ -110,6 +113,25 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<MetadataReference> BaseMetadataReferencesWithBarotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_baseMetadataReferencesNonPublicized.IsDefaultOrEmpty)
|
||||
{
|
||||
_baseMetadataReferencesNonPublicized = Basic.Reference.Assemblies.Net80.References.All
|
||||
.Union(AssemblyLoadContext.Default.Assemblies
|
||||
.Where(ass => !ass.IsDynamic)
|
||||
.Where(ass => !ass.Location.IsNullOrWhiteSpace())
|
||||
.Select(MetadataReference (ass) => MetadataReference.CreateFromFile(ass.Location)))
|
||||
.Where(ar => ar is not null)
|
||||
.ToImmutableArray();
|
||||
}
|
||||
|
||||
return _baseMetadataReferencesNonPublicized;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Disposal
|
||||
@@ -129,6 +151,7 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
_logger = null;
|
||||
_configService = null;
|
||||
_luaScriptManagementService = null;
|
||||
_luaCsInfoProvider = null;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
@@ -199,6 +222,7 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
private IEventService _pluginEventService;
|
||||
private Lazy<ILuaPatcher> _pluginLuaPatcherService;
|
||||
private Func<IConsoleCommandsService> _consoleCommandServiceFactory;
|
||||
private ILuaCsInfoProvider _luaCsInfoProvider;
|
||||
private readonly ConcurrentDictionary<ContentPackage, IAssemblyLoaderService> _assemblyLoaders = new();
|
||||
private readonly ConcurrentDictionary<Type, ContentPackage> _pluginPackageLookup = new();
|
||||
private readonly ConcurrentDictionary<ContentPackage, ImmutableArray<IAssemblyPlugin>> _pluginInstances = new();
|
||||
@@ -215,7 +239,8 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
Lazy<ILuaScriptManagementService> luaScriptManagementService,
|
||||
Lazy<IConfigService> configService,
|
||||
Lazy<ILuaPatcher> pluginLuaPatcherService,
|
||||
Func<IConsoleCommandsService> consoleCommandServiceFactory)
|
||||
Func<IConsoleCommandsService> consoleCommandServiceFactory,
|
||||
ILuaCsInfoProvider luaCsInfoProvider)
|
||||
{
|
||||
_assemblyLoaderFactory = assemblyLoaderFactory;
|
||||
_storageService = storageService;
|
||||
@@ -225,6 +250,7 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
_configService = configService;
|
||||
_pluginLuaPatcherService = pluginLuaPatcherService;
|
||||
_consoleCommandServiceFactory = consoleCommandServiceFactory;
|
||||
_luaCsInfoProvider = luaCsInfoProvider;
|
||||
}
|
||||
|
||||
private ServiceContainer CreatePluginServiceContainer()
|
||||
@@ -485,6 +511,12 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
}
|
||||
using var lck = _operationsLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
IService.CheckDisposed(this);
|
||||
|
||||
_storageService.UseCaching = _luaCsInfoProvider.UseCaching;
|
||||
if (!_luaCsInfoProvider.UseCaching)
|
||||
{
|
||||
_storageService.PurgeCache();
|
||||
}
|
||||
|
||||
var orderedContentPacks = resources.GroupBy(res => res.OwnerPackage)
|
||||
.OrderBy(res => resources.FindIndex(r2 => r2.OwnerPackage == res.Key))
|
||||
@@ -554,7 +586,8 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
return;
|
||||
}
|
||||
|
||||
var metadataReferences = GetMetadataReferences();
|
||||
var metadataReferences = GetMetadataReferences(false).ToImmutableArray();
|
||||
var metadataReferencesNonPublicized = GetMetadataReferences(true).ToImmutableArray();
|
||||
|
||||
var assemblyLoader = _assemblyLoaders.GetOrAdd(contentPackRes.Key, (cp) => _assemblyLoaderFactory.CreateInstance(
|
||||
new IAssemblyLoaderService.LoaderInitData(
|
||||
@@ -578,7 +611,10 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
|
||||
foreach (var resourceInfo in scripts)
|
||||
{
|
||||
if (!hasInternalsAwareBeenAdded && resourceInfo.UseInternalAccessName)
|
||||
// this should be the same for the entire collection of src files so we just grab it from the collection
|
||||
compileWithInternalName = resourceInfo.UseInternalAccessName;
|
||||
|
||||
if (!hasInternalsAwareBeenAdded)
|
||||
{
|
||||
hasInternalsAwareBeenAdded = true;
|
||||
syntaxTreesBuilder.Add(BaseAssemblyImports);
|
||||
@@ -597,9 +633,6 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
_logger.LogResults(loadRes.ToResult());
|
||||
continue;
|
||||
}
|
||||
|
||||
// this should be the same for the entire collection of src files so we just grab it from the collection
|
||||
compileWithInternalName = resourceInfo.UseInternalAccessName;
|
||||
|
||||
CancellationToken token = CancellationToken.None;
|
||||
|
||||
@@ -622,14 +655,35 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
}
|
||||
|
||||
_logger.LogMessage($"Compiling assembly for {scripts.Key}, in ContentPackage {contentPackRes.Key.Name}");
|
||||
|
||||
result.WithReasons(assemblyLoader.CompileScriptAssembly(
|
||||
|
||||
var res = assemblyLoader.CompileScriptAssembly(
|
||||
assemblyName: scripts.Key,
|
||||
compileWithInternalAccess: compileWithInternalName,
|
||||
syntaxTrees: syntaxTreesBuilder.ToImmutable(),
|
||||
metadataReferences: metadataReferences.ToImmutableArray(),
|
||||
compilationOptions: CompilationOptions)
|
||||
.Reasons);
|
||||
metadataReferences: compileWithInternalName ? metadataReferencesNonPublicized : metadataReferences,
|
||||
compilationOptions: CompilationOptions);
|
||||
|
||||
// try with internal access instead for legacy mods
|
||||
if (!compileWithInternalName && res.IsFailed)
|
||||
{
|
||||
_logger.LogMessage($"Attempted compilation of {scripts.Key} for package {contentPackRes.Key.Name}. Trying fallback method.");
|
||||
var res2 = assemblyLoader.CompileScriptAssembly(
|
||||
assemblyName: scripts.Key,
|
||||
compileWithInternalAccess: true,
|
||||
syntaxTrees: syntaxTreesBuilder.ToImmutable(),
|
||||
metadataReferences: metadataReferencesNonPublicized,
|
||||
compilationOptions: CompilationOptions);
|
||||
|
||||
// overwrite result with good compilation
|
||||
if (res2.IsSuccess)
|
||||
{
|
||||
var reasonsStr = res.Reasons.Aggregate("", (accum, reason) => accum + "\n" + reason.Message);
|
||||
_logger.LogWarning($"Attempted compilation of {scripts.Key} for package {contentPackRes.Key.Name} succeeded. Original errors were: \n {reasonsStr}");
|
||||
res = res2;
|
||||
}
|
||||
}
|
||||
|
||||
result.WithReasons(res.Reasons);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,14 +698,28 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
return res;
|
||||
}
|
||||
|
||||
IEnumerable<MetadataReference> GetMetadataReferences()
|
||||
IEnumerable<MetadataReference> GetMetadataReferences(bool useNonPublicizedAssemblies)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<MetadataReference>();
|
||||
builder.AddRange(BaseMetadataReferences);
|
||||
foreach (var loaderService in _assemblyLoaders)
|
||||
if (useNonPublicizedAssemblies)
|
||||
{
|
||||
builder.AddRange(loaderService.Value.AssemblyReferences.Where(ar => ar is not null));
|
||||
builder.AddRange(BaseMetadataReferencesWithBarotrauma);
|
||||
foreach (var loaderService in _assemblyLoaders
|
||||
.Where(asl => !asl.Key.Name.Equals("LuaCsForBarotrauma", StringComparison.InvariantCultureIgnoreCase))
|
||||
.ToImmutableArray())
|
||||
{
|
||||
builder.AddRange(loaderService.Value.AssemblyReferences.Where(ar => ar is not null));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AddRange(BaseMetadataReferences);
|
||||
foreach (var loaderService in _assemblyLoaders)
|
||||
{
|
||||
builder.AddRange(loaderService.Value.AssemblyReferences.Where(ar => ar is not null));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
}
|
||||
@@ -768,6 +836,7 @@ public class PluginManagementService : IAssemblyManagementService
|
||||
}
|
||||
|
||||
_assemblyLoaders.Clear();
|
||||
_storageService.PurgeCache();
|
||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true);
|
||||
|
||||
#if DEBUG
|
||||
|
||||
@@ -34,4 +34,26 @@ public interface ILoggerService : IReusableService
|
||||
void LogDebugError(string message);
|
||||
|
||||
#endregion
|
||||
|
||||
#region LegacyCompat_LuaCsLogger
|
||||
|
||||
public void HandleException(Exception ex, LuaCsMessageOrigin origin)
|
||||
{
|
||||
HandleException(ex, origin.ToString());
|
||||
}
|
||||
|
||||
public void LogError(string message, LuaCsMessageOrigin origin)
|
||||
{
|
||||
LogError(message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum LuaCsMessageOrigin
|
||||
{
|
||||
LuaCs,
|
||||
Unknown,
|
||||
LuaMod,
|
||||
CSharpMod,
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ public interface IPackageManagementService : IReusableService
|
||||
public FluentResults.Result UnloadPackages(ImmutableArray<ContentPackage> packages);
|
||||
public FluentResults.Result UnloadAllPackages();
|
||||
public ImmutableArray<ContentPackage> GetAllLoadedPackages();
|
||||
public ImmutableArray<ContentPackage> GetLoadedAssemblyPackages();
|
||||
public ImmutableArray<ContentPackage> GetLoadedUnrestrictedPackages();
|
||||
public bool IsPackageRunning(ContentPackage package);
|
||||
public bool IsAnyPackageLoaded();
|
||||
public bool IsAnyPackageRunning();
|
||||
public bool PackageContainsAnyRunnableResource(ContentPackage package);
|
||||
public Result<IModConfigInfo> GetModConfigForPackage(ContentPackage package);
|
||||
}
|
||||
|
||||
@@ -154,6 +154,13 @@ public class DefaultLuaRegistrar : IDefaultLuaRegistrar
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.ReferenceFace");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.Collision");
|
||||
|
||||
_userDataService.RegisterType("Voronoi2.DoubleVector2");
|
||||
_userDataService.RegisterType("Voronoi2.Site");
|
||||
_userDataService.RegisterType("Voronoi2.Edge");
|
||||
_userDataService.RegisterType("Voronoi2.Halfedge");
|
||||
_userDataService.RegisterType("Voronoi2.VoronoiCell");
|
||||
_userDataService.RegisterType("Voronoi2.GraphEdge");
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.PrefabCollection`1");
|
||||
_userDataService.RegisterType("Barotrauma.PrefabSelector`1");
|
||||
_userDataService.RegisterType("Barotrauma.Pair`2");
|
||||
|
||||
@@ -19,7 +19,7 @@ public interface ILuaSafeEventService : ILuaService, ILuaCsHook
|
||||
/// </summary>
|
||||
/// <param name="eventName"></param>
|
||||
/// <param name="identifier"></param>
|
||||
void Remove(string eventName, string identifier);
|
||||
void Unsubscribe(string eventName, string identifier);
|
||||
/// <summary>
|
||||
/// Send an event to all subscribers to an interface.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,16 +2,10 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MoonSharp.Interpreter;
|
||||
using Barotrauma.LuaCs;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public enum LuaCsMessageOrigin
|
||||
{
|
||||
LuaCs,
|
||||
Unknown,
|
||||
LuaMod,
|
||||
CSharpMod,
|
||||
}
|
||||
|
||||
public partial class LuaCsLogger
|
||||
{
|
||||
|
||||
@@ -445,11 +445,33 @@ namespace Barotrauma.LuaCs
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void AddCommand(string name, LuaCsAction onExecute, LuaCsFunc getValidArgs = null, bool isCheat = false)
|
||||
{
|
||||
_consoleCommands.RegisterCommand(name, "",
|
||||
(string[] args) =>
|
||||
{
|
||||
onExecute(new object[] { args });
|
||||
},
|
||||
() =>
|
||||
{
|
||||
if (getValidArgs == null) { return null; }
|
||||
var validArgs = getValidArgs();
|
||||
if (validArgs is DynValue luaValue)
|
||||
{
|
||||
return luaValue.ToObject<string[][]>();
|
||||
}
|
||||
return (string[][])validArgs;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public bool IsDisposed => throw new NotImplementedException();
|
||||
|
||||
public void AssignOnExecute(string names, LuaCsAction onExecute) =>
|
||||
_consoleCommands.AssignOnExecute(names, args => onExecute(args));
|
||||
public void AssignOnExecute(string names, object onExecute) => DebugConsole.AssignOnExecute(names, (string[] args) =>
|
||||
{
|
||||
LuaCsSetup.Instance.LuaScriptManagementService.CallFunctionSafe(onExecute, new object[] { args });
|
||||
});
|
||||
|
||||
public void SaveGame(string path)
|
||||
{
|
||||
|
||||
@@ -816,7 +816,7 @@ namespace Barotrauma
|
||||
|
||||
public static float GetDistanceFactor(PhysicsBody triggererBody, PhysicsBody triggerBody, float colliderRadius)
|
||||
{
|
||||
return 1.0f - ConvertUnits.ToDisplayUnits(Vector2.Distance(triggererBody.SimPosition, triggerBody.SimPosition)) / colliderRadius;
|
||||
return 1.0f - ConvertUnits.ToDisplayUnits(Vector2.Distance(triggererBody.SimPosition, triggerBody.SimPosition) - triggererBody.GetMaxExtent() / 2) / colliderRadius;
|
||||
}
|
||||
|
||||
public Vector2 GetWaterFlowVelocity(Vector2 viewPosition)
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
v1.12.7.0
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
- Reduced how much the new weak points in the reworked subs push bots around to make them more capable of fixing broken weak points.
|
||||
- Fixed selecting any item that forces the character to some pose (chairs, periscopes) getting logged in the server console.
|
||||
- Mac only: added a button for settings mic permissions to the audio settings. It seems that on Mac, the game updates may cause the OS to revoke the permissions.
|
||||
- Fixed some of the Workshop tags you can choose in-game not working on Steam's side.
|
||||
|
||||
Modding:
|
||||
- Fixed contained items being misaligned on attachable items (e.g. in mods that make diving suit cabinets attachable).
|
||||
- Fixed monsters spawned by an event inside an outpost being unable to attack any items inside that outpost. To our knowledge, didn't affect vanilla events, but caused issues in certain mods.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
v1.12.6.2
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -439,7 +452,6 @@ v1.8.8.1
|
||||
|
||||
Modding:
|
||||
- Fixed transferring afflictions to a newly spawned character using status effects causing a crash if the original character had already been removed. Didn't affect any vanilla content.
|
||||
>>>>>>> master
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
v1.8.7.0
|
||||
|
||||
Reference in New Issue
Block a user