Fix LuaCs initializing too late for singleplayer campaigns and rework the C# prompt to only show when enabling mods/joining server

This commit is contained in:
Evil Factory
2026-04-10 21:40:32 -03:00
parent ccd0b9f7cc
commit df8bfb1807
5 changed files with 103 additions and 114 deletions
@@ -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.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Barotrauma.CharacterEditor; using static System.Collections.Specialized.BitVector32;
using Barotrauma.LuaCs;
using Barotrauma.LuaCs.Data;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
// ReSharper disable ObjectCreationAsStatement // ReSharper disable ObjectCreationAsStatement
namespace Barotrauma namespace Barotrauma
{ {
partial class LuaCsSetup partial class LuaCsSetup
{ {
private bool _isClientPromptActive; public void PromptCSharpMods(Action<bool> onSelection, bool joiningServer)
private bool _isCsEnabledForSession = false;
public void CheckRunConditionalHostingCsEnabled(Action onReadyToRun)
{ {
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() ImmutableArray<ContentPackage> contentPackages = PackageManagementService.GetLoadedAssemblyPackages()
.Where(p => p.Name != PackageName) .Where(p => p.Name != PackageName)
.ToImmutableArray(); .ToImmutableArray();
return (contentPackages.IsEmpty, contentPackages); if (_csRunPolicy?.Value is "Enabled")
}
partial void CheckReadyToRun(Action onReadyToRun)
{
var res = ReadyToRunNoPrompt();
if (res.ShouldRun)
{ {
onReadyToRun?.Invoke(); IsCsEnabledForSession = true;
onSelection(true);
return; return;
} }
else if (_csRunPolicy?.Value is "Disabled")
if (GameMain.Client?.ClientPeer is P2POwnerPeer)
{ {
SetCsPolicyAndContinue(true); IsCsEnabledForSession = false;
onSelection(false);
return; return;
} }
DisplayCsModsPromptClient(res.PromptPackages, (selectedYes) => if (contentPackages.None())
{ {
SetCsPolicyAndContinue(selectedYes); onSelection(true);
return; 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( GUIMessageBox messageBox = new GUIMessageBox(
TextManager.Get("warning"), TextManager.Get("warning"),
@@ -126,22 +67,39 @@ namespace Barotrauma
foreach (ContentPackage package in contentPackages) foreach (ContentPackage package in contentPackages)
{ {
GUIFrame packageFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), packageListBox.Content.RectTransform), style: "ListBoxElement"); 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. 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 Lua mods.";
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0f), msgBoxLayout.RectTransform), bodyText, wrap: true)
{ {
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); 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 }, TextBlock = { AutoScaleHorizontal = true },
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
_isClientPromptActive = false; IsCsEnabledForSession = true;
onSelection(true); onSelection(true);
messageBox.Close(); messageBox.Close();
return true; return true;
@@ -152,7 +110,7 @@ namespace Barotrauma
{ {
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
_isClientPromptActive = false; IsCsEnabledForSession = false;
onSelection(false); onSelection(false);
messageBox.Close(); messageBox.Close();
return true; return true;
@@ -201,10 +159,18 @@ namespace Barotrauma
case SpriteEditorScreen: case SpriteEditorScreen:
case SubEditorScreen: case SubEditorScreen:
case TestScreen: // notes: TestScreen is a Linux edge case editor screen and is deprecated. case TestScreen: // notes: TestScreen is a Linux edge case editor screen and is deprecated.
CheckReadyToRun(() =>
if (screen is NetLobbyScreen)
{
PromptCSharpMods(selection =>
{
SetRunState(RunState.Running);
}, joiningServer: true);
}
else
{ {
SetRunState(RunState.Running); SetRunState(RunState.Running);
}); }
break; break;
default: default:
Logger.LogError( Logger.LogError(
@@ -1015,25 +1015,20 @@ namespace Barotrauma
private void TryStartServer() 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") }); CoroutineManager.StopCoroutines(waitCoroutine);
var waitCoroutine = CoroutineManager.StartCoroutine(WaitForSubmarineHashCalculations(waitBox), "WaitForSubmarineHashCalculations"); return true;
waitBox.Buttons[0].OnClicked += (btn, userdata) => };
{ }
CoroutineManager.StopCoroutines(waitCoroutine); else
return true; {
}; StartServer();
} }
else
{
StartServer();
}
});
} }
private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox) private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
@@ -10,6 +10,6 @@
</Values> </Values>
</Setting> </Setting>
<Setting Name="UseCaching" Type="bool" Value="true" AllowChangesWhileExecuting="false"/> <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"/>
</Settings> </Settings>
</Configuration> </Configuration>
@@ -85,20 +85,31 @@ namespace Barotrauma
public LuaGame Game => _game ??= _servicesProvider.GetService<LuaGame>(); public LuaGame Game => _game ??= _servicesProvider.GetService<LuaGame>();
public Script Lua => LuaScriptManagementService.InternalScript; public Script Lua => LuaScriptManagementService.InternalScript;
private ISettingBase<bool> _isCsEnabledForSession;
public bool IsCsEnabledForSession
{
get => _isCsEnabledForSession?.Value ?? false;
internal set
{
_isCsEnabledForSession?.TrySetValue(value);
ConfigService.SaveConfigValue(_isCsEnabledForSession);
}
}
/// <summary> /// <summary>
/// Whether C# plugin code is enabled. /// Whether C# plugin code is enabled.
/// </summary> /// </summary>
public bool IsCsEnabled public bool IsCsEnabled
{ {
#if CLIENT #if CLIENT
get => _csRunPolicy?.Value == "Enabled" || _isCsEnabledForSession; get => _csRunPolicy?.Value == "Enabled" || IsCsEnabledForSession;
#elif SERVER #elif SERVER
// cs settings cannot be changed on the server after launch // cs settings cannot be changed on the server after launch
get => _csRunPolicy?.Value is "Enabled" or "Prompt"; get => _csRunPolicy?.Value is "Enabled" or "Prompt";
#endif #endif
} }
private ISettingList<string> _csRunPolicy; private ISettingList<string> _csRunPolicy;
private bool ShouldPromptForCs => _csRunPolicy?.Value is "Prompt";
/// <summary> /// <summary>
/// Whether usernames are anonymized or show in logs. /// Whether usernames are anonymized or show in logs.
@@ -139,6 +150,10 @@ namespace Barotrauma
ConfigService.TryGetConfig<ISettingBase<bool>>(luaCsPackage, "UseCaching", out var val5) ConfigService.TryGetConfig<ISettingBase<bool>>(luaCsPackage, "UseCaching", out var val5)
? val5 ? val5
: null; : null;
_isCsEnabledForSession =
ConfigService.TryGetConfig<ISettingBase<bool>>(luaCsPackage, "IsCsEnabledForSession", out var val6)
? val6
: null;
if (!ContentPackageManager.EnabledPackages.All.Contains(luaCsPackage)) if (!ContentPackageManager.EnabledPackages.All.Contains(luaCsPackage))
{ {
@@ -237,14 +252,8 @@ namespace Barotrauma
CoroutineManager.Invoke(() => CoroutineManager.Invoke(() =>
{ {
#if CLIENT
bool prevCsEnabled = _isCsEnabledForSession;
#endif
var state = CurrentRunState; var state = CurrentRunState;
SetRunState(RunState.Unloaded); SetRunState(RunState.Unloaded);
#if CLIENT
_isCsEnabledForSession = prevCsEnabled;
#endif
SetRunState(state); SetRunState(state);
}); });
} }
@@ -266,7 +275,7 @@ namespace Barotrauma
SetRunState(state); // restore SetRunState(state); // restore
} }
private void SetRunState(RunState targetRunState) public void SetRunState(RunState targetRunState)
{ {
if (CurrentRunState == targetRunState) if (CurrentRunState == targetRunState)
{ {
@@ -393,9 +402,6 @@ namespace Barotrauma
void RunStateRunning_OnExit(State<RunState> currentState) void RunStateRunning_OnExit(State<RunState> currentState)
{ {
EventService.Call("stop"); EventService.Call("stop");
#if CLIENT
_isCsEnabledForSession = false;
#endif
Logger.LogResults(PackageManagementService.StopRunningPackages()); Logger.LogResults(PackageManagementService.StopRunningPackages());
Logger.LogMessage("LuaCs running state exited"); Logger.LogMessage("LuaCs running state exited");
} }
@@ -2,6 +2,7 @@
using Barotrauma.LuaCs; using Barotrauma.LuaCs;
using Barotrauma.LuaCs.Events; using Barotrauma.LuaCs.Events;
using Barotrauma.Networking; using Barotrauma.Networking;
using Barotrauma.Steam;
using HarmonyLib; using HarmonyLib;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
@@ -95,6 +96,27 @@ internal class HarmonyEventPatchesService : ISystem
_eventService.PublishEvent<IEventScreenSelected>(x => x.OnScreenSelected(__instance)); _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] [HarmonyPatch(typeof(ContentPackageManager.PackageSource), nameof(ContentPackageManager.PackageSource.Refresh)), HarmonyPostfix]
public static void PackageSource_Refresh_Post() public static void PackageSource_Refresh_Post()
{ {