Compare commits

...

3 Commits
v62 ... v1.0.65

Author SHA1 Message Date
Roflmuffin
20f50289ee docs: update docs to use ILogger 2023-11-21 16:50:20 +10:00
Michael Wilson
bb5fb5de72 Managed Core Logging & Plugin Logging (#102) 2023-11-21 16:42:56 +10:00
Nexd
6147739cfa hotfix: new signatures (#107) 2023-11-21 12:49:42 +10:00
22 changed files with 262 additions and 111 deletions

View File

@@ -11,18 +11,20 @@ This project is an ongoing migration of a previous project (titled [VSP.NET](htt
Due to the architectural changes of CS2, the plugin is being rebuilt on the ground up, to support Linux 64-bit, something which was previously impossible.
## Install
Download the latest build from [here](https://github.com/roflmuffin/CounterStrikeSharp/releases). (Download the with runtime version if this is your first time installing).
Detailed installation instructions can be found in the [docs](https://docs.cssharp.dev/guides/getting-started/).
## What works?
_(Note, these were features in the previous VSP.NET project, but have not been implemented yet in this project)_
These features are the core of the platform and work pretty well/have a low risk of causing issues.
- [x] Console Commands, Server Commands (e.g. css_mycommand)
- [x] Chat Commands with `!` and `/` prefixes (e.g. !mycommand)
- [ ] **(In Progress)** Console Variables
- [x] Console Commands, Server Commands (e.g. css_mycommand)
- [x] Chat Commands with `!` and `/` prefixes (e.g. !mycommand)
- [ ] **(In Progress)** Console Variables
- [x] Game Event Handlers & Custom Events (e.g. player_death)
- [x] Basic event value get/set (string, bool, int32, float)
- [x] Complex event values get/set (ehandle, pawn, player controller)
@@ -33,9 +35,10 @@ These features are the core of the platform and work pretty well/have a low risk
- [x] OnMapStart
- [x] OnTick
- [x] Server Information (current map, game time, tick rate, model precaching)
- [x] Schema System Access (access player values like current weapon, money, location etc.)
- [x] Schema System Access (access player values like current weapon, money, location etc.)
## Links
- [Join the Discord](https://discord.gg/X7r3PmuYKq): Ask questions, provide suggestions
- [Read the docs](https://docs.cssharp.dev/): Getting started guide, hello world plugin example
- [Issue tracker](https://github.com/roflmuffin/CounterStrikeSharp/issues): Raise any issues here
@@ -62,14 +65,14 @@ public class HelloWorldPlugin : BasePlugin
public override void Load(bool hotReload)
{
Console.WriteLine("Hello World!");
Logger.LogInformation("Plugin loaded successfully!");
}
[GameEventHandler]
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
{
// Userid will give you a reference to a CCSPlayerController class
Log($"Player {@event.Userid.PlayerName} has connected!");
Logger.LogInformation("Player {Name} has connected!", @event.Userid.PlayerName);
return HookResult.Continue;
}
@@ -77,7 +80,7 @@ public class HelloWorldPlugin : BasePlugin
[ConsoleCommand("issue_warning", "Issue warning to player")]
public void OnCommand(CCSPlayerController? player, CommandInfo command)
{
Log("You shouldn't be doing that!");
Logger.LogWarning("Player shouldn't be doing that");
}
}
```

View File

@@ -51,7 +51,7 @@
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x7C\\x24\\x20\\x55\\x48\\x8B\\xEC\\x48\\x83\\xEC\\x50",
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x7D\\xE0\\x48\\x83\\xEC\\x18\\x48\\x8D\\x05\\x25"
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x7D\\xE0\\x48\\x83\\xEC\\x18\\x48\\x8D\\x05\\xA5"
}
},
"CCSPlayer_ItemServices_DropActivePlayerWeapon": {
@@ -76,21 +76,21 @@
"signatures": {
"library": "server",
"windows": "\\x48\\x8B\\xC4\\x4C\\x89\\x48\\x20\\x55\\x56",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x49\\x89\\xFD\\x41\\x54\\x53\\x48\\x81\\xEC\\xE8\\x01\\x00\\x00"
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x49\\x89\\xFD\\x41\\x54\\x53\\x48\\x81\\xEC\\xE8"
}
},
"UTIL_CreateEntityByName": {
"signatures": {
"library": "server",
"windows": "\\x48\\x83\\xEC\\x48\\xC6\\x44\\x24\\x30\\x00\\x4C\\x8B\\xC1",
"linux": "\\x48\\x8D\\x05\\x49\\xB5\\xBC\\x00\\x55\\x48\\x89\\xFA\\x41\\x89\\xF0\\x48"
"linux": "\\x48\\x8D\\x05\\xC9\\xC2\\xBC"
}
},
"CBaseEntity_DispatchSpawn": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x57\\x48\\x83\\xEC\\x30\\x48\\x8B\\xDA\\x48\\x8B\\xF9\\x48\\x85\\xC9",
"linux": "\\x48\\x85\\xFF\\x74\\x4B\\x55\\x48\\x89\\xE5\\x41\\x56\\x41\\x55\\x49\\x89\\xFD"
"linux": "\\x48\\x85\\xFF\\x74\\x4B\\x55\\x48\\x89\\xE5\\x41\\x56"
}
},
"LegacyGameEventListener": {

View File

@@ -18,7 +18,7 @@ The first parameter type must be a subclass of the `GameEvent` class. The names
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
{
// Userid will give you a reference to a CCSPlayerController class
Log($"Player {@event.Userid.PlayerName} has connected!");
Logger.LogInformation("Player {Name} has connected!", @event.Userid.PlayerName);
return HookResult.Continue;
}
@@ -33,7 +33,7 @@ public override void Load(bool hotReload)
{
RegisterEventHandler<EventRoundStart>((@event, info) =>
{
Console.WriteLine($"Round has started with time limit of {@event.Timelimit}");
Logger.LogInformation("Round has started with time limit of {Timelimit}", @event.Timelimit);
return HookResult.Continue;
});

View File

@@ -22,7 +22,7 @@ public override void Load(bool hotReload)
projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f;
projectile.SmokeColor.Y = Random.Shared.NextSingle() * 255.0f;
projectile.SmokeColor.Z = Random.Shared.NextSingle() * 255.0f;
Log($"Smoke grenade spawned with color {projectile.SmokeColor}");
Logger.LogInformation("Smoke grenade spawned with color {SmokeColor}", projectile.SmokeColor);
});
});
}

View File

@@ -27,14 +27,14 @@ public class HelloWorldPlugin : BasePlugin
public override void Load(bool hotReload)
{
Console.WriteLine("Hello World!");
Logger.LogInformation("Plugin loaded successfully!");
}
[GameEventHandler]
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
{
// Userid will give you a reference to a CCSPlayerController class
Log($"Player {@event.Userid.PlayerName} has connected!");
Logger.LogInformation("Player {Name} has connected!", @event.Userid.PlayerName);
return HookResult.Continue;
}
@@ -42,7 +42,7 @@ public class HelloWorldPlugin : BasePlugin
[ConsoleCommand("issue_warning", "Issue warning to player")]
public void OnCommand(CCSPlayerController? player, CommandInfo command)
{
Log("You shouldn't be doing that!");
Logger.LogWarning("Player shouldn't be doing that");
}
}
```

View File

@@ -32,6 +32,7 @@ using CounterStrikeSharp.API.Modules.Listeners;
using CounterStrikeSharp.API.Modules.Timers;
using McMaster.NETCore.Plugins;
using CounterStrikeSharp.API.Modules.Config;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Core
{
@@ -53,6 +54,7 @@ namespace CounterStrikeSharp.API.Core
public string ModulePath { get; internal set; }
public string ModuleDirectory => Path.GetDirectoryName(ModulePath);
public ILogger Logger { get; set; }
public virtual void Load(bool hotReload)
{
@@ -314,7 +316,8 @@ namespace CounterStrikeSharp.API.Core
.Select(p => p.GetCustomAttribute<CastFromAttribute>()?.Type)
.ToArray();
Console.WriteLine($"Registering listener for {listenerName} with {parameterTypes.Length}");
GlobalContext.Instance.Logger.LogDebug("Registering listener for {ListenerName} with {ParameterCount} parameters",
listenerName, parameterTypes.Length);
var wrappedHandler = new Action<ScriptContext>(context =>
{

View File

@@ -24,6 +24,8 @@ using CounterStrikeSharp.API.Modules.Utils;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using System.Collections.Generic;
using CounterStrikeSharp.API.Core.Logging;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Core
{
@@ -79,6 +81,9 @@ namespace CounterStrikeSharp.API.Core
public static partial class CoreConfig
{
private static CoreConfigData _coreConfig = new CoreConfigData();
// TODO: ServiceCollection
private static ILogger _logger = CoreLogging.Factory.CreateLogger("CoreConfig");
static CoreConfig()
{
@@ -97,7 +102,7 @@ namespace CounterStrikeSharp.API.Core
{
if (!File.Exists(coreConfigPath))
{
Console.WriteLine($"Core configuration could not be found at path '{coreConfigPath}', fallback values will be used.");
_logger.LogWarning("Core configuration could not be found at path \"{CoreConfigPath}\", fallback values will be used.", coreConfigPath);
return;
}
@@ -109,12 +114,12 @@ namespace CounterStrikeSharp.API.Core
{
_coreConfig = data;
}
Console.WriteLine($"Loaded core configuration");
_logger.LogInformation("Successfully loaded core configuration.");
}
catch (Exception ex)
{
Console.WriteLine($"Failed to load core configuration: {ex}, fallback values will be used.");
_logger.LogWarning(ex, "Failed to load core configuration, fallback values will be used");
}
}
}

View File

@@ -18,6 +18,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Core
{
@@ -76,7 +77,7 @@ namespace CounterStrikeSharp.API.Core
}
catch (Exception e)
{
Console.WriteLine(e);
GlobalContext.Instance.Logger.LogError(e, "Error invoking callback");
}
});
s_callback = dg;
@@ -140,10 +141,7 @@ namespace CounterStrikeSharp.API.Core
{
ms_references.Remove(reference);
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($"Removing function/callback reference: {reference}");
Console.ResetColor();
GlobalContext.Instance.Logger.LogDebug("Removing function/callback reference: {Reference}", reference);
}
}
}

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Core;
@@ -39,17 +41,17 @@ public static class GameData
{
_methods = JsonSerializer.Deserialize<Dictionary<string, LoadedGameData>>(File.ReadAllText(gameDataPath))!;
Console.WriteLine($"Loaded game data with {_methods.Count} methods.");
GlobalContext.Instance.Logger.LogInformation("Loaded game data with {Count} methods.", _methods.Count);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to load game data: {ex}");
GlobalContext.Instance.Logger.LogError(ex, "Failed to load game data");
}
}
public static string GetSignature(string key)
{
Console.WriteLine($"Getting signature: {key}");
GlobalContext.Instance.Logger.LogDebug("Getting signature: {Key}", key);
if (!_methods.ContainsKey(key))
{
throw new ArgumentException($"Method {key} not found in gamedata.json");

View File

@@ -1,4 +1,4 @@
/*
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,18 +20,25 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using CounterStrikeSharp.API.Core.Logging;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Menu;
using CounterStrikeSharp.API.Modules.Utils;
using Microsoft.Extensions.Logging;
using Serilog;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace CounterStrikeSharp.API.Core
{
public sealed class GlobalContext
{
private static GlobalContext _instance = null;
public ILogger Logger { get; }
public static GlobalContext Instance => _instance;
public static string RootDirectory => _instance.rootDir.FullName;
private DirectoryInfo rootDir;
private readonly List<PluginContext> _loadedPlugins = new();
@@ -40,6 +47,9 @@ namespace CounterStrikeSharp.API.Core
{
rootDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.Parent;
_instance = this;
Logger = CoreLogging.Factory.CreateLogger("Core");
Logger.LogInformation("CounterStrikeSharp is starting up...");
}
~GlobalContext()
@@ -59,18 +69,26 @@ namespace CounterStrikeSharp.API.Core
}
public void InitGlobalContext()
{
Console.WriteLine("Loading CoreConfig from \"configs/core.json\"");
CoreConfig.Load(Path.Combine(rootDir.FullName, "configs", "core.json"));
var coreConfigPath = Path.Combine(rootDir.FullName, "configs", "core.json");
Logger.LogInformation("Loading CoreConfig from {Path}", coreConfigPath);
CoreConfig.Load(coreConfigPath);
Console.WriteLine("Loading GameData from \"gamedata/gamedata.json\"");
GameData.Load(Path.Combine(rootDir.FullName, "gamedata", "gamedata.json"));
Console.WriteLine("Loading Admin Groups from \"configs/admin_groups.json\"");
AdminManager.LoadAdminGroups(Path.Combine(rootDir.FullName, "configs", "admin_groups.json"));
Console.WriteLine("Loading Admins from \"configs/admins.json\"");
AdminManager.LoadAdminData(Path.Combine(rootDir.FullName, "configs", "admins.json"));
Console.WriteLine("Loading Admin Command Overrides from \"configs/admin_overrides.json\"");
AdminManager.LoadCommandOverrides(Path.Combine(rootDir.FullName, "configs", "admin_overrides.json"));
var gameDataPath = Path.Combine(rootDir.FullName, "gamedata", "gamedata.json");
Logger.LogInformation("Loading GameData from {Path}", gameDataPath);
GameData.Load(gameDataPath);
var adminGroupsPath = Path.Combine(rootDir.FullName, "configs", "admin_groups.json");
Logger.LogInformation("Loading Admin Groups from {Path}", adminGroupsPath);
AdminManager.LoadAdminGroups(adminGroupsPath);
var adminPath = Path.Combine(rootDir.FullName, "configs", "admins.json");
Logger.LogInformation("Loading Admins from {Path}", adminPath);
AdminManager.LoadAdminData(adminPath);
var overridePath = Path.Combine(rootDir.FullName, "configs", "admin_overrides.json");
Logger.LogInformation("Loading Admin Command Overrides from {Path}", overridePath);
AdminManager.LoadCommandOverrides(overridePath);
AdminManager.MergeGroupPermsIntoAdmins();
@@ -83,10 +101,10 @@ namespace CounterStrikeSharp.API.Core
ChatMenus.OnKeyPress(player, key);
});
}
Console.WriteLine("Loading C# plugins...");
Logger.LogInformation("Loading C# plugins...");
var pluginCount = LoadAllPlugins();
Console.WriteLine($"All managed modules were loaded. {pluginCount} plugins loaded.");
Logger.LogInformation("All managed modules were loaded. {PluginCount} plugins loaded.", pluginCount);
RegisterPluginCommands();
}
@@ -113,7 +131,7 @@ namespace CounterStrikeSharp.API.Core
}
catch (Exception e)
{
Console.WriteLine(e);
Logger.LogError(e, "Error finding plugin path");
return 0;
}
@@ -136,14 +154,13 @@ namespace CounterStrikeSharp.API.Core
foreach (var path in filePaths)
{
Console.WriteLine($"Plugin path: {path}");
try
{
LoadPlugin(path);
}
catch (Exception e)
{
Console.WriteLine($"Failed to load plugin {path} with error {e}");
Logger.LogError(e, "Failed to load plugin from {Path}", path);
}
}
@@ -267,7 +284,7 @@ namespace CounterStrikeSharp.API.Core
}
catch (Exception e)
{
Console.WriteLine($"Failed to load plugin {path} with error {e}");
Logger.LogError(e, "Failed to load plugin from {Path}", path);
}
break;

View File

@@ -0,0 +1,29 @@
using System;
using System.IO;
using Microsoft.Extensions.Logging;
using Serilog;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace CounterStrikeSharp.API.Core.Logging;
public static class CoreLogging
{
public static ILoggerFactory Factory { get; }
static CoreLogging()
{
var logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.With<SourceContextEnricher>()
.WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
.WriteTo.File(Path.Join(new[] {GlobalContext.RootDirectory, "logs", $"log-cssharp.txt"}), rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
.WriteTo.File(Path.Join(new[] {GlobalContext.RootDirectory, "logs", $"log-all.txt"}), rollingInterval: RollingInterval.Day, shared: true, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] (cssharp:{SourceContext}) {Message:lj}{NewLine}{Exception}")
.CreateLogger();
Factory =
LoggerFactory.Create(builder =>
{
builder.AddSerilog(logger);
});
}
}

View File

@@ -0,0 +1,32 @@
using System.IO;
using Microsoft.Extensions.Logging;
using Serilog;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace CounterStrikeSharp.API.Core.Logging;
public class PluginLogging
{
/// <summary>
/// Creates a logger scoped to a specific plugin
/// <remarks>Eventually this should probably come from a service collection</remarks>
/// </summary>
public static ILogger CreatePluginLogger(PluginContext pluginContext)
{
var logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.With(new PluginNameEnricher(pluginContext))
.WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u4}] (plugin:{PluginName}) {Message:lj}{NewLine}{Exception}")
.WriteTo.File(Path.Join(new[] {GlobalContext.RootDirectory, "logs", $"log-{pluginContext.PluginType.Name}.txt"}), rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] plugin:{PluginName} {Message:lj}{NewLine}{Exception}")
.WriteTo.File(Path.Join(new[] {GlobalContext.RootDirectory, "logs", $"log-all.txt"}), rollingInterval: RollingInterval.Day, shared: true, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u4}] plugin:{PluginName} {Message:lj}{NewLine}{Exception}")
.CreateLogger();
using ILoggerFactory loggerFactory =
LoggerFactory.Create(builder =>
{
builder.AddSerilog(logger);
});
return loggerFactory.CreateLogger(pluginContext.PluginType);
}
}

View File

@@ -0,0 +1,22 @@
using Serilog.Core;
using Serilog.Events;
namespace CounterStrikeSharp.API.Core.Logging;
public class PluginNameEnricher : ILogEventEnricher
{
public const string PropertyName = "PluginName";
public PluginNameEnricher(PluginContext pluginContext)
{
Context = pluginContext;
}
public PluginContext Context { get; }
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var property = propertyFactory.CreateProperty(PropertyName, Context.PluginType.Name);
logEvent.AddPropertyIfAbsent(property);
}
}

View File

@@ -0,0 +1,26 @@
using System.Linq;
using Serilog.Core;
using Serilog.Events;
namespace CounterStrikeSharp.API.Core.Logging;
public class SourceContextEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (logEvent.Properties.TryGetValue("SourceContext", out var property))
{
var scalarValue = property as ScalarValue;
var value = scalarValue?.Value as string;
if (value?.StartsWith("CounterStrikeSharp") ?? false)
{
var lastElement = value.Split(".").LastOrDefault();
if (!string.IsNullOrWhiteSpace(lastElement))
{
logEvent.AddOrUpdateProperty(new LogEventProperty("SourceContext", new ScalarValue(lastElement)));
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
/*
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,9 +20,9 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Modules.Config;
using CounterStrikeSharp.API.Modules.Events;
using CounterStrikeSharp.API.Core.Logging;
using McMaster.NETCore.Plugins;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Core
{
@@ -43,6 +43,9 @@ namespace CounterStrikeSharp.API.Core
private readonly string _path;
private readonly FileSystemWatcher _fileWatcher;
// TOOD: ServiceCollection
private ILogger _logger = CoreLogging.Factory.CreateLogger<PluginContext>();
public PluginContext(string path, int id)
{
_path = path;
@@ -63,7 +66,7 @@ namespace CounterStrikeSharp.API.Core
{
if (e.FullPath == path)
{
Console.WriteLine($"Plugin {Name} has been deleted, unloading...");
_logger.LogInformation("Plugin {Name} has been deleted, unloading...", Name);
Unload(true);
}
};
@@ -75,7 +78,7 @@ namespace CounterStrikeSharp.API.Core
private Task OnReloadedAsync(object sender, PluginReloadedEventArgs eventargs)
{
Console.WriteLine($"Reloading plugin {Name}");
_logger.LogInformation("Reloading plugin {Name}", Name);
_assemblyLoader = eventargs.Loader;
Unload(hotReload: true);
Load(hotReload: true);
@@ -100,14 +103,15 @@ namespace CounterStrikeSharp.API.Core
throw new Exception(
$"Plugin \"{Path.GetFileName(_path)}\" requires a newer version of CounterStrikeSharp. The plugin expects version [{minimumApiVersion}] but the current version is [{currentVersion}].");
Console.WriteLine($"Loading plugin: {pluginType.Name}");
_logger.LogInformation("Loading plugin {Name}", pluginType.Name);
_plugin = (BasePlugin)Activator.CreateInstance(pluginType)!;
_plugin.ModulePath = _path;
_plugin.RegisterAllAttributes(_plugin);
_plugin.Logger = PluginLogging.CreatePluginLogger(this);
_plugin.InitializeConfig(_plugin, pluginType);
_plugin.Load(hotReload);
Console.WriteLine($"Finished loading plugin: {Name}");
_logger.LogInformation("Finished loading plugin {Name}", Name);
}
}
@@ -115,7 +119,7 @@ namespace CounterStrikeSharp.API.Core
{
var cachedName = Name;
Console.WriteLine($"Unloading plugin {Name}");
_logger.LogInformation("Unloading plugin {Name}", Name);
_plugin.Unload(hotReload);
@@ -127,7 +131,7 @@ namespace CounterStrikeSharp.API.Core
_fileWatcher.Dispose();
}
Console.WriteLine($"Finished unloading plugin {cachedName}");
_logger.LogInformation("Finished unloading plugin {Name}", Name);
}
}
}

View File

@@ -67,13 +67,11 @@ namespace CounterStrikeSharp.API.Core
public unsafe ScriptContext()
{
//Console.WriteLine("Global context address: " + (IntPtr)m_extContext);
}
public unsafe ScriptContext(fxScriptContext* context)
{
m_extContext = *context;
//Console.WriteLine("Global context address: " + (IntPtr)m_extContext);
}
private readonly ConcurrentQueue<Action> ms_finalizers = new ConcurrentQueue<Action>();

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
@@ -23,6 +23,10 @@
<ItemGroup>
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,9 +1,11 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Utils;
using System.IO;
using System.Linq;
using System.Reflection;
using CounterStrikeSharp.API.Core.Logging;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Modules.Admin
{

View File

@@ -10,6 +10,8 @@ using CounterStrikeSharp.API.Modules.Commands;
using System.Reflection;
using System.Numerics;
using System.Linq;
using CounterStrikeSharp.API.Core.Logging;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Modules.Admin
{
@@ -24,6 +26,9 @@ namespace CounterStrikeSharp.API.Modules.Admin
public static partial class AdminManager
{
private static Dictionary<SteamID, AdminData> Admins = new();
// TODO: ServiceCollection
private static ILogger _logger = CoreLogging.Factory.CreateLogger("AdminManager");
public static void LoadAdminData(string adminDataPath)
{
@@ -31,7 +36,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
{
if (!File.Exists(adminDataPath))
{
Console.WriteLine("Admin data file not found. Skipping admin data load.");
_logger.LogWarning("Admin data file not found. Skipping admin data load.");
return;
}
@@ -56,11 +61,11 @@ namespace CounterStrikeSharp.API.Modules.Admin
}
}
Console.WriteLine($"Loaded admin data with {Admins.Count} admins.");
_logger.LogInformation("Loaded admin data with {Count} admins.", Admins.Count);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to load admin data: {ex}");
_logger.LogError(ex, "Failed to load admin data");
}
}

View File

@@ -21,6 +21,8 @@ using System.Text;
using System.Text.Json;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Logging;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Modules.Config
{
@@ -29,6 +31,7 @@ namespace CounterStrikeSharp.API.Modules.Config
private static readonly DirectoryInfo? _rootDir;
private static readonly string _pluginConfigsFolderPath;
private static ILogger _logger = CoreLogging.Factory.CreateLogger("ConfigManager");
static ConfigManager()
{
@@ -60,7 +63,7 @@ namespace CounterStrikeSharp.API.Modules.Config
}
catch (Exception ex)
{
Console.WriteLine($"Failed to generate configuration file for {pluginName}: {ex}");
_logger.LogError(ex, "Failed to generate configuration file for {PluginName}", pluginName);
}
}
@@ -70,7 +73,7 @@ namespace CounterStrikeSharp.API.Modules.Config
}
catch (Exception ex)
{
Console.WriteLine($"Failed to parse configuration '{pluginName}': {ex}");
_logger.LogError(ex, "Failed to parse configuration file for {PluginName}", pluginName);
}
return config;

View File

@@ -16,6 +16,8 @@
using System;
using System.Collections.Generic;
using CounterStrikeSharp.API.Core;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Modules.Memory
{
@@ -71,7 +73,7 @@ namespace CounterStrikeSharp.API.Modules.Memory
return types[Enum.GetUnderlyingType(type)];
}
Console.WriteLine("Error retrieving data type for type" + type.FullName);
GlobalContext.Instance.Logger.LogWarning("Error retrieving data type for type {Type}", type.FullName);
return null;
}

View File

@@ -19,7 +19,6 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
@@ -30,17 +29,17 @@ using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Events;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Menu;
using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.Utils;
using Microsoft.Extensions.Logging;
namespace TestPlugin
{
public class SampleConfig : BasePluginConfig
{
[JsonPropertyName("IsPluginEnabled")]
public bool IsPluginEnabled { get; set; } = true;
[JsonPropertyName("IsPluginEnabled")] public bool IsPluginEnabled { get; set; } = true;
[JsonPropertyName("LogPrefix")]
public string LogPrefix { get; set; } = "CSSharp";
[JsonPropertyName("LogPrefix")] public string LogPrefix { get; set; } = "CSSharp";
}
[MinimumApiVersion(33)]
@@ -67,14 +66,14 @@ namespace TestPlugin
// Basic usage of the configuration system
if (!Config.IsPluginEnabled)
{
Console.WriteLine($"{Config.LogPrefix} {ModuleName} is disabled");
Logger.LogWarning($"{Config.LogPrefix} {ModuleName} is disabled");
return;
}
Console.WriteLine(
Logger.LogInformation(
$"Test Plugin has been loaded, and the hot reload flag was {hotReload}, path is {ModulePath}");
Console.WriteLine($"Max Players: {Server.MaxPlayers}");
Logger.LogWarning($"Max Players: {Server.MaxPlayers}");
SetupConvars();
SetupGameEvents();
@@ -84,7 +83,7 @@ namespace TestPlugin
// ValveInterface provides pointers to loaded modules via Interface Name exposed from the engine (e.g. Source2Server001)
var server = ValveInterface.Server;
Log($"Server pointer found @ {server.Pointer:X}");
Logger.LogInformation("Server pointer found @ {Pointer:X}", server.Pointer);
// You can use `ModuleDirectory` to get the directory of the plugin (for storing config files, saving database files etc.)
File.WriteAllText(Path.Join(ModuleDirectory, "example.txt"),
@@ -98,7 +97,7 @@ namespace TestPlugin
// This value is asserted against the native code that points to the same function.
var virtualFunc = VirtualFunction.Create<IntPtr>(server.Pointer, 91);
var result = virtualFunc() - 8;
Log($"Result of virtual func call is {result:X}");
Logger.LogInformation("Result of virtual func call is {Pointer:X}", result);
}
private void SetupConvars()
@@ -109,13 +108,13 @@ namespace TestPlugin
cheatsCvar.SetValue(true);
var numericCvar = ConVar.Find("mp_warmuptime");
Console.WriteLine($"mp_warmuptime = {numericCvar?.GetPrimitiveValue<float>()}");
Logger.LogInformation("mp_warmuptime = {Value}", numericCvar?.GetPrimitiveValue<float>());
var stringCvar = ConVar.Find("sv_skyname");
Console.WriteLine($"sv_skyname = {stringCvar?.StringValue}");
Logger.LogInformation("sv_skyname = {Value}", stringCvar?.StringValue);
var fogCvar = ConVar.Find("fog_color");
Console.WriteLine($"fog_color = {fogCvar?.GetNativeValue<Vector>()}");
Logger.LogInformation("fog_color = {Value}", fogCvar?.GetNativeValue<Vector>());
});
}
@@ -128,7 +127,7 @@ namespace TestPlugin
var entity = new CCSPlayerController(NativeAPI.GetEntityFromIndex(@event.Userid));
if (!entity.IsValid)
{
Log("invalid entity");
Logger.LogInformation("invalid entity");
return HookResult.Continue;
}
@@ -151,7 +150,8 @@ namespace TestPlugin
if (!@event.Userid.IsValid) return 0;
if (!@event.Userid.PlayerPawn.IsValid) return 0;
Log($"Player spawned with entity index: {@event.Userid.EntityIndex} & User ID: {@event.Userid.UserId}");
Logger.LogInformation("Player spawned with entity index: {EntityIndex} & User ID: {UserId}",
@event.Userid.EntityIndex, @event.Userid.UserId);
return HookResult.Continue;
});
@@ -166,7 +166,7 @@ namespace TestPlugin
// Set player to random colour
player.PlayerPawn.Value.Render = Color.FromArgb(Random.Shared.Next(0, 255),
Random.Shared.Next(0, 255), Random.Shared.Next(0, 255));
Server.NextFrame(() =>
{
player.PrintToCenter(string.Join("\n", weapons.Select(x => x.Value.DesignerName)));
@@ -175,17 +175,14 @@ namespace TestPlugin
activeWeapon.ReserveAmmo[0] = 250;
activeWeapon.Clip1 = 250;
Log(
$"Pawn Position: {pawn.CBodyComponent?.SceneNode?.AbsOrigin} @{pawn.CBodyComponent?.SceneNode.Rotation}");
Logger.LogInformation("Pawn Position: {AbsOrigin}-{Rotation}", pawn.AbsOrigin, pawn.AbsRotation);
char randomColourChar = (char)new Random().Next(0, 16);
Server.PrintToChatAll($"Random String with Random Colour: {randomColourChar}{new Random().Next()}");
pawn.Health += 5;
Log(
$"Found steamID {new SteamID(player.SteamID)} for player {player.PlayerName}:{pawn.Health}|{pawn.InBuyZone}");
Log($"{@event.Userid}, {@event.X},{@event.Y},{@event.Z}");
Logger.LogInformation("Bullet Impact: {X},{Y},{Z}", @event.X, @event.Y, @event.Z);
return HookResult.Continue;
});
@@ -193,7 +190,7 @@ namespace TestPlugin
{
// Grab all cs_player_controller entities and set their cash value to $1337.
var playerEntities = Utilities.GetPlayers();
Log($"cs_player_controller count: {playerEntities.Count()}");
Logger.LogInformation($"cs_player_controller count: {playerEntities.Count()}");
foreach (var player in playerEntities)
{
@@ -204,7 +201,7 @@ namespace TestPlugin
// Grab everything starting with cs_, but we'll only mainpulate cs_gamerules.
// Note: this iterates through all entities, so is an expensive operation.
var csEntities = Utilities.FindAllEntitiesByDesignerName<CBaseEntity>("cs_");
Log($"Amount of cs_* entities: {csEntities.Count()}");
Logger.LogInformation("Amount of cs_* entities: {Count}", csEntities.Count());
foreach (var entity in csEntities)
{
@@ -220,15 +217,18 @@ namespace TestPlugin
private void SetupListeners()
{
// Hook global listeners defined by CounterStrikeSharp
RegisterListener<Listeners.OnMapStart>(mapName => { Log($"Map {mapName} has started!"); });
RegisterListener<Listeners.OnMapEnd>(() => { Log($"Map has ended."); });
RegisterListener<Listeners.OnMapStart>(mapName =>
{
Logger.LogInformation("Map {Map} has started!", mapName);
});
RegisterListener<Listeners.OnMapEnd>(() => { Logger.LogInformation($"Map has ended."); });
RegisterListener<Listeners.OnClientConnect>((index, name, ip) =>
{
Log($"Client {name} from {ip} has connected!");
Logger.LogInformation("Client {Name} from {Ip} has connected!", name, ip);
});
RegisterListener<Listeners.OnClientAuthorized>((index, id) =>
{
Log($"Client {index} with address {id}");
Logger.LogInformation("Client {Index} with address {Id}", index, id);
});
RegisterListener<Listeners.OnEntitySpawned>(entity =>
@@ -245,7 +245,8 @@ namespace TestPlugin
projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f;
projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f;
projectile.SmokeColor.X = Random.Shared.NextSingle() * 255.0f;
Log($"Smoke grenade spawned with color {projectile.SmokeColor}");
Logger.LogInformation("Smoke grenade spawned with color {SmokeColor}",
projectile.SmokeColor);
});
return;
case "flashbang_projectile":
@@ -298,8 +299,9 @@ namespace TestPlugin
(player, info) =>
{
if (player == null) return;
Log(
$"CounterStrikeSharp - a test command was called by {new SteamID(player.SteamID).SteamId2} with {info.ArgString}");
Logger.LogInformation(
"CounterStrikeSharp - a test command was called by {SteamID2} with {Arguments}",
((SteamID)player.SteamID).SteamId2, info.ArgString);
});
AddCommand("css_changeteam", "change team", (player, info) =>
@@ -319,7 +321,8 @@ namespace TestPlugin
// Listens for any client use of the command `jointeam`.
AddCommandListener("jointeam", (player, info) =>
{
Log($"{player.PlayerName} just did a jointeam (pre) [{info.ArgString}]");
Logger.LogInformation("{PlayerName} just did a jointeam (pre) [{ArgString}]", player.PlayerName,
info.ArgString);
return HookResult.Continue;
});
@@ -328,7 +331,7 @@ namespace TestPlugin
[GameEventHandler]
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
{
Log($"Player {@event.Name} has connected! (post)");
Logger.LogInformation("Player {Name} has connected! (post)", @event.Name);
return HookResult.Continue;
}
@@ -336,7 +339,7 @@ namespace TestPlugin
[GameEventHandler(HookMode.Pre)]
public HookResult OnPlayerConnectPre(EventPlayerConnect @event, GameEventInfo info)
{
Log($"Player {@event.Name} has connected! (pre)");
Logger.LogInformation("Player {Name} has connected! (pre)", @event.Name);
return HookResult.Continue;
}
@@ -387,7 +390,7 @@ namespace TestPlugin
[ConsoleCommand("css_pause", "Pause Game")]
public void OnCommandPause(CCSPlayerController? player, CommandInfo command)
{
Log("Pause");
Logger.LogInformation("Pause");
}
[ConsoleCommand("css_give", "Give named item")]
@@ -400,17 +403,10 @@ namespace TestPlugin
private HookResult GenericEventHandler<T>(T @event, GameEventInfo info) where T : GameEvent
{
Log($"Event found {@event.Handle:X}, event name: {@event.EventName} dont broadcast: {info.DontBroadcast}");
Logger.LogInformation("Event found {Pointer:X}, event name: {EventName}, dont broadcast: {DontBroadcast}",
@event.Handle, @event.EventName, info.DontBroadcast);
return HookResult.Continue;
}
private void Log(string message)
{
Console.BackgroundColor = ConsoleColor.DarkGray;
Console.ForegroundColor = ConsoleColor.DarkMagenta;
Console.WriteLine(message);
Console.ResetColor();
}
}
}