mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-08 00:46:34 -08:00
Compare commits
5 Commits
v1.0.344
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
851a317db5 | ||
|
|
55542dba7c | ||
|
|
33538eca60 | ||
|
|
b4e83dfb4a | ||
|
|
4ff2732d8a |
@@ -1,3 +1,7 @@
|
||||
## What's Changed in v1.0.345
|
||||
* fix: update linux signature for GetCSWeaponDataFromKey ([b4e83df](https://github.com/roflmuffin/CounterStrikeSharp/commit/b4e83dfb4a1a1723c08ac79ea68da4ab8a0255fd))
|
||||
* feat(schema): update schema generator to use @GAMMACASE schema dumper format ([4ff2732](https://github.com/roflmuffin/CounterStrikeSharp/commit/4ff2732d8a55297c18cea6181b9022f56cd8fae3))
|
||||
|
||||
## What's Changed in v1.0.344
|
||||
* chore(schema): update schema to latest ([f505405](https://github.com/roflmuffin/CounterStrikeSharp/commit/f50540583d079a6cf546ca590147905ba5eb2c83))
|
||||
* fix(schema): allow for negative enum values in source schema file ([97957f6](https://github.com/roflmuffin/CounterStrikeSharp/commit/97957f62208fa782f89e9629ddde1944aaadd149))
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "48 89 5C 24 ? 57 48 83 EC ? 33 FF 4C 8B CA 8B D9",
|
||||
"linux": "55 31 D2 48 89 E5 41 57 41 56 41 55 41 54 41 89 FC"
|
||||
"linux": "55 31 D2 48 89 E5 41 56 41 55 41 54"
|
||||
}
|
||||
},
|
||||
"CCSPlayer_ItemServices_GiveNamedItem": {
|
||||
|
||||
@@ -187,7 +187,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
// If our argument doesn't end in ".dll" - try and construct a path similar to PluginName/PluginName.dll.
|
||||
// We'll assume we have a full path if we have ".dll".
|
||||
var path = info.GetArg(2);
|
||||
path = Path.Combine(_scriptHostConfiguration.RootPath, !path.EndsWith(".dll") ? $"plugins/{path}/{path}.dll" : path);
|
||||
|
||||
path = Path.Combine(
|
||||
_scriptHostConfiguration.RootPath,
|
||||
!path.EndsWith(".dll")
|
||||
? $"plugins/{path}/{Path.GetFileName(path)}.dll"
|
||||
: path
|
||||
);
|
||||
|
||||
var plugin = _pluginContextQueryHandler.FindPluginByModulePath(path);
|
||||
|
||||
|
||||
@@ -1,218 +1,251 @@
|
||||
using System.Collections.Concurrent;
|
||||
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;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
|
||||
public class PluginManager : IPluginManager
|
||||
{
|
||||
private readonly HashSet<PluginContext> _loadedPluginContexts = new();
|
||||
private readonly IScriptHostConfiguration _scriptHostConfiguration;
|
||||
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)
|
||||
{
|
||||
_scriptHostConfiguration = scriptHostConfiguration;
|
||||
_commandManager = commandManager;
|
||||
_logger = logger;
|
||||
_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();
|
||||
|
||||
if (CoreConfig.PluginResolveNugetPackages)
|
||||
{
|
||||
foreach (var assemblyName in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
if (TryLoadDependency(path, assembly.GetName().Name, assemblyName, out var dependency))
|
||||
{
|
||||
_sharedAssemblies.TryAdd(dependency.GetName().Name, dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sharedAssemblies[assembly.GetName().Name] = 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);
|
||||
var pluginAssemblyPaths = pluginDirectories
|
||||
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
|
||||
.Where(File.Exists)
|
||||
.ToArray();
|
||||
|
||||
AssemblyLoadContext.Default.Resolving += (context, name) =>
|
||||
{
|
||||
if (!_loadedSharedLibs)
|
||||
{
|
||||
LoadSharedLibraries();
|
||||
_loadedSharedLibs = true;
|
||||
}
|
||||
|
||||
if (!_sharedAssemblies.TryGetValue(name.Name, out var assembly))
|
||||
{
|
||||
if (CoreConfig.PluginResolveNugetPackages && TryLoadExternalLibrary(name, out assembly))
|
||||
{
|
||||
return assembly;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return assembly;
|
||||
};
|
||||
|
||||
if (CoreConfig.PluginAutoLoadEnabled)
|
||||
{
|
||||
foreach (var path in pluginAssemblyPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadPlugin(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var plugin in _loadedPluginContexts)
|
||||
{
|
||||
try
|
||||
{
|
||||
plugin.Plugin?.OnAllPluginsLoaded(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "OnAllPluginsLoaded failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryLoadExternalLibrary(AssemblyName assemblyName, out Assembly? assembly)
|
||||
{
|
||||
assembly = null;
|
||||
if (!TryResolveReflectionAssemblyPath(out var pluginName, out var pluginPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryLoadDependency(pluginPath, pluginName, assemblyName, out assembly))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryLoadDependency(string pluginAssemblyPath,
|
||||
string pluginAssemblyName,
|
||||
AssemblyName dependencyAssemblyName,
|
||||
out Assembly? assembly)
|
||||
{
|
||||
assembly = null;
|
||||
|
||||
var dependencyName = dependencyAssemblyName.Name!;
|
||||
if (string.IsNullOrEmpty(pluginAssemblyPath) || _sharedAssemblies.ContainsKey(dependencyName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var resolver = new PluginContextNuGetDependencyResolver(
|
||||
rootAssemblyName: pluginAssemblyName,
|
||||
rootAssemblyPath: Path.GetDirectoryName(pluginAssemblyPath)!,
|
||||
assemblyName: dependencyAssemblyName);
|
||||
|
||||
var dependencyPath = resolver.ResolvePath();
|
||||
if (string.IsNullOrWhiteSpace(dependencyPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(dependencyPath, configure: c =>
|
||||
{
|
||||
c.PreferSharedTypes = true;
|
||||
});
|
||||
|
||||
assembly = loader.LoadDefaultAssembly();
|
||||
_sharedAssemblies[dependencyAssemblyName.Name!] = assembly;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<PluginContext> GetLoadedPlugins()
|
||||
{
|
||||
return _loadedPluginContexts;
|
||||
}
|
||||
|
||||
public void LoadPlugin(string path)
|
||||
{
|
||||
var plugin = new PluginContext(_serviceProvider, _commandManager, _scriptHostConfiguration, path,
|
||||
_loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
|
||||
_loadedPluginContexts.Add(plugin);
|
||||
plugin.Load();
|
||||
}
|
||||
|
||||
private static bool TryResolveReflectionAssemblyPath(out string? assemblyName, out string? assemblyPath)
|
||||
{
|
||||
assemblyPath = null;
|
||||
assemblyName = null;
|
||||
|
||||
if (AssemblyLoadContext.CurrentContextualReflectionContext is var reflectionContext && reflectionContext is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var mainAssemblyPathField = reflectionContext
|
||||
.GetType()
|
||||
.GetField("_mainAssemblyPath", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (mainAssemblyPathField is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
assemblyPath = (string)mainAssemblyPathField.GetValue(reflectionContext)!;
|
||||
return !string.IsNullOrEmpty(assemblyPath);
|
||||
}
|
||||
}
|
||||
using System.Collections.Concurrent;
|
||||
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;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
|
||||
public class PluginManager : IPluginManager
|
||||
{
|
||||
private readonly HashSet<PluginContext> _loadedPluginContexts = new();
|
||||
private readonly IScriptHostConfiguration _scriptHostConfiguration;
|
||||
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)
|
||||
{
|
||||
_scriptHostConfiguration = scriptHostConfiguration;
|
||||
_commandManager = commandManager;
|
||||
_logger = logger;
|
||||
_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();
|
||||
|
||||
if (CoreConfig.PluginResolveNugetPackages)
|
||||
{
|
||||
foreach (var assemblyName in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
if (TryLoadDependency(path, assembly.GetName().Name, assemblyName, out var dependency))
|
||||
{
|
||||
_sharedAssemblies.TryAdd(dependency.GetName().Name, dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sharedAssemblies[assembly.GetName().Name] = 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 pluginAssemblyPaths = GetPluginsAssemblyPaths();
|
||||
|
||||
AssemblyLoadContext.Default.Resolving += (context, name) =>
|
||||
{
|
||||
if (!_loadedSharedLibs)
|
||||
{
|
||||
LoadSharedLibraries();
|
||||
_loadedSharedLibs = true;
|
||||
}
|
||||
|
||||
if (!_sharedAssemblies.TryGetValue(name.Name, out var assembly))
|
||||
{
|
||||
if (CoreConfig.PluginResolveNugetPackages && TryLoadExternalLibrary(name, out assembly))
|
||||
{
|
||||
return assembly;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return assembly;
|
||||
};
|
||||
|
||||
if (CoreConfig.PluginAutoLoadEnabled)
|
||||
{
|
||||
foreach (var path in pluginAssemblyPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadPlugin(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var plugin in _loadedPluginContexts)
|
||||
{
|
||||
try
|
||||
{
|
||||
plugin.Plugin?.OnAllPluginsLoaded(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "OnAllPluginsLoaded failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryLoadExternalLibrary(AssemblyName assemblyName, out Assembly? assembly)
|
||||
{
|
||||
assembly = null;
|
||||
if (!TryResolveReflectionAssemblyPath(out var pluginName, out var pluginPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryLoadDependency(pluginPath, pluginName, assemblyName, out assembly))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryLoadDependency(string pluginAssemblyPath,
|
||||
string pluginAssemblyName,
|
||||
AssemblyName dependencyAssemblyName,
|
||||
out Assembly? assembly)
|
||||
{
|
||||
assembly = null;
|
||||
|
||||
var dependencyName = dependencyAssemblyName.Name!;
|
||||
if (string.IsNullOrEmpty(pluginAssemblyPath) || _sharedAssemblies.ContainsKey(dependencyName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var resolver = new PluginContextNuGetDependencyResolver(
|
||||
rootAssemblyName: pluginAssemblyName,
|
||||
rootAssemblyPath: Path.GetDirectoryName(pluginAssemblyPath)!,
|
||||
assemblyName: dependencyAssemblyName);
|
||||
|
||||
var dependencyPath = resolver.ResolvePath();
|
||||
if (string.IsNullOrWhiteSpace(dependencyPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(dependencyPath, configure: c =>
|
||||
{
|
||||
c.PreferSharedTypes = true;
|
||||
});
|
||||
|
||||
assembly = loader.LoadDefaultAssembly();
|
||||
_sharedAssemblies[dependencyAssemblyName.Name!] = assembly;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<PluginContext> GetLoadedPlugins()
|
||||
{
|
||||
return _loadedPluginContexts;
|
||||
}
|
||||
|
||||
public void LoadPlugin(string path)
|
||||
{
|
||||
var plugin = new PluginContext(_serviceProvider, _commandManager, _scriptHostConfiguration, path,
|
||||
_loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
|
||||
_loadedPluginContexts.Add(plugin);
|
||||
plugin.Load();
|
||||
}
|
||||
|
||||
private static bool TryResolveReflectionAssemblyPath(out string? assemblyName, out string? assemblyPath)
|
||||
{
|
||||
assemblyPath = null;
|
||||
assemblyName = null;
|
||||
|
||||
if (AssemblyLoadContext.CurrentContextualReflectionContext is var reflectionContext && reflectionContext is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var mainAssemblyPathField = reflectionContext
|
||||
.GetType()
|
||||
.GetField("_mainAssemblyPath", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (mainAssemblyPathField is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
assemblyPath = (string)mainAssemblyPathField.GetValue(reflectionContext)!;
|
||||
return !string.IsNullOrEmpty(assemblyPath);
|
||||
}
|
||||
|
||||
private string[] GetPluginsAssemblyPaths()
|
||||
{
|
||||
// Skip "disabled" at root level
|
||||
var rootSubDirs = Directory.GetDirectories(_scriptHostConfiguration.PluginPath)
|
||||
.Where(d => !Path.GetFileName(d).Equals("disabled", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var pluginDirectories = new List<string>();
|
||||
|
||||
foreach (var subDir in rootSubDirs)
|
||||
{
|
||||
var stack = new Stack<string>();
|
||||
stack.Push(subDir);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var currentDir = stack.Pop();
|
||||
var dirName = Path.GetFileName(currentDir);
|
||||
var expectedDll = Path.Combine(currentDir, dirName + ".dll");
|
||||
|
||||
if (File.Exists(expectedDll))
|
||||
{
|
||||
pluginDirectories.Add(currentDir);
|
||||
// Stop scanning deeper in this directory
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add subdirectories to stack for further scanning
|
||||
foreach (var child in Directory.GetDirectories(currentDir))
|
||||
stack.Push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return pluginDirectories
|
||||
.Select(d => Path.Combine(d, Path.GetFileName(d) + ".dll"))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public enum PulseTestEnumColor_t : uint
|
||||
{
|
||||
BLACK = 0x0,
|
||||
WHITE = 0x1,
|
||||
RED = 0x2,
|
||||
GREEN = 0x3,
|
||||
BLUE = 0x4,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public enum PulseTestEnumShape_t : uint
|
||||
{
|
||||
CIRCLE = 0x64,
|
||||
SQUARE = 0xC8,
|
||||
TRIANGLE = 0x12C,
|
||||
}
|
||||
74
managed/CounterStrikeSharp.SchemaGen/NewSchemaModule.cs
Normal file
74
managed/CounterStrikeSharp.SchemaGen/NewSchemaModule.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
namespace CounterStrikeSharp.SchemaGen;
|
||||
|
||||
public record NewSchemaModule(
|
||||
GameInfo game_info,
|
||||
DumperInfo dumper_info,
|
||||
string[] dump_flags,
|
||||
SchemaDef[] defs);
|
||||
|
||||
public record GameInfo(
|
||||
string ClientVersion,
|
||||
string ServerVersion,
|
||||
string PatchVersion,
|
||||
string ProductName,
|
||||
string appID,
|
||||
string ServerAppID,
|
||||
string SourceRevision,
|
||||
string VersionDate,
|
||||
string VersionTime);
|
||||
|
||||
public record DumperInfo(
|
||||
string version,
|
||||
string dump_date,
|
||||
int dump_format_version);
|
||||
|
||||
public record SchemaDef(
|
||||
string type,
|
||||
string name,
|
||||
string? scope,
|
||||
string? project,
|
||||
int? size,
|
||||
int? alignment,
|
||||
SchemaTraits? traits);
|
||||
|
||||
public record SchemaTraits(
|
||||
int? parent_class_idx,
|
||||
string[]? flags,
|
||||
SchemaMetaTag[]? metatags,
|
||||
int? multi_depth,
|
||||
int? single_depth,
|
||||
SchemaBaseClass[]? baseclasses,
|
||||
SchemaMember[]? members,
|
||||
SchemaEnumField[]? fields);
|
||||
|
||||
public record SchemaMetaTag(
|
||||
string name,
|
||||
string? value);
|
||||
|
||||
public record SchemaBaseClass(
|
||||
int offset,
|
||||
int ref_idx);
|
||||
|
||||
public record SchemaMember(
|
||||
string name,
|
||||
int offset,
|
||||
MemberTraits? traits);
|
||||
|
||||
public record MemberTraits(
|
||||
SchemaMetaTag[]? metatags,
|
||||
SchemaSubtype? subtype);
|
||||
|
||||
public record SchemaSubtype(
|
||||
string type,
|
||||
string? name,
|
||||
int? size,
|
||||
int? alignment,
|
||||
SchemaSubtype[]? template,
|
||||
int? ref_idx,
|
||||
int? element_size,
|
||||
int? count,
|
||||
SchemaSubtype? subtype);
|
||||
|
||||
public record SchemaEnumField(
|
||||
string name,
|
||||
long value);
|
||||
@@ -44,7 +44,142 @@ internal static partial class Program
|
||||
"Unknown"
|
||||
};
|
||||
|
||||
public static string SanitiseTypeName(string typeName) => typeName.Replace(":", "");
|
||||
public static string SanitiseTypeName(string typeName) =>
|
||||
typeName.Replace(":", "")
|
||||
.Replace("< ", "<")
|
||||
.Replace(" >", ">");
|
||||
|
||||
private static (Dictionary<string, SchemaEnum>, Dictionary<string, SchemaClass>) ConvertNewSchemaToOld(NewSchemaModule newSchema)
|
||||
{
|
||||
var enums = new Dictionary<string, SchemaEnum>();
|
||||
var classes = new Dictionary<string, SchemaClass>();
|
||||
|
||||
var defLookup = newSchema.defs.Select((def, idx) => new { def, idx }).ToDictionary(x => x.idx, x => x.def);
|
||||
|
||||
for (int i = 0; i < newSchema.defs.Length; i++)
|
||||
{
|
||||
var def = newSchema.defs[i];
|
||||
if (def.type == "enum" && def.traits?.fields != null)
|
||||
{
|
||||
var enumItems = def.traits.fields.Select(f => new SchemaEnumItem(f.name, f.value)).ToList();
|
||||
enums[def.name] = new SchemaEnum(def.alignment ?? 4, enumItems);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < newSchema.defs.Length; i++)
|
||||
{
|
||||
var def = newSchema.defs[i];
|
||||
if (def.type == "class" && def.traits != null)
|
||||
{
|
||||
string? parentName = null;
|
||||
|
||||
if (def.traits.baseclasses != null && def.traits.baseclasses.Length > 0)
|
||||
{
|
||||
var parentIdx = def.traits.baseclasses[0].ref_idx;
|
||||
if (defLookup.TryGetValue(parentIdx, out var parentDef))
|
||||
{
|
||||
parentName = parentDef.name;
|
||||
}
|
||||
}
|
||||
|
||||
var fields = new List<SchemaField>();
|
||||
|
||||
if (def.traits.members != null)
|
||||
{
|
||||
foreach (var member in def.traits.members)
|
||||
{
|
||||
if (member.traits?.subtype != null)
|
||||
{
|
||||
var fieldType = ConvertSubtypeToFieldType(member.traits.subtype, defLookup);
|
||||
var metadata = member.traits.metatags?.ToDictionary(m => m.name, m => m.value ?? "") ??
|
||||
new Dictionary<string, string>();
|
||||
fields.Add(new SchemaField(member.name, fieldType, metadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classes[def.name] = new SchemaClass(i, def.name, parentName, fields);
|
||||
}
|
||||
}
|
||||
|
||||
return (enums, classes);
|
||||
}
|
||||
|
||||
private static SchemaFieldType ConvertSubtypeToFieldType(SchemaSubtype subtype, Dictionary<int, SchemaDef> defLookup)
|
||||
{
|
||||
if (subtype.type == "ref" && subtype.ref_idx.HasValue)
|
||||
{
|
||||
if (defLookup.TryGetValue(subtype.ref_idx.Value, out var referencedDef))
|
||||
{
|
||||
return ConvertSubtypeToFieldType(new SchemaSubtype(
|
||||
referencedDef.type == "class"
|
||||
? "declared_class"
|
||||
: (referencedDef.type == "enum" ? "declared_enum" : referencedDef.type),
|
||||
referencedDef.name,
|
||||
referencedDef.size,
|
||||
referencedDef.alignment,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
), defLookup);
|
||||
}
|
||||
}
|
||||
|
||||
SchemaTypeCategory category = subtype.type switch
|
||||
{
|
||||
"builtin" => SchemaTypeCategory.Builtin,
|
||||
"atomic" => SchemaTypeCategory.Atomic,
|
||||
"ptr" => SchemaTypeCategory.Ptr,
|
||||
"fixed_array" => SchemaTypeCategory.FixedArray,
|
||||
"declared_class" => SchemaTypeCategory.DeclaredClass,
|
||||
"declared_enum" => SchemaTypeCategory.DeclaredEnum,
|
||||
"bitfield" => SchemaTypeCategory.Bitfield,
|
||||
_ => SchemaTypeCategory.None
|
||||
};
|
||||
|
||||
SchemaAtomicCategory? atomic = null;
|
||||
if (subtype.type == "atomic" && subtype.name != null)
|
||||
{
|
||||
if (subtype.name.Contains("CUtlVector") || subtype.name.Contains("CNetworkUtlVectorBase"))
|
||||
{
|
||||
atomic = SchemaAtomicCategory.Collection;
|
||||
}
|
||||
else if (subtype.name.Contains("CHandle") || subtype.name.Contains("CWeakHandle"))
|
||||
{
|
||||
atomic = SchemaAtomicCategory.T;
|
||||
}
|
||||
else
|
||||
{
|
||||
atomic = SchemaAtomicCategory.Basic;
|
||||
}
|
||||
}
|
||||
|
||||
SchemaFieldType? innerType = null;
|
||||
|
||||
if (subtype.template != null && subtype.template.Length > 0)
|
||||
{
|
||||
innerType = ConvertSubtypeToFieldType(subtype.template[0], defLookup);
|
||||
}
|
||||
else if (subtype.subtype != null)
|
||||
{
|
||||
innerType = ConvertSubtypeToFieldType(subtype.subtype, defLookup);
|
||||
}
|
||||
|
||||
string typeName = subtype.name ?? "unknown";
|
||||
if (category == SchemaTypeCategory.FixedArray && subtype.count.HasValue && innerType != null)
|
||||
{
|
||||
typeName = $"{innerType.Name}[{subtype.count.Value}]";
|
||||
}
|
||||
|
||||
return new SchemaFieldType(
|
||||
typeName,
|
||||
category,
|
||||
atomic,
|
||||
innerType
|
||||
);
|
||||
}
|
||||
|
||||
private static StringBuilder GetTemplate(bool includeUsings)
|
||||
{
|
||||
@@ -89,16 +224,18 @@ internal static partial class Program
|
||||
{
|
||||
var schemaPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Schema", schemaFile);
|
||||
|
||||
var schema = JsonSerializer.Deserialize<SchemaModule>(
|
||||
var newSchema = JsonSerializer.Deserialize<NewSchemaModule>(
|
||||
File.ReadAllText(schemaPath),
|
||||
SerializerOptions)!;
|
||||
|
||||
foreach (var (enumName, schemaEnum) in schema.Enums)
|
||||
var (enums, classes) = ConvertNewSchemaToOld(newSchema);
|
||||
|
||||
foreach (var (enumName, schemaEnum) in enums)
|
||||
{
|
||||
allEnums[enumName] = schemaEnum;
|
||||
}
|
||||
|
||||
foreach (var (className, schemaClass) in schema.Classes)
|
||||
foreach (var (className, schemaClass) in classes)
|
||||
{
|
||||
if (IgnoreClasses.Contains(className))
|
||||
continue;
|
||||
@@ -209,7 +346,6 @@ internal static partial class Program
|
||||
visited.Add("DecalGroupOption_t");
|
||||
visited.Add("DestructibleHitGroupToDestroy_t");
|
||||
|
||||
|
||||
var classBuilder = GetTemplate(true);
|
||||
|
||||
var visitedClassNames = new HashSet<string>();
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user