diff --git a/managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs b/managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs index bc19dbb98..06a7ea74d 100644 --- a/managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs +++ b/managed/src/SwiftlyS2.Core/Modules/Plugins/PluginManager.cs @@ -1,467 +1,465 @@ -using System.Reflection; -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; -using SwiftlyS2.Shared.Plugins; - - -namespace SwiftlyS2.Core.Plugins; - -internal class PluginManager -{ - private IServiceProvider _Provider { get; init; } - private RootDirService _RootDirService { get; init; } - private ILogger _Logger { get; init; } - private List _Plugins { get; } = new(); - private FileSystemWatcher? _Watcher { get; set; } - private InterfaceManager _InterfaceManager { get; set; } = new(); - private List _SharedTypes { get; set; } = new(); - private DataDirectoryService _DataDirectoryService { get; init; } - private DateTime lastRead = DateTime.MinValue; - private readonly HashSet reloadingPlugins = new(); - - public PluginManager( - IServiceProvider provider, - ILogger logger, - RootDirService rootDirService, - DataDirectoryService dataDirectoryService - ) - { - _Provider = provider; - _RootDirService = rootDirService; - _Logger = logger; - _DataDirectoryService = dataDirectoryService; - _Watcher = new FileSystemWatcher - { - Path = rootDirService.GetPluginsRoot(), - Filter = "*.dll", - IncludeSubdirectories = true, - NotifyFilter = NotifyFilters.LastWrite, - }; - - _Watcher.Changed += HandlePluginChange; - - _Watcher.EnableRaisingEvents = true; - - Initialize(); - } - - public void Initialize() - { - AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => - { - var loadingAssemblyName = new AssemblyName(e.Name).Name ?? ""; - if (loadingAssemblyName == "") - { - return null; - } - if (loadingAssemblyName == "SwiftlyS2.CS2") - { - return Assembly.GetExecutingAssembly(); - } - - var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies() - .FirstOrDefault(a => loadingAssemblyName == a.GetName().Name); - - if (loadedAssembly != null) - { - return loadedAssembly; - } - return null; - }; - LoadExports(); - LoadPlugins(); - } - - public void HandlePluginChange(object sender, FileSystemEventArgs e) - { - try - { - 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; - } - - var directory = Path.GetDirectoryName(e.FullPath); - if (directory == null) - { - return; - } - - foreach (var plugin in _Plugins) - { - 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; - - // 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; - } - } - } - catch (Exception ex) - { - _Logger.LogError(ex, "Error handling plugin change"); - } - } - - private void PopulateSharedManually(string startDirectory) - { - var pluginDirs = Directory.GetDirectories(startDirectory); - - foreach (var pluginDir in pluginDirs) - { - var dirName = Path.GetFileName(pluginDir); - if (dirName.StartsWith("[") && dirName.EndsWith("]")) PopulateSharedManually(pluginDir); - else - { - if (Directory.Exists(Path.Combine(pluginDir, "resources", "exports"))) - { - var exportFiles = Directory.GetFiles(Path.Combine(pluginDir, "resources", "exports"), "*.dll"); - foreach (var exportFile in exportFiles) - { - try - { - var assembly = Assembly.LoadFrom(exportFile); - var exports = assembly.GetTypes(); - foreach (var export in exports) - { - _SharedTypes.Add(export); - } - } - catch (Exception innerEx) - { - _Logger.LogWarning(innerEx, $"Failed to load export assembly: {exportFile}"); - } - } - } - } - } - } - - private void LoadExports() - { - var resolver = new DependencyResolver(_Logger); - - try - { - resolver.AnalyzeDependencies(_RootDirService.GetPluginsRoot()); - - _Logger.LogInformation(resolver.GetDependencyGraphVisualization()); - - var loadOrder = resolver.GetLoadOrder(); - - _Logger.LogInformation($"Loading {loadOrder.Count} export assemblies in dependency order."); - - foreach (var exportFile in loadOrder) - { - try - { - var assembly = Assembly.LoadFrom(exportFile); - var exports = assembly.GetTypes(); - - _Logger.LogDebug($"Loaded {exports.Length} types from {Path.GetFileName(exportFile)}."); - - - foreach (var export in exports) - { - _SharedTypes.Add(export); - } - } - catch (Exception ex) - { - _Logger.LogWarning(ex, $"Failed to load export assembly: {exportFile}"); - } - } - - _Logger.LogInformation($"Successfully loaded {_SharedTypes.Count} shared types."); - } - catch (InvalidOperationException ex) when (ex.Message.Contains("Circular dependency")) - { - _Logger.LogError(ex, "Circular dependency detected in plugin exports. Loading exports without dependency resolution."); - PopulateSharedManually(_RootDirService.GetPluginsRoot()); - } - catch (Exception ex) - { - _Logger.LogError(ex, "Unexpected error during export loading"); - } - } - - private void LoadPluginsFromFolder(string directory) - { - var pluginDirs = Directory.GetDirectories(directory); - - foreach (var pluginDir in pluginDirs) - { - var dirName = Path.GetFileName(pluginDir); - if (dirName.StartsWith("[") && dirName.EndsWith("]")) LoadPluginsFromFolder(pluginDir); - else - { - try - { - var context = LoadPlugin(pluginDir, false); - if (context != null && context.Status == PluginStatus.Loaded) - { - _Logger.LogInformation("Loaded plugin " + context.Metadata!.Id); - } - } - catch (Exception e) - { - _Logger.LogWarning(e, "Error loading plugin: " + pluginDir); - continue; - } - } - } - } - - private void LoadPlugins() - { - LoadPluginsFromFolder(_RootDirService.GetPluginsRoot()); - - RebuildSharedServices(); - } - - public List GetPlugins() - { - return _Plugins; - } - - private void RebuildSharedServices() - { - - _InterfaceManager.Dispose(); - - _Plugins - .Where(p => p.Status == PluginStatus.Loaded) - .ToList() - .ForEach(p => p.Plugin!.ConfigureSharedInterface(_InterfaceManager)); - - - _InterfaceManager.Build(); - - _Plugins - .Where(p => p.Status == PluginStatus.Loaded) - .ToList() - .ForEach(p => p.Plugin!.UseSharedInterface(_InterfaceManager)); - } - - - public PluginContext? LoadPlugin(string dir, bool hotReload) - { - - - PluginContext context = new() - { - PluginDirectory = dir, - Status = PluginStatus.Loading, - }; - _Plugins.Add(context); - - var entrypointDll = Path.Combine(dir, Path.GetFileName(dir) + ".dll"); - - if (!File.Exists(entrypointDll)) - { - _Logger.LogWarning("Plugin entrypoint DLL not found: " + entrypointDll); - context.Status = PluginStatus.Error; - return null; - } - - var loader = PluginLoader.CreateFromAssemblyFile( - assemblyFile: entrypointDll, - sharedTypes: [typeof(BasePlugin), .. _SharedTypes], - config => - { - config.IsUnloadable = true; - config.LoadInMemory = true; - } - ); - - var assembly = loader.LoadDefaultAssembly(); - - var pluginType = assembly.GetTypes().FirstOrDefault(t => t.IsSubclassOf(typeof(BasePlugin)))!; - - if (pluginType == null) - { - _Logger.LogWarning("Plugin type not found: " + entrypointDll); - context.Status = PluginStatus.Error; - return null; - } - - var metadata = pluginType.GetCustomAttribute(); - if (metadata == null) - { - _Logger.LogWarning("Plugin metadata not found: " + entrypointDll); - context.Status = PluginStatus.Error; - return null; - } - - context.Metadata = metadata; - - _DataDirectoryService.EnsurePluginDataDirectory(metadata.Id); - - var core = new SwiftlyCore(metadata.Id, Path.GetDirectoryName(entrypointDll)!, metadata, pluginType, _Provider, _DataDirectoryService.GetPluginDataDirectory(metadata.Id)); - - core.InitializeType(pluginType); - - var plugin = (BasePlugin)Activator.CreateInstance(pluginType, [core])!; - - core.InitializeObject(plugin); - - - try - { - plugin.Load(hotReload); - } - catch (Exception e) - { - _Logger.LogWarning(e, $"Error loading plugin {entrypointDll}"); - context.Status = PluginStatus.Error; - return null; - } - ; - - context.Status = PluginStatus.Loaded; - context.Core = core; - context.Plugin = plugin; - context.Loader = loader; - - return context; - } - - public bool UnloadPlugin(string id) - { - var context = _Plugins - .Where(p => p.Status == PluginStatus.Loaded) - .FirstOrDefault(p => p.Metadata?.Id == id); - if (context == null) - { - _Logger.LogWarning("Plugin not found or not loaded: " + id); - return false; - } - - context.Dispose(); - context.Status = PluginStatus.Unloaded; - return true; - } - - public bool LoadPluginById(string id) - { - var context = _Plugins - .Where(p => p.Status == PluginStatus.Unloaded) - .FirstOrDefault(p => p.Metadata?.Id == id); - if (context == null) - { - // try to find new plugins - var root = _RootDirService.GetPluginsRoot(); - var pluginDirs = Directory.GetDirectories(root); - foreach (var pluginDir in pluginDirs) - { - if (Path.GetFileName(pluginDir) == id) - { - context = LoadPlugin(pluginDir, false); - break; - } - } - if (context == null) - { - _Logger.LogWarning("Plugin not found: " + id); - return false; - } - } - else - { - var directory = context.PluginDirectory!; - _Plugins.Remove(context); - LoadPlugin(directory, true); - } - - RebuildSharedServices(); - return true; - } - - public void ReloadPlugin(string id) - { - _Logger.LogInformation("Reloading plugin " + id); - - if (!UnloadPlugin(id)) - { - return; - } - - if (!LoadPluginById(id)) - { - RebuildSharedServices(); - } - - _Logger.LogInformation("Reloaded plugin " + id); - } +using System.Reflection; +using System.Runtime.Loader; +using McMaster.NETCore.Plugins; +using Microsoft.Extensions.Logging; +using SwiftlyS2.Core.Natives; +using SwiftlyS2.Core.Modules.Plugins; +using SwiftlyS2.Core.Services; +using SwiftlyS2.Shared; +using SwiftlyS2.Shared.Plugins; + +namespace SwiftlyS2.Core.Plugins; + +internal class PluginManager +{ + private IServiceProvider _Provider { get; init; } + private RootDirService _RootDirService { get; init; } + private ILogger _Logger { get; init; } + private List _Plugins { get; } = new(); + private FileSystemWatcher? _Watcher { get; set; } + private InterfaceManager _InterfaceManager { get; set; } = new(); + private List _SharedTypes { get; set; } = new(); + private DataDirectoryService _DataDirectoryService { get; init; } + private DateTime lastRead = DateTime.MinValue; + private readonly HashSet reloadingPlugins = new(); + + public PluginManager( + IServiceProvider provider, + ILogger logger, + RootDirService rootDirService, + DataDirectoryService dataDirectoryService + ) + { + _Provider = provider; + _RootDirService = rootDirService; + _Logger = logger; + _DataDirectoryService = dataDirectoryService; + _Watcher = new FileSystemWatcher { + Path = rootDirService.GetPluginsRoot(), + Filter = "*.dll", + IncludeSubdirectories = true, + NotifyFilter = NotifyFilters.LastWrite, + }; + + _Watcher.Changed += HandlePluginChange; + + _Watcher.EnableRaisingEvents = true; + + Initialize(); + } + + public void Initialize() + { + AppDomain.CurrentDomain.AssemblyResolve += ( sender, e ) => + { + var loadingAssemblyName = new AssemblyName(e.Name).Name ?? ""; + if (string.IsNullOrWhiteSpace(loadingAssemblyName)) + { + return null; + } + + if (loadingAssemblyName == "SwiftlyS2.CS2") + { + return Assembly.GetExecutingAssembly(); + } + + var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies() + .FirstOrDefault(a => loadingAssemblyName == a.GetName().Name); + + return loadedAssembly ?? null; + }; + LoadExports(); + LoadPlugins(); + } + + public void HandlePluginChange( object sender, FileSystemEventArgs e ) + { + try + { + 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; + } + + var directory = Path.GetDirectoryName(e.FullPath); + if (directory == null) + { + return; + } + + foreach (var plugin in _Plugins) + { + 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; + + // 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); + + var fileLockSuccess = false; + for (var 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; + } + } + } + catch (Exception ex) + { + _Logger.LogError(ex, "Error handling plugin change"); + } + } + + private void PopulateSharedManually( string startDirectory ) + { + var pluginDirs = Directory.GetDirectories(startDirectory); + + foreach (var pluginDir in pluginDirs) + { + var dirName = Path.GetFileName(pluginDir); + if (dirName.StartsWith("[") && dirName.EndsWith("]")) PopulateSharedManually(pluginDir); + else + { + if (Directory.Exists(Path.Combine(pluginDir, "resources", "exports"))) + { + var exportFiles = Directory.GetFiles(Path.Combine(pluginDir, "resources", "exports"), "*.dll"); + foreach (var exportFile in exportFiles) + { + try + { + var assembly = Assembly.LoadFrom(exportFile); + var exports = assembly.GetTypes(); + foreach (var export in exports) + { + _SharedTypes.Add(export); + } + } + catch (Exception innerEx) + { + _Logger.LogWarning(innerEx, $"Failed to load export assembly: {exportFile}"); + } + } + } + } + } + } + + private void LoadExports() + { + var resolver = new DependencyResolver(_Logger); + + try + { + resolver.AnalyzeDependencies(_RootDirService.GetPluginsRoot()); + + _Logger.LogInformation(resolver.GetDependencyGraphVisualization()); + + var loadOrder = resolver.GetLoadOrder(); + + _Logger.LogInformation($"Loading {loadOrder.Count} export assemblies in dependency order."); + + foreach (var exportFile in loadOrder) + { + try + { + var assembly = Assembly.LoadFrom(exportFile); + var exports = assembly.GetTypes(); + + _Logger.LogDebug($"Loaded {exports.Length} types from {Path.GetFileName(exportFile)}."); + + + foreach (var export in exports) + { + _SharedTypes.Add(export); + } + } + catch (Exception ex) + { + _Logger.LogWarning(ex, $"Failed to load export assembly: {exportFile}"); + } + } + + _Logger.LogInformation($"Successfully loaded {_SharedTypes.Count} shared types."); + } + catch (InvalidOperationException ex) when (ex.Message.Contains("Circular dependency")) + { + _Logger.LogError(ex, "Circular dependency detected in plugin exports. Loading exports without dependency resolution."); + PopulateSharedManually(_RootDirService.GetPluginsRoot()); + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unexpected error during export loading"); + } + } + + private void LoadPluginsFromFolder( string directory ) + { + var pluginDirs = Directory.GetDirectories(directory); + + foreach (var pluginDir in pluginDirs) + { + var dirName = Path.GetFileName(pluginDir); + if (dirName.StartsWith("[") && dirName.EndsWith("]")) LoadPluginsFromFolder(pluginDir); + else + { + try + { + var context = LoadPlugin(pluginDir, false); + if (context != null && context.Status == PluginStatus.Loaded) + { + _Logger.LogInformation("Loaded plugin " + context.Metadata!.Id); + } + } + catch (Exception e) + { + _Logger.LogWarning(e, "Error loading plugin: " + pluginDir); + continue; + } + } + } + } + + private void LoadPlugins() + { + LoadPluginsFromFolder(_RootDirService.GetPluginsRoot()); + + RebuildSharedServices(); + } + + public List GetPlugins() + { + return _Plugins; + } + + private void RebuildSharedServices() + { + _InterfaceManager.Dispose(); + + _Plugins + .Where(p => p.Status == PluginStatus.Loaded) + .ToList() + .ForEach(p => p.Plugin!.ConfigureSharedInterface(_InterfaceManager)); + + + _InterfaceManager.Build(); + + _Plugins + .Where(p => p.Status == PluginStatus.Loaded) + .ToList() + .ForEach(p => p.Plugin!.UseSharedInterface(_InterfaceManager)); + } + + + public PluginContext? LoadPlugin( string dir, bool hotReload ) + { + + + PluginContext context = new() { + PluginDirectory = dir, + Status = PluginStatus.Loading, + }; + _Plugins.Add(context); + + var entrypointDll = Path.Combine(dir, Path.GetFileName(dir) + ".dll"); + + if (!File.Exists(entrypointDll)) + { + _Logger.LogWarning("Plugin entrypoint DLL not found: " + entrypointDll); + context.Status = PluginStatus.Error; + return null; + } + + var loader = PluginLoader.CreateFromAssemblyFile( + assemblyFile: entrypointDll, + sharedTypes: [typeof(BasePlugin), .. _SharedTypes], + config => + { + config.IsUnloadable = true; + config.LoadInMemory = true; + var currentContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + if (currentContext != null) + { + config.DefaultContext = currentContext; + config.PreferSharedTypes = true; + } + } + ); + + var assembly = loader.LoadDefaultAssembly(); + + var pluginType = assembly.GetTypes().FirstOrDefault(t => t.IsSubclassOf(typeof(BasePlugin)))!; + + if (pluginType == null) + { + _Logger.LogWarning("Plugin type not found: " + entrypointDll); + context.Status = PluginStatus.Error; + return null; + } + + var metadata = pluginType.GetCustomAttribute(); + if (metadata == null) + { + _Logger.LogWarning("Plugin metadata not found: " + entrypointDll); + context.Status = PluginStatus.Error; + return null; + } + + context.Metadata = metadata; + + _DataDirectoryService.EnsurePluginDataDirectory(metadata.Id); + + var core = new SwiftlyCore(metadata.Id, Path.GetDirectoryName(entrypointDll)!, metadata, pluginType, _Provider, _DataDirectoryService.GetPluginDataDirectory(metadata.Id)); + + core.InitializeType(pluginType); + + var plugin = (BasePlugin)Activator.CreateInstance(pluginType, [core])!; + + core.InitializeObject(plugin); + + + try + { + plugin.Load(hotReload); + } + catch (Exception e) + { + _Logger.LogWarning(e, $"Error loading plugin {entrypointDll}"); + context.Status = PluginStatus.Error; + return null; + } + + context.Status = PluginStatus.Loaded; + context.Core = core; + context.Plugin = plugin; + context.Loader = loader; + + return context; + } + + public bool UnloadPlugin( string id ) + { + var context = _Plugins + .Where(p => p.Status == PluginStatus.Loaded) + .FirstOrDefault(p => p.Metadata?.Id == id); + if (context == null) + { + _Logger.LogWarning("Plugin not found or not loaded: " + id); + return false; + } + + context.Dispose(); + context.Status = PluginStatus.Unloaded; + return true; + } + + public bool LoadPluginById( string id ) + { + var context = _Plugins + .Where(p => p.Status == PluginStatus.Unloaded) + .FirstOrDefault(p => p.Metadata?.Id == id); + if (context == null) + { + // try to find new plugins + var root = _RootDirService.GetPluginsRoot(); + var pluginDirs = Directory.GetDirectories(root); + foreach (var pluginDir in pluginDirs) + { + if (Path.GetFileName(pluginDir) == id) + { + context = LoadPlugin(pluginDir, false); + break; + } + } + if (context == null) + { + _Logger.LogWarning("Plugin not found: " + id); + return false; + } + } + else + { + var directory = context.PluginDirectory!; + _ = _Plugins.Remove(context); + _ = LoadPlugin(directory, true); + } + + RebuildSharedServices(); + return true; + } + + public void ReloadPlugin( string id ) + { + _Logger.LogInformation("Reloading plugin " + id); + + if (!UnloadPlugin(id)) + { + return; + } + + if (!LoadPluginById(id)) + { + RebuildSharedServices(); + } + + _Logger.LogInformation("Reloaded plugin " + id); + } } \ No newline at end of file diff --git a/managed/src/TestPlugin/TestPlugin.cs b/managed/src/TestPlugin/TestPlugin.cs index 129da29b0..8a28201f4 100644 --- a/managed/src/TestPlugin/TestPlugin.cs +++ b/managed/src/TestPlugin/TestPlugin.cs @@ -1,837 +1,839 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System.Text.RegularExpressions; -using SwiftlyS2.Shared; -using SwiftlyS2.Shared.Commands; -using SwiftlyS2.Shared.GameEventDefinitions; -using SwiftlyS2.Shared.GameEvents; -using SwiftlyS2.Shared.NetMessages; -using SwiftlyS2.Shared.Misc; -using SwiftlyS2.Shared.Natives; -using SwiftlyS2.Shared.Plugins; -using SwiftlyS2.Shared.SchemaDefinitions; -using SwiftlyS2.Shared.ProtobufDefinitions; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using SwiftlyS2.Shared.Events; -using SwiftlyS2.Shared.Memory; -using YamlDotNet.Core.Tokens; -using Dapper; -using SwiftlyS2.Shared.Sounds; -using SwiftlyS2.Shared.EntitySystem; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Hosting; -using SwiftlyS2.Shared.Players; -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Toolchains.InProcess.NoEmit; -using BenchmarkDotNet.Toolchains.InProcess.Emit; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Loggers; -using SwiftlyS2.Shared.Menus; -using SwiftlyS2.Shared.SteamAPI; -using SwiftlyS2.Core.Menus.OptionsBase; -using System.Collections.Concurrent; -using Dia2Lib; - -namespace TestPlugin; - -public class TestConfig -{ - public string Name { get; set; } - public int Age { get; set; } -} - -public class InProcessConfig : ManualConfig -{ - public InProcessConfig() - { - AddLogger(ConsoleLogger.Default); - AddJob(Job.Default - .WithToolchain(new InProcessNoEmitToolchain(true)) - .WithId("InProcess")); - } -} - -[PluginMetadata(Id = "testplugin", Version = "1.0.0")] -public class TestPlugin : BasePlugin -{ - public TestPlugin( ISwiftlyCore core ) : base(core) - { - Console.WriteLine("[TestPlugin] TestPlugin constructed successfully!"); - // Console.WriteLine($"sizeof(bool): {sizeof(bool)}"); - // Console.WriteLine($"Marshal.SizeOf: {Marshal.SizeOf()}"); - Core.Event.OnWeaponServicesCanUseHook += ( @event ) => - { - // Console.WriteLine($"WeaponServicesCanUse: {@event.Weapon.WeaponBaseVData.AttackMovespeedFactor} {@event.OriginalResult}"); - - }; - } - - [Command("be")] - public void Test2Command( ICommandContext context ) - { - BenchContext.Controller = context.Sender!.RequiredController; - BenchmarkRunner.Run(new InProcessConfig()); - } - - [GameEventHandler(HookMode.Pre)] - public HookResult OnPlayerSpawn( EventPlayerSpawn @event ) - { - if (!@event.UserIdPlayer.IsValid) - { - return HookResult.Continue; - } - var player = @event.UserIdPlayer.RequiredController; - if (player.InGameMoneyServices?.IsValid == true) - { - player.InGameMoneyServices.Account = Core.ConVar.Find("mp_maxmoney")?.Value ?? 16000; - player.InGameMoneyServices.AccountUpdated(); - } - return HookResult.Continue; - } - - public override void Load( bool hotReload ) - { - // Core.Command.HookClientCommand((playerId, commandLine) => - // { - // Console.WriteLine("TestPlugin HookClientCommand " + playerId + " " + commandLine); - // return HookResult.Continue; - // }); - - // Core.Event.OnConsoleOutput += (@event) => - // { - // Console.WriteLine($"[TestPlugin] ConsoleOutput: {@event.Message}"); - // }; - - // Core.Event.OnCommandExecuteHook += (@event) => - // { - // if (@event.HookMode == HookMode.Pre) return; - // Core.Logger.LogInformation("CommandExecute: {name} with {args}", @event.Command[0], @event.Command.ArgS); - // }; - - // Core.Event.OnEntityStartTouch += (@event) => - // { - // Console.WriteLine($"[New] EntityStartTouch: {@event.Entity.Entity?.DesignerName} -> {@event.OtherEntity.Entity?.DesignerName}"); - // }; - - // Core.Event.OnEntityTouchHook += (@event) => - // { - // switch (@event.TouchType) - // { - // case EntityTouchType.StartTouch: - // Console.WriteLine($"EntityStartTouch: {@event.Entity.Entity?.DesignerName} -> {@event.OtherEntity.Entity?.DesignerName}"); - // break; - // case EntityTouchType.Touch: - // break; - // case EntityTouchType.EndTouch: - // if (@event.Entity.Entity?.DesignerName != "player" || @event.OtherEntity.Entity?.DesignerName != "player") - // { - // return; - // } - // var player = @event.Entity.As(); - // var otherPlayer = @event.OtherEntity.As(); - // Console.WriteLine($"EntityEndTouch: {(player.Controller.Value?.PlayerName ?? string.Empty)} -> {(otherPlayer.Controller.Value?.PlayerName ?? string.Empty)}"); - // break; - // } - // }; - - Core.Engine.ExecuteCommandWithBuffer("@ping", ( buffer ) => - { - Console.WriteLine($"pong: {buffer}"); - }); - - Core.GameEvent.HookPre(@event => - { - @event.LocToken = "test"; - return HookResult.Continue; - }); - - Core.Configuration - .InitializeJsonWithModel("test.jsonc", "Main") - .Configure(( builder ) => - { - builder.AddJsonFile("test.jsonc", optional: false, reloadOnChange: true); - }); - - ServiceCollection services = new(); - - services - .AddSwiftly(Core); - - Core.Event.OnPrecacheResource += ( @event ) => - { - @event.AddItem("soundevents/mvp_anthem.vsndevts"); - }; - - Core.Event.OnConVarValueChanged += ( @event ) => - { - Console.WriteLine($"ConVar {@event.ConVarName} changed from {@event.OldValue} to {@event.NewValue} by player {@event.PlayerId}"); - }; - - - // var provider = services.BuildServiceProvider(); - - // provider.GetRequiredService(); - - - // Host.CreateDefaultBuilder() - // .ConfigureLogging((context, logging) => { - // logging.AddConsole(); - // }) - // .ConfigureAppConfiguration((context, config) => { - // config.SetBasePath(Core.Configuration.GetBasePath()); - // config.AddJsonFile("test.jsonc", optional: false, reloadOnChange: true); - // }) - // .ConfigureServices((context, services) => { - // services.AddOptionsWithValidateOnStart>() - // .Bind(context.Configuration.GetSection("Main")); - // }) - // .Build(); - - // This can be used everywhere and the value will be updated when the config is changed - // Console.WriteLine(config.CurrentValue.Age); - - - // var config = new TestConfig(); - - // throw new Exception("TestPlugin loaded"); - - // Core. - - int i = 0; - - // var token2 = Core.Scheduler.Repeat(10, () => { - // Console.WriteLine(Core.Engine.TickCount); - // Console.WriteLine("TestPlugin Timer"); - // }); - Core.Logger.LogInformation("TestPlugin loaded"); - - using var se = new SoundEvent(); - - // var func = Core.Memory.GetUnmanagedFunctionByAddress(Core.Memory.GetAddressBySignature(Library.Server, "AAAAA")!.Value); - - // func.CallOriginal(1, 2); - - // func.Call(1, 2); - - // func.AddHook((next) => { - // return (a, b) => { - // Console.WriteLine("TestPlugin Hook " + a + " " + b); - // next()(a, b); - // }; - // }); - - - // Entrypoint - - // Core.Event.OnTick += () => { - // Console.WriteLine("TestPlugin OnTick "); - // }; - - // Core.Event.OnClientConnected += (@event) => { - // Console.WriteLine("TestPlugin OnClientConnected " + @event.PlayerId); - // }; - - // Core.Event.OnClientPutInServer += (@event) => { - // Console.WriteLine("TestPlugin OnClientPutInServer " + @event.PlayerId); - // }; - - Core.Event.OnClientDisconnected += ( @event ) => - { - Console.WriteLine("TestPlugin OnClientDisconnected " + @event.PlayerId); - }; - Core.Event.OnTick += () => - { - int i = 0; - }; - - // Core.Event.OnClientProcessUsercmds += (@event) => { - // foreach(var usercmd in @event.Usercmds) { - // usercmd.Base.ButtonsPb.Buttonstate1 &= 1UL << (int)GameButtons.Ctrl; - // usercmd.Base.ButtonsPb.Buttonstate2 &= 1UL << (int)GameButtons.Ctrl; - // usercmd.Base.ButtonsPb.Buttonstate3 &= 1UL << (int)GameButtons.Ctrl; - // } - // }; - - // Core.NetMessage.HookClientMessage((msg, id) => { - // Console.WriteLine("TestPlugin OnClientMove "); - // Console.WriteLine(BitConverter.ToString(msg.Data)); - // return HookResult.Continue; - // }); - - // Core.Event.OnEntityTakeDamage += (@event) => { - // Console.WriteLine("TestPlugin OnEntityTakeDamage " + @event.Entity.Entity?.DesignerName + " " + @event.Info.HitGroupId); - // }; - - // Core.Event.OnTick += () => { - - // Console.WriteLine("TestPlugin OnTick"); - // }; - - // Core.Event.OnEntityCreated += (ev) => { - // var entity = ev.Entity; - // entity.Entity.DesignerName = "a"; - // Console.WriteLine("TestPlugin OnEntityCreated " + ev.Entity.Entity?.DesignerName); - // }; - - using CEntityKeyValues kv = new(); - - kv.SetBool("test", true); - - Console.WriteLine(kv.Get("test2")); - - CUtlStringToken token = new("hello"); - Console.WriteLine($"2"); - } - - CEntityKeyValues kv { get; set; } - CEntityInstance entity { get; set; } - - [Command("tt")] - public void TestCommand( ICommandContext context ) - { - // token2?.Cancel(); - // kv = new(); - // kv.SetString("test", "SAFE"); - - // _Core.Logger.LogInformation("!@#"); - - // _Core.Logger.LogInformation(_Core.GameData.GetSignature("CEntityInstance::AcceptInput").ToString()); - - // entity = _Core.EntitySystem.CreateEntityByDesignerName("point_worldtext"); - // entity.DispatchSpawn(kv); - // Console.WriteLine("Spawned entity with keyvalues"); - - int j = 0; - - var cvar = Core.ConVar.Find("sv_cheats")!; - Console.WriteLine(cvar); - Console.WriteLine(cvar.Value); - var cvar2 = Core.ConVar.Find("sv_autobunnyhopping")!; - Console.WriteLine(cvar2); - Console.WriteLine(cvar2.Value); - - var cvar3 = Core.ConVar.Create("sw_test_cvar", "Test cvar", "ABCDEFG"); - Console.WriteLine(cvar3); - Console.WriteLine(cvar3.Value); - - var cvar4 = Core.ConVar.Find("r_drawworld")!; - - cvar2.ReplicateToClient(0, true); - - cvar4.QueryClient(0, ( value ) => - { - Console.WriteLine("QueryCallback " + value); - }); - } - - [Command("w")] - public void TestCommand1( ICommandContext context ) - { - var ret = SteamGameServerUGC.DownloadItem(new PublishedFileId_t(3596198331), true); - Console.WriteLine(SteamGameServer.GetPublicIP().ToIPAddress()); - - - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate nint DispatchSpawnDelegate( nint pEntity, nint pKV ); - int order = 0; - - IUnmanagedFunction? _dispatchspawn; - - [Command("h1")] - public void TestCommand2( ICommandContext context ) - { - // var token = Core.Scheduler.DelayAndRepeat(500, 1000, () => - // { - - // }); - - var addres = Core.GameData.GetSignature("CBaseEntity::DispatchSpawn"); - var func = Core.Memory.GetUnmanagedFunctionByAddress(addres); - - var guid = func.AddHook(( next ) => - { - return ( pEntity, pKV ) => - { - Console.WriteLine("TestPlugin DispatchSpawn " + order++); - return next()(pEntity, pKV); - }; - }); - - _dispatchspawn.AddHook(( next ) => - { - return ( pEntity, pKV ) => - { - Console.WriteLine("TestPlugin DispatchSpawn2 " + order++); - return next()(pEntity, pKV); - }; - }); - - } - - [EventListener] - public void OnEntityCreated( IOnEntityCreatedEvent @event ) - { - // @event.Entity.Entity.DesignerName = "abc"; - Console.WriteLine("TestPlugin OnEntityCreated222 " + @event.Entity.Entity?.DesignerName); - } - - Guid _hookId = Guid.Empty; - - [Command("bad")] - public void TestCommandBad( ICommandContext context ) - { - try - { - var isValveDS = Core.EntitySystem.GetGameRules()!.IsValveDS; - } - catch (Exception e) - { - Core.Logger.LogWarning("{Exception}", e.Message); - } - - try - { - Core.EntitySystem.GetGameRules()!.IsValveDS = true; - } - catch (Exception e) - { - Core.Logger.LogWarning("{Exception}", e.Message); - } - - try - { - Core.EntitySystem.GetGameRules()!.IsValveDSUpdated(); - } - catch (Exception e) - { - Core.Logger.LogWarning("{Exception}", e.Message); - } - } - - [Command("h2")] - public void TestCommand3( ICommandContext context ) - { - var ent = Core.EntitySystem.CreateEntity(); - ent.DispatchSpawn(); - ent.Collision.MaxsUpdated(); - ent.Collision.CollisionAttribute.OwnerIdUpdated(); - } - - [Command("tt3")] - public void TestCommand33( ICommandContext context ) - { - var ent = Core.EntitySystem.CreateEntity(); - using CEntityKeyValues kv = new(); - kv.Set("m_spawnflags", 256); - ent.DispatchSpawn(kv); - ent.SetModel("weapons/models/grenade/incendiary/weapon_incendiarygrenade.vmdl"); - ent.Teleport(new Vector(context.Sender!.PlayerPawn!.AbsOrigin!.Value.X + 50, context.Sender!.PlayerPawn!.AbsOrigin!.Value.Y + 50, context.Sender!.PlayerPawn!.AbsOrigin!.Value.Z + 30), QAngle.Zero, Vector.Zero); - } - - [Command("tt4")] - public void TestCommand4( ICommandContext context ) - { - Console.WriteLine(Core.Permission.PlayerHasPermission(7656, context.Args[0])); - } - - [Command("tt5")] - public void TestCommand5( ICommandContext context ) - { - Console.WriteLine("TestPlugin TestCommand5"); - } - - [Command("tt6", permission: "tt6")] - public void TestCommand6( ICommandContext context ) - { - Console.WriteLine("TestPlugin TestCommand6"); - } - - [Command("tt7")] - public void TestCommand7( ICommandContext context ) - { - Core.Engine.ExecuteCommandWithBuffer("@ping", ( buffer ) => - { - Console.WriteLine($"pong: {buffer}"); - }); - } - - [Command("tt8")] - public unsafe void TestCommand8( ICommandContext context ) - { - Core.EntitySystem.GetAllEntitiesByDesignerName("func_buyzone").ToList().ForEach(zone => - { - if ((zone?.IsValid ?? false)) - { - zone.Despawn(); - } - }); - - var sender = context.Sender!; - var target = Core.PlayerManager.GetAllPlayers().FirstOrDefault(p => p.PlayerID != sender.PlayerID)!; - - var origin = sender.RequiredPlayerPawn.AbsOrigin ?? Vector.Zero; - var targetOrigin = target.RequiredPlayerPawn.AbsOrigin ?? Vector.Zero; - - Console.WriteLine("\n"); - Console.WriteLine($"Origin: {origin}"); - Console.WriteLine($"Target Origin: {targetOrigin}"); - - // Ray_t* ray = stackalloc Ray_t[1]; - // ray->Init(Vector.Zero, Vector.Zero); - Ray_t ray = new(); - ray.Init(Vector.Zero, Vector.Zero); - - var filter = new CTraceFilter { - // unk01 = 1, - IterateEntities = true, - QueryShapeAttributes = new RnQueryShapeAttr_t { - InteractsWith = MaskTrace.Player | MaskTrace.Solid | MaskTrace.Hitbox | MaskTrace.Npc, - InteractsExclude = MaskTrace.Empty, - InteractsAs = MaskTrace.Player, - CollisionGroup = CollisionGroup.PlayerMovement, - ObjectSetMask = RnQueryObjectSet.AllGameEntities, - HitSolid = true, - // HitTrigger = false, - // HitSolidRequiresGenerateContacts = false, - // ShouldIgnoreDisabledPairs = true, - // IgnoreIfBothInteractWithHitboxes = true, - // ForceHitEverything = true - } - }; - - // filter.QueryShapeAttributes.EntityIdsToIgnore[0] = unchecked((uint)-1); - // filter.QueryShapeAttributes.EntityIdsToIgnore[1] = unchecked((uint)-1); - // filter.QueryShapeAttributes.OwnerIdsToIgnore[0] = unchecked((uint)-1); - // filter.QueryShapeAttributes.OwnerIdsToIgnore[1] = unchecked((uint)-1); - // filter.QueryShapeAttributes.HierarchyIds[0] = 0; - // filter.QueryShapeAttributes.HierarchyIds[1] = 0; - - var trace = new CGameTrace(); - Core.Trace.TraceShape(origin, targetOrigin, ray, filter, ref trace); - - Console.WriteLine(trace.pEntity != null ? $"! Hit Entity: {trace.Entity.DesignerName}" : "! No entity hit"); - Console.WriteLine($"! SurfaceProperties: {(nint)trace.SurfaceProperties}, pEntity: {(nint)trace.pEntity}, HitBox: {(nint)trace.HitBox}({trace.HitBox->m_name.Value}), Body: {(nint)trace.Body}, Shape: {(nint)trace.Shape}, Contents: {trace.Contents}"); - Console.WriteLine($"! StartPos: {trace.StartPos}, EndPos: {trace.EndPos}, HitNormal: {trace.HitNormal}, HitPoint: {trace.HitPoint}"); - Console.WriteLine($"! HitOffset: {trace.HitOffset}, Fraction: {trace.Fraction}, Triangle: {trace.Triangle}, HitboxBoneIndex: {trace.HitboxBoneIndex}"); - Console.WriteLine($"! RayType: {trace.RayType}, StartInSolid: {trace.StartInSolid}, ExactHitPoint: {trace.ExactHitPoint}"); - Console.WriteLine("\n"); - } - - [GameEventHandler(HookMode.Pre)] - public HookResult TestGameEventHandler( EventPlayerJump @e ) - { - Console.WriteLine(@e.UserIdController.PlayerName); - return HookResult.Continue; - } - - [ServerNetMessageHandler] - public HookResult TestServerNetMessageHandler( CCSUsrMsg_SendPlayerItemDrops msg ) - { - Console.WriteLine("FIRED"); - // foreach(var item in msg.Accessor) { - // Console.WriteLine($"TestPlugin ServerNetMessageHandler: {item.EconItem.Defindex}"); - // } - return HookResult.Continue; - } - - private Callback _authTicketResponse; - - [EventListener] - public void OnSteamAPIActivated() - { - Console.WriteLine("TestPlugin OnSteamAPIActivated"); - _authTicketResponse = Callback.Create(AuthResponse); - } - - public void AuthResponse( GCMessageAvailable_t param ) - { - Console.WriteLine($"AuthResponse {param.m_nMessageSize}"); - } - - [Command("getip")] - public void GetIpCommand( ICommandContext context ) - { - context.Reply(SteamGameServer.GetPublicIP().ToString()); - } - - // [Command("i76")] - // public void TestIssue76Command( ICommandContext context ) - // { - // var player = context.Sender!; - // IMenu settingsMenu = Core.Menus.CreateMenu("Settings"); - // // Add the following code to render text properly - // //settingsMenu.Builder.AddText("123", overflowStyle: MenuHorizontalStyle.ScrollLeftLoop(25f)); - // settingsMenu.Builder.AddText("123"); - // settingsMenu.Builder.AddText("1234"); - // settingsMenu.Builder.AddText("12345"); - - // Core.Menus.OpenMenu(player, settingsMenu); - // } - - // [Command("i77")] - // public void TestIssue77Command( ICommandContext context ) - // { - // var player = context.Sender!; - // IMenu settingsMenu = Core.Menus.CreateMenu("Settings"); - - // settingsMenu.Builder.AddText("123"); - // settingsMenu.Builder.AddSubmenu("Submenu", () => - // { - // var menu = Core.Menus.CreateMenu("Submenu"); - // menu.Builder.AddText("1234"); - // return menu; - // }); - - // settingsMenu.Builder.AddSubmenu("Async Submenu", async () => - // { - // await Task.Delay(5000); - // var menu = Core.Menus.CreateMenu("Async Submenu"); - // menu.Builder.AddText("12345"); - // return menu; - // }); - - // Core.Menus.OpenMenu(player, settingsMenu); - // } - - // [Command("i78")] - // public void TestIssue78Command( ICommandContext context ) - // { - // var player = context.Sender!; - // IMenu settingsMenu = Core.Menus.CreateMenu("Settings"); - // settingsMenu.Builder.AddButton("123", ( p ) => - // { - // player.SendMessage(MessageType.Chat, "Button"); - // }); - - // settingsMenu.Builder.Design.OverrideExitButton("shift"); - // settingsMenu.Builder.Design.OverrideSelectButton("e"); - - // Core.Menus.OpenMenu(player, settingsMenu); - // } - - [Command("rmt")] - public void RefactoredMenuTestCommand( ICommandContext context ) - { - var button = new ButtonMenuOption($"{HtmlGradient.GenerateGradientText("Swiftlys2 向这广袤世界致以温柔问候", "#FFE4E1", "#FFC0CB", "#FF69B4")}") { TextStyle = MenuOptionTextStyle.ScrollLeftLoop }; - button.Click += ( sender, args ) => - { - args.Player.SendMessage(MessageType.Chat, "Swiftlys2 向这广袤世界致以温柔问候"); - // button.Visible = false; - button.Enabled = false; - // button.TextStyle = MenuOptionTextStyle.ScrollRightFade; - // button.Text = Regex.Match(button.Text, @"^(.*)#(\d+)$") is { Success: true } m - // ? $"{m.Groups[1].Value}#{int.Parse(m.Groups[2].Value) + 1}" - // : $"{button.Text}#1"; - // button.MaxWidth -= 1f; - _ = Task.Run(async () => - { - await Task.Delay(1000); - // button.Visible = true; - button.Enabled = true; - }); - return ValueTask.CompletedTask; - }; - - var player = context.Sender!; - var menu = Core.MenusAPI - .CreateBuilder() - .SetPlayerFrozen(false) - // .SetAutoCloseDelay(15f) - .Design.SetMaxVisibleItems(5) - .Design.SetMenuTitle($"{HtmlGradient.GenerateGradientText("Redesigned Menu", "#00FA9A", "#F5FFFA")}") - .Design.SetMenuTitleVisible(true) - .Design.SetMenuFooterVisible(true) - .Design.EnableAutoAdjustVisibleItems() - .Design.SetGlobalScrollStyle(MenuOptionScrollStyle.WaitingCenter) - // .AddOption(new TextMenuOption($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#AFEEEE", "#7FFFD4", "#40E0D0")}", textStyle: MenuOptionTextStyle.ScrollRightFade)) - // .AddOption(new TextMenuOption($"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", textStyle: MenuOptionTextStyle.ScrollLeftFade)) - // .AddOption(new TextMenuOption("12345678901234567890split12345678901234567890", textStyle: MenuOptionTextStyle.TruncateBothEnds)) - .AddOption(new TextMenuOption("1") { Visible = false }) - .AddOption(new ToggleMenuOption("12")) - .AddOption(new ChoiceMenuOption("123", ["Option 1", "Option 2", "Option 3"])) - .AddOption(new SliderMenuOption("1234")) - .AddOption(new ProgressBarMenuOption("12345", () => (float)new Random().NextDouble(), multiLine: false)) - .AddOption(new SubmenuMenuOption("123456", async () => - { - await Task.Delay(1000); - var menu = Core.MenusAPI.CreateBuilder() - .SetPlayerFrozen(true) - .Design.SetMenuTitle("Async Submenu") - .AddOption(new TextMenuOption("123456")) - .Build(); - return menu; - })) - .AddOption(new InputMenuOption("1234567")) - .AddOption(new TextMenuOption("12345678") { TextStyle = MenuOptionTextStyle.ScrollLeftLoop }) - .AddOption(new TextMenuOption("123456789")) - .AddOption(new TextMenuOption("1234567890") { Visible = false }) - .AddOption(button) - .AddOption(new TextMenuOption($"{HtmlGradient.GenerateGradientText("Swiftlys2 からこの広大なる世界へ温かい挨拶を", "#FFE5CC", "#FFAB91", "#FF7043")}") { TextStyle = MenuOptionTextStyle.ScrollRightLoop }) - .AddOption(new TextMenuOption($"{HtmlGradient.GenerateGradientText("Swiftlys2 가 이 넓은 세상에 따뜻한 인사를 전합니다", "#E6E6FA", "#00FFFF", "#FF1493")}") { TextStyle = MenuOptionTextStyle.ScrollLeftFade }) - .AddOption(new TextMenuOption($"{HtmlGradient.GenerateGradientText("Swiftlys2 приветствует этот прекрасный мир", "#AFEEEE", "#7FFFD4", "#40E0D0")}") { TextStyle = MenuOptionTextStyle.ScrollRightFade }) - .AddOption(new TextMenuOption("Swiftlys2 salută această lume minunată") { TextStyle = MenuOptionTextStyle.TruncateEnd }) - .AddOption(new TextMenuOption("Swiftlys2 extends warmest greetings to this wondrous world") { TextStyle = MenuOptionTextStyle.TruncateBothEnds }) - // .AddOption(new TextMenuOption("Swiftlys2 sendas korajn salutojn al ĉi tiu mirinda mondo")) - .AddOption(new TextMenuOption("1234567890") { Visible = false }) - .AddOption(new TextMenuOption("123456789")) - .AddOption(new TextMenuOption("12345678") { TextStyle = MenuOptionTextStyle.ScrollRightLoop }) - .AddOption(new InputMenuOption("1234567")) - .AddOption(new SubmenuMenuOption("123456", () => - { - var menu = Core.MenusAPI.CreateBuilder() - .Design.SetMenuTitle("Async Submenu") - .AddOption(new TextMenuOption("123456")) - .Build(); - return menu; - })) - .AddOption(new ProgressBarMenuOption("12345", () => (float)new Random().NextDouble(), multiLine: false)) - .AddOption(new SliderMenuOption("1234")) - .AddOption(new ChoiceMenuOption("123", ["Option 1", "Option 2", "Option 3"])) - .AddOption(new ToggleMenuOption("12")) - .AddOption(new TextMenuOption("1") { Visible = false }) - .Build(); - - Core.MenusAPI.OpenMenu(menu); - // Core.MenusAPI.OpenMenuForPlayer(player, menu); - } - - // [Command("mt")] - // public void MenuTestCommand( ICommandContext context ) - // { - // var player = context.Sender!; - - // IMenu settingsMenu = Core.Menus.CreateMenu("MenuTest"); - - // settingsMenu.Builder.Design.MaxVisibleItems(5); - - // // settingsMenu.Builder.Design.MaxVisibleItems(Random.Shared.Next(-2, 8)); - // if (context.Args.Length < 1 || !int.TryParse(context.Args[0], out int vtype)) vtype = 0; - // settingsMenu.Builder.Design.SetVerticalScrollStyle(vtype switch { - // 1 => MenuVerticalScrollStyle.LinearScroll, - // 2 => MenuVerticalScrollStyle.WaitingCenter, - // _ => MenuVerticalScrollStyle.CenterFixed - // }); - - // if (context.Args.Length < 2 || !int.TryParse(context.Args[1], out int htype)) htype = 0; - // settingsMenu.Builder.Design.SetGlobalHorizontalStyle(htype switch { - // 0 => MenuHorizontalStyle.Default, - // 1 => MenuHorizontalStyle.TruncateBothEnds(26f), - // 2 => MenuHorizontalStyle.ScrollLeftFade(26f, 8, 128), - // 3 => MenuHorizontalStyle.ScrollLeftLoop(26f, 8, 128), - // 1337 => MenuHorizontalStyle.TruncateEnd(0f), - // _ => MenuHorizontalStyle.TruncateEnd(26f) - // }); - - // settingsMenu.Builder.AddButton("1. AButton", ( p ) => - // { - // player.SendMessage(MessageType.Chat, "Button"); - // }); - - // settingsMenu.Builder.AddToggle("2. Toggle", defaultValue: true, ( p, value ) => - // { - // player.SendMessage(MessageType.Chat, $"AddToggle {value}"); - // }); - - // settingsMenu.Builder.AddSlider("3. Slider", min: 0, max: 100, defaultValue: 10, step: 10, ( p, value ) => - // { - // player.SendMessage(MessageType.Chat, $"AddSlider {value}"); - // }); - - // settingsMenu.Builder.AddAsyncButton("4. AsyncButton", async ( p ) => - // { - // await Task.Delay(2000); - // }); - - // settingsMenu.Builder.AddText("5. Text"); - // settingsMenu.Builder.AddText("6. Text"); - // settingsMenu.Builder.AddText("7. Text"); - // settingsMenu.Builder.AddText("8. Text"); - // settingsMenu.Builder.AddText("9. Text"); - // settingsMenu.Builder.AddSeparator(); - // settingsMenu.Builder.AddText($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#FFE4E1", "#FFC0CB", "#FF69B4")}", overflowStyle: MenuHorizontalStyle.TruncateEnd(26f)); - // settingsMenu.Builder.AddText($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#FFE5CC", "#FFAB91", "#FF7043")}", overflowStyle: MenuHorizontalStyle.TruncateBothEnds(26f)); - // settingsMenu.Builder.AddText($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#E6E6FA", "#00FFFF", "#FF1493")}", overflowStyle: MenuHorizontalStyle.ScrollRightFade(26f, 8)); - // settingsMenu.Builder.AddText($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#AFEEEE", "#7FFFD4", "#40E0D0")}", overflowStyle: MenuHorizontalStyle.ScrollLeftLoop(26f, 8)); - // settingsMenu.Builder.AddText("12345678901234567890split12345678901234567890", overflowStyle: MenuHorizontalStyle.ScrollLeftFade(26f, 8, 128)); - // settingsMenu.Builder.AddText("一二三四五六七八九十分割一二三四五六七八九十", overflowStyle: MenuHorizontalStyle.ScrollRightLoop(26f, 8, 64)); - // settingsMenu.Builder.AddSeparator(); - // settingsMenu.Builder.AddText("Swiftlys2 向这广袤世界致以温柔问候", overflowStyle: MenuHorizontalStyle.ScrollRightLoop(26f, 8)); - // settingsMenu.Builder.AddText("Swiftlys2 からこの広大なる世界へ温かい挨拶を"); - // settingsMenu.Builder.AddText("Swiftlys2 가 이 넓은 세상에 따뜻한 인사를 전합니다"); - // settingsMenu.Builder.AddText("Swiftlys2 приветствует этот прекрасный мир"); - // settingsMenu.Builder.AddText("Swiftlys2 salută această lume minunată"); - // settingsMenu.Builder.AddText("Swiftlys2 extends warmest greetings to this wondrous world"); - // settingsMenu.Builder.AddText("Swiftlys2 sendas korajn salutojn al ĉi tiu mirinda mondo"); - // settingsMenu.Builder.AddSeparator(); - // settingsMenu.Builder.AddAsyncButton("AsyncButton|AsyncButton|AsyncButton", async ( p ) => await Task.Delay(2000)); - // settingsMenu.Builder.AddButton("Button|Button|Button|Button", ( p ) => { }); - // settingsMenu.Builder.AddChoice("Choice|Choice|Choice|Choice", ["Option 1", "Option 2", "Option 3"], "Option 1", ( p, value ) => { }, overflowStyle: MenuHorizontalStyle.TruncateEnd(8f)); - // settingsMenu.Builder.AddProgressBar("ProgressBar|ProgressBar|ProgressBar", () => (float)Random.Shared.NextDouble(), overflowStyle: MenuHorizontalStyle.ScrollLeftLoop(26f, 12)); - // settingsMenu.Builder.AddSlider("Slider|Slider|Slider|Slider", 0f, 100f, 0f, 1f, ( p, value ) => { }, overflowStyle: MenuHorizontalStyle.ScrollRightLoop(8f, 12)); - // // settingsMenu.Builder.AddSubmenu("Submenu"); - // settingsMenu.Builder.AddToggle("Toggle|Toggle|Toggle|Toggle", true, ( p, value ) => { }); - // settingsMenu.Builder.AddSeparator(); - - // Core.Menus.OpenMenu(player, settingsMenu); - // } - - // [Command("menu")] - // public void MenuCommand( ICommandContext context ) - // { - // var player = context.Sender!; - // var menu = Core.Menus.CreateMenu("Test Menu"); - - // menu.Builder - // .AddButton("Button 1", ( ctx ) => - // { - // player.SendMessage(MessageType.Chat, "You clicked Button 1"); - // }) - // .AddButton("Button 2", ( ctx ) => - // { - // player.SendMessage(MessageType.Chat, "You clicked Button 2"); - // }) - // .AddButton("Button 3", ( ctx ) => - // { - // player.SendMessage(MessageType.Chat, "You clicked Button 3"); - // }) - // .AddButton("Button 4", ( ctx ) => - // { - // player.SendMessage(MessageType.Chat, "You clicked Button 4"); - // }) - // .AddButton("Button 5", ( ctx ) => - // { - // player.SendMessage(MessageType.Chat, "You clicked Button 5"); - // }) - // .AddButton("Button 6", ( ctx ) => - // { - // player.SendMessage(MessageType.Chat, "You clicked Button 6"); - // }) - // .AddButton("Button 7", ( ctx ) => - // { - // player.SendMessage(MessageType.Chat, "You clicked Button 7"); - // }) - // .AddButton("Button 8", ( ctx ) => - // { - // player.SendMessage(MessageType.Chat, "You clicked Button 8"); - // }) - // .AddSeparator() - // .AddText("hello!", size: IMenuTextSize.ExtraLarge) - // .AutoClose(15f) - // .HasSound(true) - // .ForceFreeze(); - - // menu.Builder.Design.SetColor(new(0, 186, 105, 255)); - - // Core.Menus.OpenMenu(player, menu); - // } - - public override void Unload() - { - Console.WriteLine("TestPlugin unloaded"); - } +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Tomlyn.Extensions.Configuration; +using System.Text.RegularExpressions; +using SwiftlyS2.Shared; +using SwiftlyS2.Shared.Commands; +using SwiftlyS2.Shared.GameEventDefinitions; +using SwiftlyS2.Shared.GameEvents; +using SwiftlyS2.Shared.NetMessages; +using SwiftlyS2.Shared.Misc; +using SwiftlyS2.Shared.Natives; +using SwiftlyS2.Shared.Plugins; +using SwiftlyS2.Shared.SchemaDefinitions; +using SwiftlyS2.Shared.ProtobufDefinitions; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using SwiftlyS2.Shared.Events; +using SwiftlyS2.Shared.Memory; +using YamlDotNet.Core.Tokens; +using Dapper; +using SwiftlyS2.Shared.Sounds; +using SwiftlyS2.Shared.EntitySystem; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Hosting; +using SwiftlyS2.Shared.Players; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Toolchains.InProcess.NoEmit; +using BenchmarkDotNet.Toolchains.InProcess.Emit; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using SwiftlyS2.Shared.Menus; +using SwiftlyS2.Shared.SteamAPI; +using SwiftlyS2.Core.Menus.OptionsBase; +using System.Collections.Concurrent; +using Dia2Lib; + +namespace TestPlugin; + +public class TestConfig +{ + public string Name { get; set; } + public int Age { get; set; } +} + +public class InProcessConfig : ManualConfig +{ + public InProcessConfig() + { + AddLogger(ConsoleLogger.Default); + AddJob(Job.Default + .WithToolchain(new InProcessNoEmitToolchain(true)) + .WithId("InProcess")); + } +} + +[PluginMetadata(Id = "testplugin", Version = "1.0.0")] +public class TestPlugin : BasePlugin +{ + public TestPlugin( ISwiftlyCore core ) : base(core) + { + Console.WriteLine("[TestPlugin] TestPlugin constructed successfully!"); + // Console.WriteLine($"sizeof(bool): {sizeof(bool)}"); + // Console.WriteLine($"Marshal.SizeOf: {Marshal.SizeOf()}"); + Core.Event.OnWeaponServicesCanUseHook += ( @event ) => + { + // Console.WriteLine($"WeaponServicesCanUse: {@event.Weapon.WeaponBaseVData.AttackMovespeedFactor} {@event.OriginalResult}"); + + }; + } + + [Command("be")] + public void Test2Command( ICommandContext context ) + { + BenchContext.Controller = context.Sender!.RequiredController; + BenchmarkRunner.Run(new InProcessConfig()); + } + + [GameEventHandler(HookMode.Pre)] + public HookResult OnPlayerSpawn( EventPlayerSpawn @event ) + { + if (!@event.UserIdPlayer.IsValid) + { + return HookResult.Continue; + } + var player = @event.UserIdPlayer.RequiredController; + if (player.InGameMoneyServices?.IsValid == true) + { + player.InGameMoneyServices.Account = Core.ConVar.Find("mp_maxmoney")?.Value ?? 16000; + player.InGameMoneyServices.AccountUpdated(); + } + return HookResult.Continue; + } + + public override void Load( bool hotReload ) + { + // Core.Command.HookClientCommand((playerId, commandLine) => + // { + // Console.WriteLine("TestPlugin HookClientCommand " + playerId + " " + commandLine); + // return HookResult.Continue; + // }); + + // Core.Event.OnConsoleOutput += (@event) => + // { + // Console.WriteLine($"[TestPlugin] ConsoleOutput: {@event.Message}"); + // }; + + // Core.Event.OnCommandExecuteHook += (@event) => + // { + // if (@event.HookMode == HookMode.Pre) return; + // Core.Logger.LogInformation("CommandExecute: {name} with {args}", @event.Command[0], @event.Command.ArgS); + // }; + + // Core.Event.OnEntityStartTouch += (@event) => + // { + // Console.WriteLine($"[New] EntityStartTouch: {@event.Entity.Entity?.DesignerName} -> {@event.OtherEntity.Entity?.DesignerName}"); + // }; + + // Core.Event.OnEntityTouchHook += (@event) => + // { + // switch (@event.TouchType) + // { + // case EntityTouchType.StartTouch: + // Console.WriteLine($"EntityStartTouch: {@event.Entity.Entity?.DesignerName} -> {@event.OtherEntity.Entity?.DesignerName}"); + // break; + // case EntityTouchType.Touch: + // break; + // case EntityTouchType.EndTouch: + // if (@event.Entity.Entity?.DesignerName != "player" || @event.OtherEntity.Entity?.DesignerName != "player") + // { + // return; + // } + // var player = @event.Entity.As(); + // var otherPlayer = @event.OtherEntity.As(); + // Console.WriteLine($"EntityEndTouch: {(player.Controller.Value?.PlayerName ?? string.Empty)} -> {(otherPlayer.Controller.Value?.PlayerName ?? string.Empty)}"); + // break; + // } + // }; + + Core.Engine.ExecuteCommandWithBuffer("@ping", ( buffer ) => + { + Console.WriteLine($"pong: {buffer}"); + }); + + Core.GameEvent.HookPre(@event => + { + @event.LocToken = "test"; + return HookResult.Continue; + }); + + Core.Configuration + .InitializeJsonWithModel("test.jsonc", "Main") + .Configure(( builder ) => + { + builder.AddJsonFile("test.jsonc", optional: false, reloadOnChange: true); + builder.AddTomlFile("test.toml", optional: true, reloadOnChange: true); + }); + + ServiceCollection services = new(); + + services + .AddSwiftly(Core); + + Core.Event.OnPrecacheResource += ( @event ) => + { + @event.AddItem("soundevents/mvp_anthem.vsndevts"); + }; + + Core.Event.OnConVarValueChanged += ( @event ) => + { + Console.WriteLine($"ConVar {@event.ConVarName} changed from {@event.OldValue} to {@event.NewValue} by player {@event.PlayerId}"); + }; + + + // var provider = services.BuildServiceProvider(); + + // provider.GetRequiredService(); + + + // Host.CreateDefaultBuilder() + // .ConfigureLogging((context, logging) => { + // logging.AddConsole(); + // }) + // .ConfigureAppConfiguration((context, config) => { + // config.SetBasePath(Core.Configuration.GetBasePath()); + // config.AddJsonFile("test.jsonc", optional: false, reloadOnChange: true); + // }) + // .ConfigureServices((context, services) => { + // services.AddOptionsWithValidateOnStart>() + // .Bind(context.Configuration.GetSection("Main")); + // }) + // .Build(); + + // This can be used everywhere and the value will be updated when the config is changed + // Console.WriteLine(config.CurrentValue.Age); + + + // var config = new TestConfig(); + + // throw new Exception("TestPlugin loaded"); + + // Core. + + int i = 0; + + // var token2 = Core.Scheduler.Repeat(10, () => { + // Console.WriteLine(Core.Engine.TickCount); + // Console.WriteLine("TestPlugin Timer"); + // }); + Core.Logger.LogInformation("TestPlugin loaded"); + + using var se = new SoundEvent(); + + // var func = Core.Memory.GetUnmanagedFunctionByAddress(Core.Memory.GetAddressBySignature(Library.Server, "AAAAA")!.Value); + + // func.CallOriginal(1, 2); + + // func.Call(1, 2); + + // func.AddHook((next) => { + // return (a, b) => { + // Console.WriteLine("TestPlugin Hook " + a + " " + b); + // next()(a, b); + // }; + // }); + + + // Entrypoint + + // Core.Event.OnTick += () => { + // Console.WriteLine("TestPlugin OnTick "); + // }; + + // Core.Event.OnClientConnected += (@event) => { + // Console.WriteLine("TestPlugin OnClientConnected " + @event.PlayerId); + // }; + + // Core.Event.OnClientPutInServer += (@event) => { + // Console.WriteLine("TestPlugin OnClientPutInServer " + @event.PlayerId); + // }; + + Core.Event.OnClientDisconnected += ( @event ) => + { + Console.WriteLine("TestPlugin OnClientDisconnected " + @event.PlayerId); + }; + Core.Event.OnTick += () => + { + int i = 0; + }; + + // Core.Event.OnClientProcessUsercmds += (@event) => { + // foreach(var usercmd in @event.Usercmds) { + // usercmd.Base.ButtonsPb.Buttonstate1 &= 1UL << (int)GameButtons.Ctrl; + // usercmd.Base.ButtonsPb.Buttonstate2 &= 1UL << (int)GameButtons.Ctrl; + // usercmd.Base.ButtonsPb.Buttonstate3 &= 1UL << (int)GameButtons.Ctrl; + // } + // }; + + // Core.NetMessage.HookClientMessage((msg, id) => { + // Console.WriteLine("TestPlugin OnClientMove "); + // Console.WriteLine(BitConverter.ToString(msg.Data)); + // return HookResult.Continue; + // }); + + // Core.Event.OnEntityTakeDamage += (@event) => { + // Console.WriteLine("TestPlugin OnEntityTakeDamage " + @event.Entity.Entity?.DesignerName + " " + @event.Info.HitGroupId); + // }; + + // Core.Event.OnTick += () => { + + // Console.WriteLine("TestPlugin OnTick"); + // }; + + // Core.Event.OnEntityCreated += (ev) => { + // var entity = ev.Entity; + // entity.Entity.DesignerName = "a"; + // Console.WriteLine("TestPlugin OnEntityCreated " + ev.Entity.Entity?.DesignerName); + // }; + + using CEntityKeyValues kv = new(); + + kv.SetBool("test", true); + + Console.WriteLine(kv.Get("test2")); + + CUtlStringToken token = new("hello"); + Console.WriteLine($"2"); + } + + CEntityKeyValues kv { get; set; } + CEntityInstance entity { get; set; } + + [Command("tt")] + public void TestCommand( ICommandContext context ) + { + // token2?.Cancel(); + // kv = new(); + // kv.SetString("test", "SAFE"); + + // _Core.Logger.LogInformation("!@#"); + + // _Core.Logger.LogInformation(_Core.GameData.GetSignature("CEntityInstance::AcceptInput").ToString()); + + // entity = _Core.EntitySystem.CreateEntityByDesignerName("point_worldtext"); + // entity.DispatchSpawn(kv); + // Console.WriteLine("Spawned entity with keyvalues"); + + int j = 0; + + var cvar = Core.ConVar.Find("sv_cheats")!; + Console.WriteLine(cvar); + Console.WriteLine(cvar.Value); + var cvar2 = Core.ConVar.Find("sv_autobunnyhopping")!; + Console.WriteLine(cvar2); + Console.WriteLine(cvar2.Value); + + var cvar3 = Core.ConVar.Create("sw_test_cvar", "Test cvar", "ABCDEFG"); + Console.WriteLine(cvar3); + Console.WriteLine(cvar3.Value); + + var cvar4 = Core.ConVar.Find("r_drawworld")!; + + cvar2.ReplicateToClient(0, true); + + cvar4.QueryClient(0, ( value ) => + { + Console.WriteLine("QueryCallback " + value); + }); + } + + [Command("w")] + public void TestCommand1( ICommandContext context ) + { + var ret = SteamGameServerUGC.DownloadItem(new PublishedFileId_t(3596198331), true); + Console.WriteLine(SteamGameServer.GetPublicIP().ToIPAddress()); + + + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate nint DispatchSpawnDelegate( nint pEntity, nint pKV ); + int order = 0; + + IUnmanagedFunction? _dispatchspawn; + + [Command("h1")] + public void TestCommand2( ICommandContext context ) + { + // var token = Core.Scheduler.DelayAndRepeat(500, 1000, () => + // { + + // }); + + var addres = Core.GameData.GetSignature("CBaseEntity::DispatchSpawn"); + var func = Core.Memory.GetUnmanagedFunctionByAddress(addres); + + var guid = func.AddHook(( next ) => + { + return ( pEntity, pKV ) => + { + Console.WriteLine("TestPlugin DispatchSpawn " + order++); + return next()(pEntity, pKV); + }; + }); + + _dispatchspawn.AddHook(( next ) => + { + return ( pEntity, pKV ) => + { + Console.WriteLine("TestPlugin DispatchSpawn2 " + order++); + return next()(pEntity, pKV); + }; + }); + + } + + [EventListener] + public void OnEntityCreated( IOnEntityCreatedEvent @event ) + { + // @event.Entity.Entity.DesignerName = "abc"; + Console.WriteLine("TestPlugin OnEntityCreated222 " + @event.Entity.Entity?.DesignerName); + } + + Guid _hookId = Guid.Empty; + + [Command("bad")] + public void TestCommandBad( ICommandContext context ) + { + try + { + var isValveDS = Core.EntitySystem.GetGameRules()!.IsValveDS; + } + catch (Exception e) + { + Core.Logger.LogWarning("{Exception}", e.Message); + } + + try + { + Core.EntitySystem.GetGameRules()!.IsValveDS = true; + } + catch (Exception e) + { + Core.Logger.LogWarning("{Exception}", e.Message); + } + + try + { + Core.EntitySystem.GetGameRules()!.IsValveDSUpdated(); + } + catch (Exception e) + { + Core.Logger.LogWarning("{Exception}", e.Message); + } + } + + [Command("h2")] + public void TestCommand3( ICommandContext context ) + { + var ent = Core.EntitySystem.CreateEntity(); + ent.DispatchSpawn(); + ent.Collision.MaxsUpdated(); + ent.Collision.CollisionAttribute.OwnerIdUpdated(); + } + + [Command("tt3")] + public void TestCommand33( ICommandContext context ) + { + var ent = Core.EntitySystem.CreateEntity(); + using CEntityKeyValues kv = new(); + kv.Set("m_spawnflags", 256); + ent.DispatchSpawn(kv); + ent.SetModel("weapons/models/grenade/incendiary/weapon_incendiarygrenade.vmdl"); + ent.Teleport(new Vector(context.Sender!.PlayerPawn!.AbsOrigin!.Value.X + 50, context.Sender!.PlayerPawn!.AbsOrigin!.Value.Y + 50, context.Sender!.PlayerPawn!.AbsOrigin!.Value.Z + 30), QAngle.Zero, Vector.Zero); + } + + [Command("tt4")] + public void TestCommand4( ICommandContext context ) + { + Console.WriteLine(Core.Permission.PlayerHasPermission(7656, context.Args[0])); + } + + [Command("tt5")] + public void TestCommand5( ICommandContext context ) + { + Console.WriteLine("TestPlugin TestCommand5"); + } + + [Command("tt6", permission: "tt6")] + public void TestCommand6( ICommandContext context ) + { + Console.WriteLine("TestPlugin TestCommand6"); + } + + [Command("tt7")] + public void TestCommand7( ICommandContext context ) + { + Core.Engine.ExecuteCommandWithBuffer("@ping", ( buffer ) => + { + Console.WriteLine($"pong: {buffer}"); + }); + } + + [Command("tt8")] + public unsafe void TestCommand8( ICommandContext context ) + { + Core.EntitySystem.GetAllEntitiesByDesignerName("func_buyzone").ToList().ForEach(zone => + { + if ((zone?.IsValid ?? false)) + { + zone.Despawn(); + } + }); + + var sender = context.Sender!; + var target = Core.PlayerManager.GetAllPlayers().FirstOrDefault(p => p.PlayerID != sender.PlayerID)!; + + var origin = sender.RequiredPlayerPawn.AbsOrigin ?? Vector.Zero; + var targetOrigin = target.RequiredPlayerPawn.AbsOrigin ?? Vector.Zero; + + Console.WriteLine("\n"); + Console.WriteLine($"Origin: {origin}"); + Console.WriteLine($"Target Origin: {targetOrigin}"); + + // Ray_t* ray = stackalloc Ray_t[1]; + // ray->Init(Vector.Zero, Vector.Zero); + Ray_t ray = new(); + ray.Init(Vector.Zero, Vector.Zero); + + var filter = new CTraceFilter { + // unk01 = 1, + IterateEntities = true, + QueryShapeAttributes = new RnQueryShapeAttr_t { + InteractsWith = MaskTrace.Player | MaskTrace.Solid | MaskTrace.Hitbox | MaskTrace.Npc, + InteractsExclude = MaskTrace.Empty, + InteractsAs = MaskTrace.Player, + CollisionGroup = CollisionGroup.PlayerMovement, + ObjectSetMask = RnQueryObjectSet.AllGameEntities, + HitSolid = true, + // HitTrigger = false, + // HitSolidRequiresGenerateContacts = false, + // ShouldIgnoreDisabledPairs = true, + // IgnoreIfBothInteractWithHitboxes = true, + // ForceHitEverything = true + } + }; + + // filter.QueryShapeAttributes.EntityIdsToIgnore[0] = unchecked((uint)-1); + // filter.QueryShapeAttributes.EntityIdsToIgnore[1] = unchecked((uint)-1); + // filter.QueryShapeAttributes.OwnerIdsToIgnore[0] = unchecked((uint)-1); + // filter.QueryShapeAttributes.OwnerIdsToIgnore[1] = unchecked((uint)-1); + // filter.QueryShapeAttributes.HierarchyIds[0] = 0; + // filter.QueryShapeAttributes.HierarchyIds[1] = 0; + + var trace = new CGameTrace(); + Core.Trace.TraceShape(origin, targetOrigin, ray, filter, ref trace); + + Console.WriteLine(trace.pEntity != null ? $"! Hit Entity: {trace.Entity.DesignerName}" : "! No entity hit"); + Console.WriteLine($"! SurfaceProperties: {(nint)trace.SurfaceProperties}, pEntity: {(nint)trace.pEntity}, HitBox: {(nint)trace.HitBox}({trace.HitBox->m_name.Value}), Body: {(nint)trace.Body}, Shape: {(nint)trace.Shape}, Contents: {trace.Contents}"); + Console.WriteLine($"! StartPos: {trace.StartPos}, EndPos: {trace.EndPos}, HitNormal: {trace.HitNormal}, HitPoint: {trace.HitPoint}"); + Console.WriteLine($"! HitOffset: {trace.HitOffset}, Fraction: {trace.Fraction}, Triangle: {trace.Triangle}, HitboxBoneIndex: {trace.HitboxBoneIndex}"); + Console.WriteLine($"! RayType: {trace.RayType}, StartInSolid: {trace.StartInSolid}, ExactHitPoint: {trace.ExactHitPoint}"); + Console.WriteLine("\n"); + } + + [GameEventHandler(HookMode.Pre)] + public HookResult TestGameEventHandler( EventPlayerJump @e ) + { + Console.WriteLine(@e.UserIdController.PlayerName); + return HookResult.Continue; + } + + [ServerNetMessageHandler] + public HookResult TestServerNetMessageHandler( CCSUsrMsg_SendPlayerItemDrops msg ) + { + Console.WriteLine("FIRED"); + // foreach(var item in msg.Accessor) { + // Console.WriteLine($"TestPlugin ServerNetMessageHandler: {item.EconItem.Defindex}"); + // } + return HookResult.Continue; + } + + private Callback _authTicketResponse; + + [EventListener] + public void OnSteamAPIActivated() + { + Console.WriteLine("TestPlugin OnSteamAPIActivated"); + _authTicketResponse = Callback.Create(AuthResponse); + } + + public void AuthResponse( GCMessageAvailable_t param ) + { + Console.WriteLine($"AuthResponse {param.m_nMessageSize}"); + } + + [Command("getip")] + public void GetIpCommand( ICommandContext context ) + { + context.Reply(SteamGameServer.GetPublicIP().ToString()); + } + + // [Command("i76")] + // public void TestIssue76Command( ICommandContext context ) + // { + // var player = context.Sender!; + // IMenu settingsMenu = Core.Menus.CreateMenu("Settings"); + // // Add the following code to render text properly + // //settingsMenu.Builder.AddText("123", overflowStyle: MenuHorizontalStyle.ScrollLeftLoop(25f)); + // settingsMenu.Builder.AddText("123"); + // settingsMenu.Builder.AddText("1234"); + // settingsMenu.Builder.AddText("12345"); + + // Core.Menus.OpenMenu(player, settingsMenu); + // } + + // [Command("i77")] + // public void TestIssue77Command( ICommandContext context ) + // { + // var player = context.Sender!; + // IMenu settingsMenu = Core.Menus.CreateMenu("Settings"); + + // settingsMenu.Builder.AddText("123"); + // settingsMenu.Builder.AddSubmenu("Submenu", () => + // { + // var menu = Core.Menus.CreateMenu("Submenu"); + // menu.Builder.AddText("1234"); + // return menu; + // }); + + // settingsMenu.Builder.AddSubmenu("Async Submenu", async () => + // { + // await Task.Delay(5000); + // var menu = Core.Menus.CreateMenu("Async Submenu"); + // menu.Builder.AddText("12345"); + // return menu; + // }); + + // Core.Menus.OpenMenu(player, settingsMenu); + // } + + // [Command("i78")] + // public void TestIssue78Command( ICommandContext context ) + // { + // var player = context.Sender!; + // IMenu settingsMenu = Core.Menus.CreateMenu("Settings"); + // settingsMenu.Builder.AddButton("123", ( p ) => + // { + // player.SendMessage(MessageType.Chat, "Button"); + // }); + + // settingsMenu.Builder.Design.OverrideExitButton("shift"); + // settingsMenu.Builder.Design.OverrideSelectButton("e"); + + // Core.Menus.OpenMenu(player, settingsMenu); + // } + + [Command("rmt")] + public void RefactoredMenuTestCommand( ICommandContext context ) + { + var button = new ButtonMenuOption($"{HtmlGradient.GenerateGradientText("Swiftlys2 向这广袤世界致以温柔问候", "#FFE4E1", "#FFC0CB", "#FF69B4")}") { TextStyle = MenuOptionTextStyle.ScrollLeftLoop }; + button.Click += ( sender, args ) => + { + args.Player.SendMessage(MessageType.Chat, "Swiftlys2 向这广袤世界致以温柔问候"); + // button.Visible = false; + button.Enabled = false; + // button.TextStyle = MenuOptionTextStyle.ScrollRightFade; + // button.Text = Regex.Match(button.Text, @"^(.*)#(\d+)$") is { Success: true } m + // ? $"{m.Groups[1].Value}#{int.Parse(m.Groups[2].Value) + 1}" + // : $"{button.Text}#1"; + // button.MaxWidth -= 1f; + _ = Task.Run(async () => + { + await Task.Delay(1000); + // button.Visible = true; + button.Enabled = true; + }); + return ValueTask.CompletedTask; + }; + + var player = context.Sender!; + var menu = Core.MenusAPI + .CreateBuilder() + .SetPlayerFrozen(false) + // .SetAutoCloseDelay(15f) + .Design.SetMaxVisibleItems(5) + .Design.SetMenuTitle($"{HtmlGradient.GenerateGradientText("Redesigned Menu", "#00FA9A", "#F5FFFA")}") + .Design.SetMenuTitleVisible(true) + .Design.SetMenuFooterVisible(true) + .Design.EnableAutoAdjustVisibleItems() + .Design.SetGlobalScrollStyle(MenuOptionScrollStyle.WaitingCenter) + // .AddOption(new TextMenuOption($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#AFEEEE", "#7FFFD4", "#40E0D0")}", textStyle: MenuOptionTextStyle.ScrollRightFade)) + // .AddOption(new TextMenuOption($"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", textStyle: MenuOptionTextStyle.ScrollLeftFade)) + // .AddOption(new TextMenuOption("12345678901234567890split12345678901234567890", textStyle: MenuOptionTextStyle.TruncateBothEnds)) + .AddOption(new TextMenuOption("1") { Visible = false }) + .AddOption(new ToggleMenuOption("12")) + .AddOption(new ChoiceMenuOption("123", ["Option 1", "Option 2", "Option 3"])) + .AddOption(new SliderMenuOption("1234")) + .AddOption(new ProgressBarMenuOption("12345", () => (float)new Random().NextDouble(), multiLine: false)) + .AddOption(new SubmenuMenuOption("123456", async () => + { + await Task.Delay(1000); + var menu = Core.MenusAPI.CreateBuilder() + .SetPlayerFrozen(true) + .Design.SetMenuTitle("Async Submenu") + .AddOption(new TextMenuOption("123456")) + .Build(); + return menu; + })) + .AddOption(new InputMenuOption("1234567")) + .AddOption(new TextMenuOption("12345678") { TextStyle = MenuOptionTextStyle.ScrollLeftLoop }) + .AddOption(new TextMenuOption("123456789")) + .AddOption(new TextMenuOption("1234567890") { Visible = false }) + .AddOption(button) + .AddOption(new TextMenuOption($"{HtmlGradient.GenerateGradientText("Swiftlys2 からこの広大なる世界へ温かい挨拶を", "#FFE5CC", "#FFAB91", "#FF7043")}") { TextStyle = MenuOptionTextStyle.ScrollRightLoop }) + .AddOption(new TextMenuOption($"{HtmlGradient.GenerateGradientText("Swiftlys2 가 이 넓은 세상에 따뜻한 인사를 전합니다", "#E6E6FA", "#00FFFF", "#FF1493")}") { TextStyle = MenuOptionTextStyle.ScrollLeftFade }) + .AddOption(new TextMenuOption($"{HtmlGradient.GenerateGradientText("Swiftlys2 приветствует этот прекрасный мир", "#AFEEEE", "#7FFFD4", "#40E0D0")}") { TextStyle = MenuOptionTextStyle.ScrollRightFade }) + .AddOption(new TextMenuOption("Swiftlys2 salută această lume minunată") { TextStyle = MenuOptionTextStyle.TruncateEnd }) + .AddOption(new TextMenuOption("Swiftlys2 extends warmest greetings to this wondrous world") { TextStyle = MenuOptionTextStyle.TruncateBothEnds }) + // .AddOption(new TextMenuOption("Swiftlys2 sendas korajn salutojn al ĉi tiu mirinda mondo")) + .AddOption(new TextMenuOption("1234567890") { Visible = false }) + .AddOption(new TextMenuOption("123456789")) + .AddOption(new TextMenuOption("12345678") { TextStyle = MenuOptionTextStyle.ScrollRightLoop }) + .AddOption(new InputMenuOption("1234567")) + .AddOption(new SubmenuMenuOption("123456", () => + { + var menu = Core.MenusAPI.CreateBuilder() + .Design.SetMenuTitle("Async Submenu") + .AddOption(new TextMenuOption("123456")) + .Build(); + return menu; + })) + .AddOption(new ProgressBarMenuOption("12345", () => (float)new Random().NextDouble(), multiLine: false)) + .AddOption(new SliderMenuOption("1234")) + .AddOption(new ChoiceMenuOption("123", ["Option 1", "Option 2", "Option 3"])) + .AddOption(new ToggleMenuOption("12")) + .AddOption(new TextMenuOption("1") { Visible = false }) + .Build(); + + Core.MenusAPI.OpenMenu(menu); + // Core.MenusAPI.OpenMenuForPlayer(player, menu); + } + + // [Command("mt")] + // public void MenuTestCommand( ICommandContext context ) + // { + // var player = context.Sender!; + + // IMenu settingsMenu = Core.Menus.CreateMenu("MenuTest"); + + // settingsMenu.Builder.Design.MaxVisibleItems(5); + + // // settingsMenu.Builder.Design.MaxVisibleItems(Random.Shared.Next(-2, 8)); + // if (context.Args.Length < 1 || !int.TryParse(context.Args[0], out int vtype)) vtype = 0; + // settingsMenu.Builder.Design.SetVerticalScrollStyle(vtype switch { + // 1 => MenuVerticalScrollStyle.LinearScroll, + // 2 => MenuVerticalScrollStyle.WaitingCenter, + // _ => MenuVerticalScrollStyle.CenterFixed + // }); + + // if (context.Args.Length < 2 || !int.TryParse(context.Args[1], out int htype)) htype = 0; + // settingsMenu.Builder.Design.SetGlobalHorizontalStyle(htype switch { + // 0 => MenuHorizontalStyle.Default, + // 1 => MenuHorizontalStyle.TruncateBothEnds(26f), + // 2 => MenuHorizontalStyle.ScrollLeftFade(26f, 8, 128), + // 3 => MenuHorizontalStyle.ScrollLeftLoop(26f, 8, 128), + // 1337 => MenuHorizontalStyle.TruncateEnd(0f), + // _ => MenuHorizontalStyle.TruncateEnd(26f) + // }); + + // settingsMenu.Builder.AddButton("1. AButton", ( p ) => + // { + // player.SendMessage(MessageType.Chat, "Button"); + // }); + + // settingsMenu.Builder.AddToggle("2. Toggle", defaultValue: true, ( p, value ) => + // { + // player.SendMessage(MessageType.Chat, $"AddToggle {value}"); + // }); + + // settingsMenu.Builder.AddSlider("3. Slider", min: 0, max: 100, defaultValue: 10, step: 10, ( p, value ) => + // { + // player.SendMessage(MessageType.Chat, $"AddSlider {value}"); + // }); + + // settingsMenu.Builder.AddAsyncButton("4. AsyncButton", async ( p ) => + // { + // await Task.Delay(2000); + // }); + + // settingsMenu.Builder.AddText("5. Text"); + // settingsMenu.Builder.AddText("6. Text"); + // settingsMenu.Builder.AddText("7. Text"); + // settingsMenu.Builder.AddText("8. Text"); + // settingsMenu.Builder.AddText("9. Text"); + // settingsMenu.Builder.AddSeparator(); + // settingsMenu.Builder.AddText($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#FFE4E1", "#FFC0CB", "#FF69B4")}", overflowStyle: MenuHorizontalStyle.TruncateEnd(26f)); + // settingsMenu.Builder.AddText($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#FFE5CC", "#FFAB91", "#FF7043")}", overflowStyle: MenuHorizontalStyle.TruncateBothEnds(26f)); + // settingsMenu.Builder.AddText($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#E6E6FA", "#00FFFF", "#FF1493")}", overflowStyle: MenuHorizontalStyle.ScrollRightFade(26f, 8)); + // settingsMenu.Builder.AddText($"{HtmlGradient.GenerateGradientText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "#AFEEEE", "#7FFFD4", "#40E0D0")}", overflowStyle: MenuHorizontalStyle.ScrollLeftLoop(26f, 8)); + // settingsMenu.Builder.AddText("12345678901234567890split12345678901234567890", overflowStyle: MenuHorizontalStyle.ScrollLeftFade(26f, 8, 128)); + // settingsMenu.Builder.AddText("一二三四五六七八九十分割一二三四五六七八九十", overflowStyle: MenuHorizontalStyle.ScrollRightLoop(26f, 8, 64)); + // settingsMenu.Builder.AddSeparator(); + // settingsMenu.Builder.AddText("Swiftlys2 向这广袤世界致以温柔问候", overflowStyle: MenuHorizontalStyle.ScrollRightLoop(26f, 8)); + // settingsMenu.Builder.AddText("Swiftlys2 からこの広大なる世界へ温かい挨拶を"); + // settingsMenu.Builder.AddText("Swiftlys2 가 이 넓은 세상에 따뜻한 인사를 전합니다"); + // settingsMenu.Builder.AddText("Swiftlys2 приветствует этот прекрасный мир"); + // settingsMenu.Builder.AddText("Swiftlys2 salută această lume minunată"); + // settingsMenu.Builder.AddText("Swiftlys2 extends warmest greetings to this wondrous world"); + // settingsMenu.Builder.AddText("Swiftlys2 sendas korajn salutojn al ĉi tiu mirinda mondo"); + // settingsMenu.Builder.AddSeparator(); + // settingsMenu.Builder.AddAsyncButton("AsyncButton|AsyncButton|AsyncButton", async ( p ) => await Task.Delay(2000)); + // settingsMenu.Builder.AddButton("Button|Button|Button|Button", ( p ) => { }); + // settingsMenu.Builder.AddChoice("Choice|Choice|Choice|Choice", ["Option 1", "Option 2", "Option 3"], "Option 1", ( p, value ) => { }, overflowStyle: MenuHorizontalStyle.TruncateEnd(8f)); + // settingsMenu.Builder.AddProgressBar("ProgressBar|ProgressBar|ProgressBar", () => (float)Random.Shared.NextDouble(), overflowStyle: MenuHorizontalStyle.ScrollLeftLoop(26f, 12)); + // settingsMenu.Builder.AddSlider("Slider|Slider|Slider|Slider", 0f, 100f, 0f, 1f, ( p, value ) => { }, overflowStyle: MenuHorizontalStyle.ScrollRightLoop(8f, 12)); + // // settingsMenu.Builder.AddSubmenu("Submenu"); + // settingsMenu.Builder.AddToggle("Toggle|Toggle|Toggle|Toggle", true, ( p, value ) => { }); + // settingsMenu.Builder.AddSeparator(); + + // Core.Menus.OpenMenu(player, settingsMenu); + // } + + // [Command("menu")] + // public void MenuCommand( ICommandContext context ) + // { + // var player = context.Sender!; + // var menu = Core.Menus.CreateMenu("Test Menu"); + + // menu.Builder + // .AddButton("Button 1", ( ctx ) => + // { + // player.SendMessage(MessageType.Chat, "You clicked Button 1"); + // }) + // .AddButton("Button 2", ( ctx ) => + // { + // player.SendMessage(MessageType.Chat, "You clicked Button 2"); + // }) + // .AddButton("Button 3", ( ctx ) => + // { + // player.SendMessage(MessageType.Chat, "You clicked Button 3"); + // }) + // .AddButton("Button 4", ( ctx ) => + // { + // player.SendMessage(MessageType.Chat, "You clicked Button 4"); + // }) + // .AddButton("Button 5", ( ctx ) => + // { + // player.SendMessage(MessageType.Chat, "You clicked Button 5"); + // }) + // .AddButton("Button 6", ( ctx ) => + // { + // player.SendMessage(MessageType.Chat, "You clicked Button 6"); + // }) + // .AddButton("Button 7", ( ctx ) => + // { + // player.SendMessage(MessageType.Chat, "You clicked Button 7"); + // }) + // .AddButton("Button 8", ( ctx ) => + // { + // player.SendMessage(MessageType.Chat, "You clicked Button 8"); + // }) + // .AddSeparator() + // .AddText("hello!", size: IMenuTextSize.ExtraLarge) + // .AutoClose(15f) + // .HasSound(true) + // .ForceFreeze(); + + // menu.Builder.Design.SetColor(new(0, 186, 105, 255)); + + // Core.Menus.OpenMenu(player, menu); + // } + + public override void Unload() + { + Console.WriteLine("TestPlugin unloaded"); + } } \ No newline at end of file diff --git a/managed/src/TestPlugin/TestPlugin.csproj b/managed/src/TestPlugin/TestPlugin.csproj index 458fcf6bc..da8249f20 100644 --- a/managed/src/TestPlugin/TestPlugin.csproj +++ b/managed/src/TestPlugin/TestPlugin.csproj @@ -1,30 +1,31 @@ - - - - net10.0 - enable - enable - true - true - true - ..\..\build\ - $(BaseOutputPath)Release\TestPlugin - TestPlugin - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + net10.0 + enable + enable + true + true + true + ..\..\build\ + $(BaseOutputPath)Release\TestPlugin + TestPlugin + + + + + + + + + + + + + + + + + + + \ No newline at end of file