Compare commits

...

5 Commits

Author SHA1 Message Date
roflmuffin
64d1c0a9f4 chore: remove erroneous log 2024-03-04 16:56:49 +10:00
roflmuffin
a6de51c444 fix: use concurrent dictionary for function reference 2024-03-04 13:41:18 +10:00
roflmuffin
2535ac0575 feat: add assembly name lazy loading of shared libraries 2024-03-04 12:15:37 +10:00
Michael Wilson
bc61323315 chore: migrate to protobufs submodule (#362) 2024-03-04 10:48:52 +10:00
roflmuffin
241817b7f2 feat: update game events dump from Feb 14 update 2024-03-04 10:34:58 +10:00
13 changed files with 102 additions and 50 deletions

6
.gitmodules vendored
View File

@@ -14,12 +14,12 @@
[submodule "libraries/dyncall"]
path = libraries/dyncall
url = https://github.com/LWJGL-CI/dyncall
[submodule "libraries/GameTracking-CS2"]
path = libraries/GameTracking-CS2
url = https://github.com/SteamDatabase/GameTracking-CS2
[submodule "libraries/DynoHook"]
path = libraries/DynoHook
url = https://github.com/qubka/DynoHook
[submodule "libraries/asmjit"]
path = libraries/asmjit
url = https://github.com/asmjit/asmjit
[submodule "libraries/Protobufs"]
path = libraries/Protobufs
url = https://github.com/SteamDatabase/Protobufs

View File

@@ -103,8 +103,8 @@ if (LINUX)
)
endif()
set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs)
file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs/*.proto")
set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/Protobufs/csgo)
file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/Protobufs/csgo/*.proto")
## Generate protobuf source & headers
if (LINUX)

1
libraries/Protobufs Submodule

Submodule libraries/Protobufs added at 686a0628e6

View File

@@ -130,11 +130,11 @@ namespace CounterStrikeSharp.API.Core
{
var sb = new StringBuilder();
sb.AppendFormat(" [#{0}:{1}]: \"{2}\" ({3})", plugin.PluginId,
plugin.State.ToString().ToUpper(), plugin.Plugin.ModuleName,
plugin.Plugin.ModuleVersion);
if (!string.IsNullOrEmpty(plugin.Plugin.ModuleAuthor))
plugin.State.ToString().ToUpper(), plugin.Plugin?.ModuleName ?? "Unknown",
plugin.Plugin?.ModuleVersion ?? "Unknown");
if (!string.IsNullOrEmpty(plugin.Plugin?.ModuleAuthor))
sb.AppendFormat(" by {0}", plugin.Plugin.ModuleAuthor);
if (!string.IsNullOrEmpty(plugin.Plugin.ModuleDescription))
if (!string.IsNullOrEmpty(plugin.Plugin?.ModuleDescription))
{
sb.Append("\n");
sb.Append(" ");

View File

@@ -15,6 +15,7 @@
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
@@ -99,7 +100,7 @@ namespace CounterStrikeSharp.API.Core
{
Remove(Identifier);
if (references.ContainsKey(m_method))
references.Remove(m_method);
references.Remove(m_method, out _);
}
}
});
@@ -112,9 +113,9 @@ namespace CounterStrikeSharp.API.Core
public static FunctionReference Create(Delegate method)
{
if (references.ContainsKey(method))
if (references.TryGetValue(method, out var existingReference))
{
return references[method];
return existingReference;
}
var reference = new FunctionReference(method);
@@ -125,11 +126,11 @@ namespace CounterStrikeSharp.API.Core
return reference;
}
private static Dictionary<int, FunctionReference> ms_references = new Dictionary<int, FunctionReference>();
private static ConcurrentDictionary<int, FunctionReference> ms_references = new ConcurrentDictionary<int, FunctionReference>();
private static int ms_referenceId;
private static Dictionary<Delegate, FunctionReference> references =
new Dictionary<Delegate, FunctionReference>();
private static ConcurrentDictionary<Delegate, FunctionReference> references =
new ConcurrentDictionary<Delegate, FunctionReference>();
private static int Register(FunctionReference reference)
{
@@ -162,7 +163,7 @@ namespace CounterStrikeSharp.API.Core
{
if (ms_references.TryGetValue(reference, out var funcRef))
{
ms_references.Remove(reference);
ms_references.Remove(reference, out _);
Application.Instance.Logger.LogDebug("Removing function/callback reference: {Reference}", reference);
}

View File

@@ -7119,6 +7119,15 @@ namespace CounterStrikeSharp.API.Core
}
}
[EventName("warmup_end")]
public class EventWarmupEnd : GameEvent
{
public EventWarmupEnd(IntPtr pointer) : base(pointer){}
public EventWarmupEnd(bool force) : base("warmup_end", force){}
}
[EventName("weapon_fire")]
public class EventWeaponFire : GameEvent
{

View File

@@ -1,8 +1,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using CounterStrikeSharp.API.Core.Capabilities;
using CounterStrikeSharp.API.Core.Commands;
using CounterStrikeSharp.API.Core.Hosting;
using McMaster.NETCore.Plugins;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -15,8 +18,11 @@ public class PluginManager : IPluginManager
private readonly ICommandManager _commandManager;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<PluginManager> _logger;
private readonly Dictionary<string, Assembly> _sharedAssemblies = new();
private bool _loadedSharedLibs = false;
public PluginManager(IScriptHostConfiguration scriptHostConfiguration, ICommandManager commandManager, ILogger<PluginManager> logger, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory)
public PluginManager(IScriptHostConfiguration scriptHostConfiguration, ICommandManager commandManager,
ILogger<PluginManager> logger, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory)
{
_scriptHostConfiguration = scriptHostConfiguration;
_commandManager = commandManager;
@@ -24,6 +30,36 @@ public class PluginManager : IPluginManager
_serviceProvider = serviceProvider;
}
private void LoadLibrary(string path)
{
var loader = PluginLoader.CreateFromAssemblyFile(path, new[] { typeof(IPlugin), typeof(PluginCapability<>), typeof(PlayerCapability<>) },
config => { config.PreferSharedTypes = true; });
var assembly = loader.LoadDefaultAssembly();
_sharedAssemblies[assembly.GetName().FullName] = assembly;
}
private void LoadSharedLibraries()
{
var sharedDirectory = Directory.GetDirectories(_scriptHostConfiguration.SharedPath);
var sharedAssemblyPaths = sharedDirectory
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
.Where(File.Exists)
.ToArray();
foreach (var sharedAssemblyPath in sharedAssemblyPaths)
{
try
{
LoadLibrary(sharedAssemblyPath);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to load shared assembly from {Path}", sharedAssemblyPath);
}
}
}
public void Load()
{
var pluginDirectories = Directory.GetDirectories(_scriptHostConfiguration.PluginPath);
@@ -31,23 +67,22 @@ public class PluginManager : IPluginManager
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
.Where(File.Exists)
.ToArray();
var sharedDirectory = Directory.GetDirectories(_scriptHostConfiguration.SharedPath);
var sharedAssemblyPaths = sharedDirectory
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
.Where(File.Exists)
.ToArray();
foreach (var sharedAssemblyPath in sharedAssemblyPaths)
AssemblyLoadContext.Default.Resolving += (context, name) =>
{
try
if (!_loadedSharedLibs)
{
AssemblyLoadContext.Default.LoadFromAssemblyPath(sharedAssemblyPath);
} catch (Exception e)
{
_logger.LogError(e, "Failed to load shared assembly from {Path}", sharedAssemblyPath);
LoadSharedLibraries();
_loadedSharedLibs = true;
}
}
if (!_sharedAssemblies.TryGetValue(name.FullName, out var assembly))
{
return null;
}
return assembly;
};
foreach (var path in pluginAssemblyPaths)
{
@@ -60,7 +95,7 @@ public class PluginManager : IPluginManager
_logger.LogError(e, "Failed to load plugin from {Path}", path);
}
}
foreach (var plugin in _loadedPluginContexts)
{
plugin.Plugin.OnAllPluginsLoaded(false);
@@ -74,7 +109,8 @@ public class PluginManager : IPluginManager
public void LoadPlugin(string path)
{
var plugin = new PluginContext(_serviceProvider, _commandManager, _scriptHostConfiguration, path, _loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
var plugin = new PluginContext(_serviceProvider, _commandManager, _scriptHostConfiguration, path,
_loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
_loadedPluginContexts.Add(plugin);
plugin.Load();
}

View File

@@ -18,6 +18,7 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Core.Capabilities;
using CounterStrikeSharp.API.Core.Commands;
using CounterStrikeSharp.API.Core.Hosting;
using CounterStrikeSharp.API.Core.Logging;
@@ -76,7 +77,7 @@ namespace CounterStrikeSharp.API.Core.Plugin
config.IsUnloadable = true;
config.PreferSharedTypes = true;
});
if (CoreConfig.PluginHotReloadEnabled)
{
_fileWatcher = new FileSystemWatcher
@@ -119,12 +120,12 @@ namespace CounterStrikeSharp.API.Core.Plugin
public void Load(bool hotReload = false)
{
if (State == PluginState.Loaded) return;
using (Loader.EnterContextualReflection())
{
var defaultAssembly = Loader.LoadDefaultAssembly();
Type pluginType = defaultAssembly.GetTypes()
Type pluginType = defaultAssembly.GetExportedTypes()
.FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
if (pluginType == null) throw new Exception("Unable to find plugin in assembly");

View File

@@ -132,7 +132,6 @@ namespace CounterStrikeSharp.API.Modules.Admin
foreach (var adminDef in adminsFromFile.Values)
{
adminDef.InitalizeFlags();
Console.WriteLine($"Domains: {adminDef.Flags.Count}");
if (SteamID.TryParse(adminDef.Identity, out var steamId))
{

View File

@@ -4,7 +4,7 @@ rm -rf temp/ generated/
mkdir -p temp generated
mkdir -p generated
cp ../../libraries/GameTracking-CS2/Protobufs/*.proto temp/
cp ../../libraries/Protobufs/csgo/*.proto temp/
cp -r google/ temp/
for file in temp/*.proto; do

View File

@@ -23,7 +23,7 @@ namespace CodeGen.Natives
static void Main(string[] args)
{
Generators.GenerateNatives();
Generators.GenerateGameEvents();
Generators.GenerateGameEvents().GetAwaiter().GetResult();
}
}
}

View File

@@ -30,16 +30,25 @@ public partial class Generators
public string? Comment { get; set; }
}
private static List<GameEvent> GetGameEvents()
private static HttpClient _httpClient = new HttpClient();
private static string BaseUrl = "https://raw.githubusercontent.com/SteamDatabase/GameTracking-CS2/master/";
private static List<string> GameEventFiles = new List<string>()
{
"game/core/pak01_dir/resource/core.gameevents",
"game/csgo/pak01_dir/resource/game.gameevents",
"game/csgo/pak01_dir/resource/mod.gameevents"
};
private static async Task<List<GameEvent>> GetGameEvents()
{
// temporary, not committing resource files directly to git for now
var pathToSearch = @"/home/michael/Steam/cs2-ds/game/csgo/events/resource";
if (!Directory.Exists(pathToSearch)) Environment.Exit(0);
var allGameEvents = new Dictionary<string, GameEvent>();
foreach (string file in Directory.EnumerateFiles(pathToSearch, "*.gameevents", SearchOption.AllDirectories).OrderBy(Path.GetFileName))
foreach (string url in GameEventFiles)
// foreach (string file in Directory.EnumerateFiles(pathToSearch, "*.gameevents", SearchOption.AllDirectories).OrderBy(Path.GetFileName))
{
var deserialized = VdfConvert.Deserialize(File.ReadAllText(file));
var file = await _httpClient.GetStringAsync($"{BaseUrl}/{url}");
var deserialized = VdfConvert.Deserialize(file);
var properties =
deserialized.Value.Where(x => x.Type == VTokenType.Property);
@@ -79,12 +88,9 @@ public partial class Generators
return allGameEvents.Values.ToList();
}
public static void GenerateGameEvents()
public static async Task GenerateGameEvents()
{
var pathToSearch = @"/home/michael/Steam/cs2-ds/game/csgo/events/resource";
if (!Directory.Exists(pathToSearch)) return;
var allGameEvents = GetGameEvents();
var allGameEvents = await GetGameEvents();
var gameEventsString = string.Join("\n", allGameEvents.OrderBy(x => x.NamePascalCase).Select(gameEvent =>
{