Files
NotAlwaysTrue 9b35f6b23f 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 from bb21a09244

* Added networking tests for configs.

* Added missing diffs for f61f852a25.

* 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>
2026-04-25 12:10:24 +08:00

536 lines
23 KiB
C#

using Barotrauma.LuaCs;
using Barotrauma.LuaCs.Compatibility;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Events;
using LightInject;
using MoonSharp.Interpreter;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using AssemblyLoader = Barotrauma.LuaCs.AssemblyLoader;
[assembly: InternalsVisibleTo("ImpromptuInterfaceDynamicAssembly")]
[assembly: InternalsVisibleTo("Dynamitey")]
namespace Barotrauma
{
internal delegate void LuaCsMessageLogger(string message);
internal delegate void LuaCsErrorHandler(Exception ex, LuaCsMessageOrigin origin);
internal delegate void LuaCsExceptionHandler(Exception ex, LuaCsMessageOrigin origin);
partial class LuaCsSetup : IDisposable, IEventScreenSelected, IEventEnabledPackageListChanged,
IEventReloadAllPackages
{
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)
{
throw new Exception("Tried to create another LuaCsSetup instance");
}
DebugConsoleCommandVanillaIndex = DebugConsole.Commands.Count;
// == startup
_servicesProvider = SetupServicesProvider();
_runStateMachine = SetupStateMachine();
SubscribeToLuaCsEvents();
}
private void SubscribeToLuaCsEvents()
{
EventService.Subscribe<IEventScreenSelected>(this); // game state hook in
EventService.Subscribe<IEventEnabledPackageListChanged>(this);
EventService.Subscribe<IEventReloadAllPackages>(this);
}
#region CONST_DEF
#if SERVER
public const bool IsServer = true;
#else
public const bool IsServer = false;
#endif
public const bool IsClient = !IsServer;
#endregion
#region Services_CVars
/*
* === Singleton Services
*/
private readonly IServicesProvider _servicesProvider;
private PerformanceCounterService _performanceCounterService;
public PerformanceCounterService PerformanceCounterService => _performanceCounterService ??= _servicesProvider.GetService<PerformanceCounterService>();
public ILoggerService Logger => _servicesProvider.GetService<ILoggerService>();
public IConfigService ConfigService => _servicesProvider.GetService<IConfigService>();
public IPackageManagementService PackageManagementService => _servicesProvider.GetService<IPackageManagementService>();
public IPluginManagementService PluginManagementService => _servicesProvider.GetService<IPluginManagementService>();
public ILuaScriptManagementService LuaScriptManagementService => _servicesProvider.GetService<ILuaScriptManagementService>();
public INetworkingService NetworkingService => _servicesProvider.GetService<INetworkingService>();
// hotpath performance ref cache
private IEventService _eventService = null;
public IEventService EventService => _eventService ??= _servicesProvider.GetService<IEventService>();
// 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.
/// </summary>
public bool IsCsEnabled
{
#if CLIENT
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;
public string CsRunPolicyValue => _csRunPolicy?.Value ?? "Prompt";
/// <summary>
/// Whether usernames are anonymized or show in logs.
/// </summary>
public bool HideUserNamesInLogs
{
get => _hideUserNamesInLogs?.Value ?? false;
internal set => _hideUserNamesInLogs?.TrySetValue(value);
}
private ISettingBase<bool> _hideUserNamesInLogs;
public bool UseCaching
{
get => _useCaching?.Value ?? true;
}
private ISettingBase<bool> _useCaching;
public static ContentPackage GetLuaCsPackage()
{
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()
{
var luaCsPackage = GetLuaCsPackage();
_csRunPolicy =
ConfigService.TryGetConfig<ISettingList<string>>(luaCsPackage, "CsRunPolicy", out var val1)
? val1
: null;
_hideUserNamesInLogs =
ConfigService.TryGetConfig<ISettingBase<bool>>(luaCsPackage, "HideUserNamesInLogs", out var val4)
? val4
: null;
_useCaching =
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()
{
var servicesProvider = new ServicesProvider();
// Base Service
servicesProvider.RegisterServiceType<ILoggerService, LoggerService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<PerformanceCounterService, PerformanceCounterService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IStorageService, StorageService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<ISafeStorageService, SafeStorageService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IEventService, EventService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceResolver<ILuaCsHook>(factory => factory.GetInstance<IEventService>() as ILuaCsHook);
servicesProvider.RegisterServiceType<IPackageManagementService, PackageManagementService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IAssemblyManagementService, PluginManagementService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceResolver<IPluginManagementService>(factory => factory.GetInstance<IAssemblyManagementService>());
servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptManagementService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IConfigService, ConfigService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<INetworkingService, NetworkingService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<INetworkIdProvider, NetworkingIdProvider>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<HarmonyEventPatchesService, HarmonyEventPatchesService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IConsoleCommandsService, ConsoleCommandsService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<MainMenuPatch, MainMenuPatch>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceResolver<ILuaConfigService>(factory => factory.GetInstance<IConfigService>() as ILuaConfigService);
// Extension/Sub Services
servicesProvider.RegisterServiceType<IAssemblyLoaderService.IFactory, AssemblyLoader.Factory>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<ISettingsRegistrationProvider, SettingsEntryRegistrar>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IModConfigService, ModConfigService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, IAssemblyResourceInfo>, ModConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, ILuaScriptResourceInfo>, ModConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceAsync<ResourceParserInfo, IConfigResourceInfo>, ModConfigFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigInfo>, SettingsFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<IParserServiceOneToManyAsync<IConfigResourceInfo, IConfigProfileInfo>, SettingsFileParserService>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<INetworkIdProvider, NetworkingIdProvider>(ServiceLifetime.Transient);
// All Lua Extras
servicesProvider.RegisterServiceType<IDefaultLuaRegistrar, DefaultLuaRegistrar>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<ILuaPatcher, LuaPatcherService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<ILuaUserDataService, LuaUserDataService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<ISafeLuaUserDataService, SafeLuaUserDataService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<ILuaCsInfoProvider, LuaCsInfoProvider>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<ILuaScriptLoader, LuaScriptLoader>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceType<LuaGame, LuaGame>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<ILuaCsTimer, LuaCsTimer>(ServiceLifetime.Singleton);
// service config data
servicesProvider.RegisterServiceType<IStorageServiceConfig, StorageServiceConfig>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<ILuaScriptServicesConfig, LuaScriptServicesConfig>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IConfigServiceConfig, ConfigServiceConfig>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IPackageManagementServiceConfig, PackageManagementServiceConfig>(ServiceLifetime.Singleton);
#if CLIENT
SetupServicesProviderClient(servicesProvider);
#endif
// gen IL
servicesProvider.CompileAndRun();
return servicesProvider;
}
#endregion
#region StateMachine
private RunState _runState;
/// <summary>
/// The current run state of all services managed by LuaCs.
/// </summary>
public RunState CurrentRunState
{
get => _runState;
private set => _runState = value;
}
private readonly StateMachine<RunState> _runStateMachine;
public void OnEnabledPackageListChanged(CorePackage package, IEnumerable<RegularPackage> regularPackages)
{
ProcessEnabledPackageChanges(new []{ package }.Concat<ContentPackage>(regularPackages).ToImmutableArray());
}
public void OnReloadAllPackages()
{
CoroutineManager.Invoke(() =>
{
SetRunState(RunState.Unloaded);
CoroutineManager.Invoke(() =>
{
SetRunState(RunState.Running);
},0.25f);
});
}
private void ProcessEnabledPackageChanges(ImmutableArray<ContentPackage> packages)
{
if (CurrentRunState < RunState.LoadedNoExec)
{
return;
}
var state = CurrentRunState;
if (CurrentRunState > RunState.LoadedNoExec)
{
SetRunState(RunState.LoadedNoExec);
}
this.Logger.LogResults(PackageManagementService.SyncLoadedPackagesList(GetLuaCsEnabledPackagesList(packages)));
ConfigService.LoadSavedConfigsValues();
SetRunState(state); // restore
}
public void SetRunState(RunState targetRunState)
{
if (CurrentRunState == targetRunState)
{
return;
}
_runStateMachine.GotoState(targetRunState);
}
private ImmutableArray<ContentPackage> GetEnabledPackagesList()
=> GetLuaCsEnabledPackagesList(ContentPackageManager.EnabledPackages.Regular
.ToImmutableArray<ContentPackage>());
private ImmutableArray<ContentPackage> GetLuaCsEnabledPackagesList(ImmutableArray<ContentPackage> enabledRegular)
{
if (!enabledRegular.Any(p => p.Name.Equals(PackageName, StringComparison.InvariantCultureIgnoreCase)))
{
var luaCs = ContentPackageManager.AllPackages.FirstOrDefault(p => p.Name.Equals(PackageName, StringComparison.InvariantCultureIgnoreCase));
if (luaCs is null)
{
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;
}
enabledRegular = new[] { luaCs }.Concat(enabledRegular).ToImmutableArray();
}
return enabledRegular;
}
private StateMachine<RunState> SetupStateMachine()
{
return new StateMachine<RunState>(false, RunState.Unloaded, onEnter: RunStateUnloaded_OnEnter, null)
.AddState(RunState.LoadedNoExec, onEnter: RunStateLoadedNoExec_OnEnter, null)
.AddState(RunState.Running, onEnter: RunStateRunning_OnEnter, RunStateRunning_OnExit);
// ReSharper disable InconsistentNaming
void RunStateUnloaded_OnEnter(State<RunState> currentState)
{
Logger.LogMessage("LuaCs unloaded state entered");
if (PackageManagementService.IsAnyPackageRunning())
{
Logger.LogResults(PackageManagementService.StopRunningPackages());
}
if (PackageManagementService.IsAnyPackageLoaded())
{
DisposeLuaCsConfig();
Logger.LogResults(PackageManagementService.UnloadAllPackages());
}
EventService.Reset();
ConfigService.Reset();
LuaScriptManagementService.Reset();
PackageManagementService.Reset();
NetworkingService.Reset();
Game.Reset();
_servicesProvider.GetService<MainMenuPatch>().Reset();
Logger.LogMessage("Services have been reset");
SubscribeToLuaCsEvents();
CurrentRunState = RunState.Unloaded;
}
void RunStateLoadedNoExec_OnEnter(State<RunState> currentState)
{
Logger.LogMessage("LuaCs no execution state entered");
if (PackageManagementService.IsAnyPackageRunning())
{
Logger.LogResults(PackageManagementService.StopRunningPackages());
}
if (!PackageManagementService.IsAnyPackageLoaded())
{
foreach (var registrationProvider in _servicesProvider.GetAllServices<ISettingsRegistrationProvider>())
{
registrationProvider.RegisterTypeProviders(ConfigService, null);
}
Logger.LogResults(PackageManagementService.LoadPackagesInfo(GetEnabledPackagesList()));
Logger.LogResults(ConfigService.LoadSavedConfigsValues());
LoadLuaCsConfig();
}
CurrentRunState = RunState.LoadedNoExec;
}
void RunStateRunning_OnEnter(State<RunState> currentState)
{
if (!PackageManagementService.IsAnyPackageLoaded())
{
foreach (var registrationProvider in _servicesProvider.GetAllServices<ISettingsRegistrationProvider>())
{
registrationProvider.RegisterTypeProviders(ConfigService, null);
}
Logger.LogResults(PackageManagementService.LoadPackagesInfo(GetEnabledPackagesList()));
Logger.LogResults(ConfigService.LoadSavedConfigsValues());
LoadLuaCsConfig();
}
string csEnabled = IsCsEnabled ? "enabled" : "disabled";
Logger.LogMessage($"LuaCs running state entered. Running under commit {AssemblyInfo.GitRevision}, CSharp is {csEnabled}");
if (!PackageManagementService.IsAnyPackageRunning())
{
Logger.LogResults(PackageManagementService.ExecuteLoadedPackages(GetEnabledPackagesList(), IsCsEnabled));
}
#if CLIENT
// Technically not very accurate, but we want to call after we run mods anyway
if (GameMain.Client != null)
{
EventService.PublishEvent<IEventServerConnected>(static p => p.OnServerConnected());
}
#endif
#if SERVER
GameMain.Server.ServerSettings.LoadClientPermissions();
#endif
CurrentRunState = RunState.Running;
}
void RunStateRunning_OnExit(State<RunState> currentState)
{
EventService.Call("stop");
Logger.LogResults(PackageManagementService.StopRunningPackages());
Logger.LogMessage("LuaCs running state exited");
}
// ReSharper restore InconsistentNaming
}
#endregion
/// <summary>
/// Checks for Cs Execution Policy (ie. prompting the user) and then calls the delegate once completed.
/// </summary>
/// <param name="onReadyToRun"></param>
partial void CheckReadyToRun(Action onReadyToRun);
#region LegacyRedirects
// --- Compatibility
/// <summary>
/// <b>[Obsolete]</b> Legacy support only.
/// </summary>
[Obsolete]
public LuaCsPerformanceCounter PerformanceCounter { get; private set; } = new LuaCsPerformanceCounter();
/// <summary>
/// <b>[Obsolete] Use <see cref="IPluginManagementService"/> instead.</b>
/// </summary>
[Obsolete($"Use {nameof(PluginManagementService)} instead.")]
public IPluginManagementService PluginPackageManager => this.PluginManagementService;
public ILuaCsHook Hook => this.EventService;
public INetworkingService Networking => this.NetworkingService;
public ILuaCsTimer Timer => _servicesProvider.GetService<ILuaCsTimer>();
public DynValue CallLuaFunction(object function, params object[] args) => LuaScriptManagementService.CallFunctionSafe(function, args);
#endregion
public void Dispose()
{
try
{
SetRunState(RunState.Unloaded);
}
catch (Exception e)
{
Logger.LogError(e.Message);
}
try
{
DisposeLuaCsConfig();
PluginManagementService.Dispose();
LuaScriptManagementService.Dispose();
ConfigService.Dispose();
PackageManagementService.Dispose();
// TODO: Add all missing services.
//NetworkingService.Dispose();
EventService.Dispose();
_eventService = null;
_game = null;
PerformanceCounter = null;
_servicesProvider.DisposeAndReset();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
_luaCsSetup = null;
GC.SuppressFinalize(this);
}
/// <summary>
/// Handles changes in game states tracked by screen changes.
/// </summary>
/// <param name="screen">The new game screen.</param>
public partial void OnScreenSelected(Screen screen);
void DisposeLuaCsConfig()
{
_csRunPolicy = null;
_hideUserNamesInLogs = null;
}
}
/// <summary>
/// Specifies the current run state of the LuaCs Modding System.
/// <b>[Important]Enum State values ordering must be in the form of (lower state) === (higher state)</b>
/// </summary>
public enum RunState : byte
{
/// <summary>
/// No assets are loaded, code execution suspended.
/// </summary>
Unloaded = 0,
/// <summary>
/// Loaded mod configs, settings and assets. No code execution.
/// </summary>
LoadedNoExec = 1,
/// <summary>
/// All assets loaded, code execution is active.
/// </summary>
Running = 2
}
}