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

View File

@@ -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 != 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"),
@@ -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. 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
};
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)
{
PromptCSharpMods(selection =>
{
SetRunState(RunState.Running);
}, joiningServer: true);
}
else
{
SetRunState(RunState.Running);
});
}
break;
default:
Logger.LogError(

View File

@@ -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)

View File

@@ -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"/>
</Settings>
</Configuration>

View File

@@ -85,20 +85,31 @@ namespace Barotrauma
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);
ConfigService.SaveConfigValue(_isCsEnabledForSession);
}
}
/// <summary>
/// Whether C# plugin code is enabled.
/// </summary>
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";
/// <summary>
/// Whether usernames are anonymized or show in logs.
@@ -139,6 +150,10 @@ 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))
{
@@ -237,14 +252,8 @@ namespace Barotrauma
CoroutineManager.Invoke(() =>
{
#if CLIENT
bool prevCsEnabled = _isCsEnabledForSession;
#endif
var state = CurrentRunState;
SetRunState(RunState.Unloaded);
#if CLIENT
_isCsEnabledForSession = prevCsEnabled;
#endif
SetRunState(state);
});
}
@@ -266,7 +275,7 @@ namespace Barotrauma
SetRunState(state); // restore
}
private void SetRunState(RunState targetRunState)
public void SetRunState(RunState targetRunState)
{
if (CurrentRunState == targetRunState)
{
@@ -393,9 +402,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");
}

View File

@@ -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;
@@ -95,6 +96,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()
{