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