diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66b33713a..85d2f6396 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -118,8 +118,73 @@ jobs: name: swiftlys2_windows_pdb path: build/windows/x64/release/swiftlys2.pdb + linux_loader: + needs: versioning + runs-on: ubuntu-latest + container: + image: registry.gitlab.steamos.cloud/steamrt/sniper/sdk + steps: + - name: Install dependencies + run: | + apt-get update + apt install libreadline-dev + - name: Checkout repository and submodules + uses: actions/checkout@v4 + with: + repository: swiftly-solution/swiftlys2-loader + submodules: recursive + fetch-depth: 0 + - name: Setup xmake + uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: latest + actions-cache-folder: '.xmake-cache' + actions-cache-key: xmake-loader-${{ runner.os }} + - name: Build + run: | + xmake f --cc=gcc-14 --cxx=g++-14 -y + xmake build -y + env: + SWIFTLY_VERSION: ${{ needs.versioning.outputs.semVer }} + - name: Upload plugin as artifact + uses: actions/upload-artifact@v4 + with: + name: swiftlys2_loader_linux + path: build/package/addons + + windows_loader: + needs: versioning + runs-on: windows-latest + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v4 + with: + repository: swiftly-solution/swiftlys2-loader + submodules: recursive + fetch-depth: 0 + - name: Setup xmake + uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: latest + actions-cache-key: xmake-loader-${{ runner.os }} + - name: Build + run: | + xmake build -y + env: + SWIFTLY_VERSION: ${{ needs.versioning.outputs.semVer }} + - name: Upload plugin as artifact + uses: actions/upload-artifact@v4 + with: + name: swiftlys2_loader_windows + path: build/package/addons + - name: Upload PDB as artifact + uses: actions/upload-artifact@v4 + with: + name: swiftlys2_loader_windows_pdb + path: build/windows/x64/release/server.pdb + packaging: - needs: [versioning, linux_core, windows_core, managed] + needs: [versioning, linux_core, windows_core, managed, linux_loader, windows_loader] runs-on: ubuntu-latest env: LINUX_FOLDER: swiftlys2-linux-v${{ needs.versioning.outputs.semVer }} @@ -130,11 +195,31 @@ jobs: with: name: swiftlys2_linux path: ./${{ env.LINUX_FOLDER }} + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: swiftlys2_loader_linux + path: ./${{ env.LINUX_FOLDER }}-loader - name: Download artifacts uses: actions/download-artifact@v4 with: name: swiftlys2_windows path: ./${{ env.WINDOWS_FOLDER }} + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: swiftlys2_windows_pdb + path: ./swiftlys2.pdb + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: swiftlys2_loader_windows + path: ./${{ env.WINDOWS_FOLDER }}-loader + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: swiftlys2_loader_windows_pdb + path: ./server.pdb - name: Download artifacts uses: actions/download-artifact@v4 @@ -161,6 +246,12 @@ jobs: cp -r managed/SwiftlyS2 ${{ env.LINUX_FOLDER }}/swiftlys2/bin/managed cp -r managed/SwiftlyS2 ${{ env.WINDOWS_FOLDER }}/swiftlys2/bin/managed + cp -r ${{ env.LINUX_FOLDER }}-loader/* ${{ env.LINUX_FOLDER }}/ + cp -r ${{ env.WINDOWS_FOLDER }}-loader/* ${{ env.WINDOWS_FOLDER }}/ + + cp -r ./swiftlys2.pdb/swiftlys2.pdb ${{ env.WINDOWS_FOLDER }}/swiftlys2/bin/win64/ + cp -r ./server.pdb/server.pdb ${{ env.WINDOWS_FOLDER }}/swiftlys2/bin/win64/ + mkdir -p ${{ env.LINUX_FOLDER }}/swiftlys2/plugins/ mkdir -p ${{ env.WINDOWS_FOLDER }}/swiftlys2/plugins/ diff --git a/managed/managed.csproj b/managed/managed.csproj index d885e4871..ff5a11582 100644 --- a/managed/managed.csproj +++ b/managed/managed.csproj @@ -21,6 +21,7 @@ build\ $(BaseOutputPath)Release\SwiftlyS2 SwiftlyS2.CS2 + true true $(NoWarn);CS1591 diff --git a/managed/src/SwiftlyS2.Core/Modules/Commands/CommandCallback.cs b/managed/src/SwiftlyS2.Core/Modules/Commands/CommandCallback.cs index 43715699e..b4de05d26 100644 --- a/managed/src/SwiftlyS2.Core/Modules/Commands/CommandCallback.cs +++ b/managed/src/SwiftlyS2.Core/Modules/Commands/CommandCallback.cs @@ -76,7 +76,7 @@ public CommandCallback(string commandName, bool registerRaw, ICommandService.Com var args = argsString.Split('\x01'); var context = new CommandContext(playerId, args, commandNameString, prefixString, slient == 1); - if (string.IsNullOrWhiteSpace(_permissions) || _permissionManager.PlayerHasPermission(_playerManagerService.GetPlayer(playerId).SteamID, _permissions)) + if (!context.IsSentByPlayer || string.IsNullOrWhiteSpace(_permissions) || _permissionManager.PlayerHasPermission(_playerManagerService.GetPlayer(playerId).SteamID, _permissions)) { _handler(context); } diff --git a/managed/src/SwiftlyS2.Core/Modules/Engine/EngineService.cs b/managed/src/SwiftlyS2.Core/Modules/Engine/EngineService.cs index b95139c5d..97eaa387c 100644 --- a/managed/src/SwiftlyS2.Core/Modules/Engine/EngineService.cs +++ b/managed/src/SwiftlyS2.Core/Modules/Engine/EngineService.cs @@ -38,4 +38,10 @@ public bool IsMapValid(string map) { return NativeEngineHelpers.IsMapValid(map); } + + public nint? FindGameSystemByName(string name) + { + var handle = NativeEngineHelpers.FindGameSystemByName(name); + return handle == nint.Zero ? null : handle; + } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs b/managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs index 96a7ca228..be8741f58 100644 --- a/managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs +++ b/managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs @@ -2,6 +2,7 @@ using McMaster.NETCore.Plugins; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using SwiftlyS2.Core.Natives; using SwiftlyS2.Core.Modules.Plugins; using SwiftlyS2.Core.Services; using SwiftlyS2.Shared; @@ -21,6 +22,7 @@ internal class PluginManager private List _SharedTypes { get; set; } = new(); private DateTime lastRead = DateTime.MinValue; + private readonly HashSet reloadingPlugins = new(); public PluginManager( IServiceProvider provider, @@ -77,7 +79,12 @@ public void HandlePluginChange(object sender, FileSystemEventArgs e) { try { - // why i have to make a debounce here? + if (!NativeServerHelpers.UseAutoHotReload()) + { + return; + } + + // Windows FileSystemWatcher triggers multiple (open, write, close) events for a single file change if (DateTime.Now - lastRead < TimeSpan.FromSeconds(1)) { return; @@ -91,10 +98,70 @@ public void HandlePluginChange(object sender, FileSystemEventArgs e) foreach (var plugin in _Plugins) { - if (plugin.Metadata?.Id == Path.GetFileName(directory)) + if (Path.GetFileName(plugin?.PluginDirectory) == Path.GetFileName(directory)) { + var pluginId = plugin.Metadata?.Id; + if (string.IsNullOrWhiteSpace(pluginId)) + { + break; + } + + lock (reloadingPlugins) + { + if (reloadingPlugins.Contains(pluginId)) + { + return; + } + reloadingPlugins.Add(pluginId); + } + lastRead = DateTime.Now; - ReloadPlugin(plugin.Metadata!.Id); + + // meh, Idk why, but when using Mstsc to copy and overwrite files + // it sometimes triggers: "System.IO.IOException: The process cannot access the file because it is being used by another process." + // therefore, we use a retry mechanism + Task.Run(async () => + { + try + { + await Task.Delay(500); + + bool fileLockSuccess = false; + for (int attempt = 0; attempt < 3; attempt++) + { + try + { + using (var stream = File.Open(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.None)) + { + } + fileLockSuccess = true; + break; + } + catch (IOException) when (attempt < 1) + { + _Logger.LogWarning($"{Path.GetFileName(plugin?.PluginDirectory)} is locked, retrying in 500ms... (Attempt {attempt + 1}/3)"); + await Task.Delay(500); + } + catch (IOException) + { + _Logger.LogError($"Failed to reload {Path.GetFileName(plugin?.PluginDirectory)} after 3 attempts"); + } + } + + if (fileLockSuccess) + { + ReloadPlugin(pluginId); + } + } + finally + { + lock (reloadingPlugins) + { + reloadingPlugins.Remove(pluginId); + } + } + }); + break; } } diff --git a/managed/src/SwiftlyS2.Core/Services/PluginConfigurationService.cs b/managed/src/SwiftlyS2.Core/Services/PluginConfigurationService.cs index 65ee0e670..2bd92c92f 100644 --- a/managed/src/SwiftlyS2.Core/Services/PluginConfigurationService.cs +++ b/managed/src/SwiftlyS2.Core/Services/PluginConfigurationService.cs @@ -86,6 +86,7 @@ public IPluginConfigurationService InitializeWithTemplate(string name, string te var options = new JsonSerializerOptions { WriteIndented = true, + IncludeFields = true, PropertyNamingPolicy = null }; diff --git a/managed/src/SwiftlyS2.Generated/Natives/ServerHelpers.cs b/managed/src/SwiftlyS2.Generated/Natives/ServerHelpers.cs index a2b22ad6d..d3cc02596 100644 --- a/managed/src/SwiftlyS2.Generated/Natives/ServerHelpers.cs +++ b/managed/src/SwiftlyS2.Generated/Natives/ServerHelpers.cs @@ -38,4 +38,11 @@ public unsafe static bool IsFollowingServerGuidelines() { var ret = _IsFollowingServerGuidelines(); return ret == 1; } + + private unsafe static delegate* unmanaged _UseAutoHotReload; + + public unsafe static bool UseAutoHotReload() { + var ret = _UseAutoHotReload(); + return ret == 1; + } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Modules/Engine/IEngineService.cs b/managed/src/SwiftlyS2.Shared/Modules/Engine/IEngineService.cs index e88431e7f..f9c296f28 100644 --- a/managed/src/SwiftlyS2.Shared/Modules/Engine/IEngineService.cs +++ b/managed/src/SwiftlyS2.Shared/Modules/Engine/IEngineService.cs @@ -46,4 +46,11 @@ public interface IEngineService /// The number of simulation ticks that have occurred since the server started. /// int TickCount { get; } + + /// + /// Find a game system by name. + /// + /// The name of the game system. + /// The game system handle. Null if not found. + nint? FindGameSystemByName(string name); } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Modules/EntitySystem/CEntityKeyValues.cs b/managed/src/SwiftlyS2.Shared/Modules/EntitySystem/CEntityKeyValues.cs index f7e2f9610..1780641c9 100644 --- a/managed/src/SwiftlyS2.Shared/Modules/EntitySystem/CEntityKeyValues.cs +++ b/managed/src/SwiftlyS2.Shared/Modules/EntitySystem/CEntityKeyValues.cs @@ -16,7 +16,7 @@ public void Dispose() { _handle.Dispose(); } - internal nint Address => _handle.Address; + public nint Address => _handle.Address; public void SetBool(string key, bool value) { NativeCEntityKeyValues.SetBool(Address, key, value); diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlLeanVector.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlLeanVector.cs index 8cef2d1f4..3c7ce0358 100644 --- a/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlLeanVector.cs +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlLeanVector.cs @@ -40,6 +40,10 @@ public override bool Equals(object? obj) private I ExternalBufferMarker => I.One << ((Marshal.SizeOf() * 8) - 1); + /// + /// Please use instead to construct it. + /// If you really want to use this, you should call after you are done with it. + /// public CUtlLeanVector(I growSize, I initSize) { Count = (I)(object)0; @@ -47,6 +51,10 @@ public CUtlLeanVector(I growSize, I initSize) EnsureCapacity(int.CreateChecked(initSize), true); } + /// + /// Please use instead to construct it. + /// If you really want to use this, you should call after you are done with it. + /// public CUtlLeanVector(nint memory, I allocationCount, I numElements) { Count = numElements; diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlMemory.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlMemory.cs index 41e6b0f25..4de228972 100644 --- a/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlMemory.cs +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlMemory.cs @@ -14,7 +14,7 @@ public enum BufferMarkers } [StructLayout(LayoutKind.Sequential)] -public struct CUtlMemory : IDisposable +public struct CUtlMemory { private nint _memory; private uint _allocationCount; @@ -22,6 +22,10 @@ public struct CUtlMemory : IDisposable public int ElementSize => SchemaSize.Get(); + /// + /// Please use instead to construct it. + /// If you really want to use this, you should call after you are done with it. + /// public CUtlMemory(int growSize, int initSize) { _memory = 0; @@ -30,6 +34,10 @@ public CUtlMemory(int growSize, int initSize) Init(growSize, initSize); } + /// + /// Please use instead to construct it. + /// If you really want to use this, you should call after you are done with it. + /// public CUtlMemory(nint memory, int numelements, bool readOnly) { _memory = 0; @@ -38,11 +46,6 @@ public CUtlMemory(nint memory, int numelements, bool readOnly) SetExternalBuffer(memory, numelements, readOnly); } - public void Dispose() - { - Purge(); - } - public void Init(int growSize, int initSize) { Purge(); diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlVector.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlVector.cs index ca3e9d832..9ab921c79 100644 --- a/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlVector.cs +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/CUtlVector.cs @@ -7,31 +7,34 @@ using SwiftlyS2.Shared.Schemas; [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 24)] -public struct CUtlVector : IDisposable, IEnumerable +public struct CUtlVector : IEnumerable { private int _size; private CUtlMemory _memory; public int ElementSize => SchemaSize.Get(); + /// + /// Please use instead to construct it. + /// If you really want to use this, you should call after you are done with it. + /// public CUtlVector(int growSize, int initSize) { _memory = new(growSize, initSize); _size = 0; } + /// + /// Please use instead to construct it. + /// If you really want to use this, you should call after you are done with it. + /// public CUtlVector(nint memory, int allocationCount, int numElements) { _memory = new(memory, allocationCount, false); _size = numElements; } - public void Dispose() - { - Purge(); - } - - void Purge() + public void Purge() { RemoveAll(); _memory.Purge(); diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlLeanVector.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlLeanVector.cs new file mode 100644 index 000000000..65b268290 --- /dev/null +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlLeanVector.cs @@ -0,0 +1,47 @@ +using System.Numerics; + +namespace SwiftlyS2.Shared.Natives; + +public class ManagedCUtlLeanVector : IDisposable where T : unmanaged where I : unmanaged, IBinaryInteger, IMinMaxValue +{ + private CUtlLeanVector _vector; + private bool _disposed; + + public ManagedCUtlLeanVector() + { + _vector = new CUtlLeanVector(I.Zero, I.One); + } + + public ManagedCUtlLeanVector(I growSize, I initSize) + { + _vector = new CUtlLeanVector(growSize, initSize); + } + + ~ManagedCUtlLeanVector() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + _vector.Purge(); + + _disposed = true; + } + + private void ThrowIfDisposed() + { + if (_disposed) + throw new ObjectDisposedException(nameof(ManagedCUtlLeanVector)); + } + + public ref CUtlLeanVector Value { get { ThrowIfDisposed(); return ref _vector; } } +} \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlMemory.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlMemory.cs index e28989c33..b9b7711e0 100644 --- a/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlMemory.cs +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlMemory.cs @@ -3,24 +3,42 @@ namespace SwiftlyS2.Shared.Natives; public class ManagedCUtlMemory : IDisposable where T : unmanaged { private CUtlMemory _memory; + private bool _disposed; + public ManagedCUtlMemory() + { + _memory = new CUtlMemory(0, 1); + } public ManagedCUtlMemory(int growSize, int initSize) { _memory = new CUtlMemory(growSize, initSize); } - public ManagedCUtlMemory(nint memory, int numelements, bool readOnly) + ~ManagedCUtlMemory() { - _memory = new CUtlMemory(memory, numelements, readOnly); + Dispose(false); } public void Dispose() { - _memory.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } - public nint Base => _memory.Base; - public int Count => _memory.Count; + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + _memory.Purge(); + + _disposed = true; + } + + private void ThrowIfDisposed() + { + if (_disposed) + throw new ObjectDisposedException(nameof(ManagedCUtlMemory)); + } - public ref T this[int index] => ref _memory[index]; + public ref CUtlMemory Value { get { ThrowIfDisposed(); return ref _memory; } } } \ No newline at end of file diff --git a/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlVector.cs b/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlVector.cs index d7204d731..4060b6044 100644 --- a/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlVector.cs +++ b/managed/src/SwiftlyS2.Shared/Natives/Structs/ManagedCUtlVector.cs @@ -3,24 +3,45 @@ namespace SwiftlyS2.Shared.Natives; public class ManagedCUtlVector : IDisposable where T : unmanaged { private CUtlVector _vector; + private bool _disposed; + + public ManagedCUtlVector() + { + _vector = new CUtlVector(0, 1); + } public ManagedCUtlVector(int growSize, int initSize) { _vector = new CUtlVector(growSize, initSize); } - public ManagedCUtlVector(nint memory, int allocationCount, int numElements) + ~ManagedCUtlVector() { - _vector = new CUtlVector(memory, allocationCount, numElements); + Dispose(false); } public void Dispose() { - _vector.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + + _vector.Purge(); + + _disposed = true; + } + + private void ThrowIfDisposed() + { + if (_disposed) + throw new ObjectDisposedException(nameof(ManagedCUtlVector)); } - public nint Base => _vector.Base; - public int Count => _vector.Count; - public ref T this[int index] => ref _vector[index]; + public ref CUtlVector Value { get { ThrowIfDisposed(); return ref _vector; } } } \ No newline at end of file diff --git a/natives/server/helpers.native b/natives/server/helpers.native index 4fbaa820f..004c5719c 100644 --- a/natives/server/helpers.native +++ b/natives/server/helpers.native @@ -2,4 +2,5 @@ class ServerHelpers string GetServerLanguage = void bool UsePlayerLanguage = void -bool IsFollowingServerGuidelines = void \ No newline at end of file +bool IsFollowingServerGuidelines = void +bool UseAutoHotReload = void \ No newline at end of file diff --git a/plugin_files/configs/core.example.jsonc b/plugin_files/configs/core.example.jsonc index 4396d74ad..82f60fe43 100644 --- a/plugin_files/configs/core.example.jsonc +++ b/plugin_files/configs/core.example.jsonc @@ -6,6 +6,7 @@ "CommandSilentPrefixes": [ "/" ], + "AutoHotReload": true, "ConsoleFilter": true, "FollowCS2ServerGuidelines": true, "Language": "en", diff --git a/src/scripting/engine/enginehelpers.cpp b/src/scripting/engine/enginehelpers.cpp index 24ebd7344..aecdbabfa 100644 --- a/src/scripting/engine/enginehelpers.cpp +++ b/src/scripting/engine/enginehelpers.cpp @@ -31,6 +31,12 @@ #include +struct CBaseGameSystemFactory_t : public IGameSystemFactory { + CBaseGameSystemFactory_t* m_pNext; + const char* m_pName; + void** reallocating_ptr; +}; + int Bridge_EngineHelpers_GetServerIP(char* out) { static std::string s; @@ -117,7 +123,19 @@ int Bridge_EngineHelpers_GetServerTickCount() void* Bridge_EngineHelpers_FindGameSystemByName(const char* name) { - return CBaseGameSystemFactory::GetGlobalPtrByName(name); + CBaseGameSystemFactory_t* pFactoryList = *reinterpret_cast(CBaseGameSystemFactory::sm_pFirst); + while (pFactoryList) + { + if (strcmp(pFactoryList->m_pName, name) == 0) + { + if (pFactoryList->IsReallocating()) { + return *pFactoryList->reallocating_ptr; + } + return pFactoryList->GetStaticGameSystem(); + } + pFactoryList = pFactoryList->m_pNext; + } + return nullptr; } void Bridge_EngineHelpers_SendMessageToConsole(const char* message) diff --git a/src/scripting/server/helpers.cpp b/src/scripting/server/helpers.cpp index d25e4e77f..05eda8ff9 100644 --- a/src/scripting/server/helpers.cpp +++ b/src/scripting/server/helpers.cpp @@ -43,6 +43,13 @@ bool Bridge_ServerHelpers_IsFollowingServerGuidelines() return std::get(configuration->GetValue("core.FollowCS2ServerGuidelines")); } +bool Bridge_ServerHelpers_UseAutoHotReload() +{ + static auto configuration = g_ifaceService.FetchInterface(CONFIGURATION_INTERFACE_VERSION); + return std::get(configuration->GetValue("core.AutoHotReload")); +} + DEFINE_NATIVE("ServerHelpers.GetServerLanguage", Bridge_ServerHelpers_GetServerLanguage); DEFINE_NATIVE("ServerHelpers.UsePlayerLanguage", Bridge_ServerHelpers_UsePlayerLanguage); -DEFINE_NATIVE("ServerHelpers.IsFollowingServerGuidelines", Bridge_ServerHelpers_IsFollowingServerGuidelines); \ No newline at end of file +DEFINE_NATIVE("ServerHelpers.IsFollowingServerGuidelines", Bridge_ServerHelpers_IsFollowingServerGuidelines); +DEFINE_NATIVE("ServerHelpers.UseAutoHotReload", Bridge_ServerHelpers_UseAutoHotReload); \ No newline at end of file diff --git a/src/server/commands/manager.cpp b/src/server/commands/manager.cpp index 0386ba872..5fb4efc25 100644 --- a/src/server/commands/manager.cpp +++ b/src/server/commands/manager.cpp @@ -47,6 +47,8 @@ static void commandsCallback(const CCommandContext& context, const CCommand& arg tokenizedArgs.Tokenize(args.GetCommandString()); std::string commandName = tokenizedArgs[0]; + + std::transform(commandName.begin(), commandName.end(), commandName.begin(), ::tolower); std::string originalCommandName = commandName; if (!g_mCommandHandlers.contains(commandName)) commandName = "sw_" + commandName; if (!g_mCommandHandlers.contains(commandName)) return; @@ -147,6 +149,7 @@ int CServerCommands::HandleCommand(int playerid, const std::string& text) return 0; commandName.erase(0, selectedPrefix.size()); + std::transform(commandName.begin(), commandName.end(), commandName.begin(), ::tolower); std::string originalCommandName = commandName; if (!g_mCommandHandlers.contains(commandName)) commandName = "sw_" + commandName; @@ -187,6 +190,8 @@ bool CServerCommands::HandleClientChat(int playerid, const std::string& text, bo uint64_t CServerCommands::RegisterCommand(std::string command_name, std::function, std::string, std::string, bool)> handler, bool registerRaw) { + std::transform(command_name.begin(), command_name.end(), command_name.begin(), ::tolower); + if (!registerRaw) { if (conCommandCreated.contains(command_name)) diff --git a/src/server/configuration/configuration.cpp b/src/server/configuration/configuration.cpp index 6b855e87d..4593f10fd 100644 --- a/src/server/configuration/configuration.cpp +++ b/src/server/configuration/configuration.cpp @@ -462,6 +462,7 @@ bool Configuration::Load() RegisterConfigurationVector(wasEdited, config_json, "core", "core", "CommandPrefixes", { "!" }, true, " "); RegisterConfigurationVector(wasEdited, config_json, "core", "core", "CommandSilentPrefixes", { "/" }, true, " "); + RegisterConfiguration(wasEdited, config_json, "core", "core", "AutoHotReload", true); RegisterConfiguration(wasEdited, config_json, "core", "core", "ConsoleFilter", true); RegisterConfigurationVector(wasEdited, config_json, "core", "core", "PatchesToPerform", {}, true, " "); diff --git a/vendor/metamod b/vendor/metamod index c1cc0a613..4399ff0fd 160000 --- a/vendor/metamod +++ b/vendor/metamod @@ -1 +1 @@ -Subproject commit c1cc0a6134b3503abb40e18227ef04619db33841 +Subproject commit 4399ff0fd4ad7a6620f6763afe6fc0e30005496a