mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-06 08:03:12 -08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49974d23fc | ||
|
|
89ef4a6323 | ||
|
|
5211fde474 | ||
|
|
291c0db25e | ||
|
|
0f994b4ae1 | ||
|
|
64ddf3ef19 | ||
|
|
9b11af112c | ||
|
|
1b6bd22e06 | ||
|
|
8cf9024ee5 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,6 @@
|
||||
.ccls-cache/
|
||||
.cmake/
|
||||
cmake-build-debug*/
|
||||
cmake-build-debug/
|
||||
.kdev4/
|
||||
.vscode/
|
||||
generated/
|
||||
|
||||
@@ -83,7 +83,7 @@ SET(SOURCE_FILES
|
||||
src/core/managers/server_manager.h
|
||||
src/scripting/natives/natives_server.cpp
|
||||
libraries/nlohmann/json.hpp
|
||||
src/scripting/natives/natives_dynamichooks.cpp
|
||||
src/scripting/natives/natives_dynamichooks.cpp
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -164,5 +164,12 @@
|
||||
"windows": 91,
|
||||
"linux": 91
|
||||
}
|
||||
},
|
||||
"CEntityIOOutput_FireOutputInternal": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x8B\\xC4\\x4C\\x89\\x48\\x20\\x55\\x57\\x41\\x54\\x41\\x56",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x53\\x48\\x83\\xEC\\x58\\x4C\\x8B\\x6F\\x08"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# With Database (Dapper)
|
||||
Simple SQLite database example using Dapper library to track kills.
|
||||
@@ -1,16 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.1.24" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.14" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,89 +0,0 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using Dapper;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WithDatabaseDapper;
|
||||
|
||||
[MinimumApiVersion(80)]
|
||||
public class WithDatabaseDapperPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: With Database (Dapper)";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A plugin that reads and writes from the database.";
|
||||
|
||||
private SqliteConnection _connection = null!;
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
Logger.LogInformation("Loading database from {Path}", Path.Join(ModuleDirectory, "database.db"));
|
||||
_connection = new SqliteConnection($"Data Source={Path.Join(ModuleDirectory, "database.db")}");
|
||||
_connection.Open();
|
||||
|
||||
// Create the table if it doesn't exist
|
||||
// Run in a separate thread to avoid blocking the main thread
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await _connection.ExecuteAsync(@"
|
||||
CREATE TABLE IF NOT EXISTS `players` (
|
||||
`steamid` UNSIGNED BIG INT NOT NULL,
|
||||
`kills` INT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`steamid`));");
|
||||
});
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerKilled(EventPlayerDeath @event, GameEventInfo info)
|
||||
{
|
||||
// Don't count suicides.
|
||||
if (@event.Attacker == @event.Userid) return HookResult.Continue;
|
||||
|
||||
// Capture the steamid of the player as `@event` will not be available outside of this function.
|
||||
var steamId = @event.Attacker.AuthorizedSteamID?.SteamId64;
|
||||
|
||||
if (steamId == null) return HookResult.Continue;
|
||||
|
||||
// Run in a separate thread to avoid blocking the main thread
|
||||
Task.Run(async () =>
|
||||
{
|
||||
// insert or update the player's kills
|
||||
await _connection.ExecuteAsync(@"
|
||||
INSERT INTO `players` (`steamid`, `kills`) VALUES (@SteamId, 1)
|
||||
ON CONFLICT(`steamid`) DO UPDATE SET `kills` = `kills` + 1;",
|
||||
new
|
||||
{
|
||||
SteamId = steamId
|
||||
});
|
||||
});
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[ConsoleCommand("css_kills", "Get count of kills for a player")]
|
||||
public void OnKillsCommand(CCSPlayerController? player, CommandInfo commandInfo)
|
||||
{
|
||||
if (player == null) return;
|
||||
|
||||
// Capture the SteamID of the player as `@event` will not be available outside of this function.
|
||||
var steamId = player.AuthorizedSteamID.SteamId64;
|
||||
|
||||
// Run in a separate thread to avoid blocking the main thread
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var result = await _connection.QueryFirstOrDefaultAsync(@"SELECT `kills` FROM `players` WHERE `steamid` = @SteamId;",
|
||||
new
|
||||
{
|
||||
SteamId = steamId
|
||||
});
|
||||
|
||||
// Print the result to the player's chat. Note that this needs to be run on the game thread.
|
||||
// So we use `Server.NextFrame` to run it on the next game tick.
|
||||
Server.NextFrame(() => { player.PrintToChat($"Kills: {result?.kills ?? 0}"); });
|
||||
});
|
||||
}
|
||||
}
|
||||
Submodule libraries/hl2sdk-cs2 updated: d7ed476064...1d394d3365
@@ -157,30 +157,6 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetClientConvarValue(int clientindex, string convarname){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(clientindex);
|
||||
ScriptContext.GlobalScriptContext.Push(convarname);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xAE4B1B79);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetFakeClientConvarValue(int clientindex, string convarname, string convarvalue){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(clientindex);
|
||||
ScriptContext.GlobalScriptContext.Push(convarname);
|
||||
ScriptContext.GlobalScriptContext.Push(convarvalue);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x4C61E8BB);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static T DynamicHookGetReturn<T>(IntPtr hook, int datatype){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -479,16 +455,6 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static void QueueTaskForNextWorldUpdate(IntPtr callback){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(callback);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xAD51A0C9);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr GetValveInterface(int interfacetype, string interfacename){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
@@ -643,14 +609,27 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPlayerIpAddress(int slot){
|
||||
public static void HookEntityOutput(string classname, string outputname, InputArgument callback){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(slot);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x46A45CB0);
|
||||
ScriptContext.GlobalScriptContext.Push(classname);
|
||||
ScriptContext.GlobalScriptContext.Push(outputname);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x15245242);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnhookEntityOutput(string classname, string outputname, InputArgument callback){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(classname);
|
||||
ScriptContext.GlobalScriptContext.Push(outputname);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x87DBD139);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1087,16 +1066,6 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsServerPaused(){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xB216AAAC);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool));
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr CreateTimer(float interval, InputArgument callback, int flags){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class EntityOutputHookAttribute : Attribute
|
||||
{
|
||||
public string Classname { get; }
|
||||
public string OutputName { get; }
|
||||
|
||||
public EntityOutputHookAttribute(string classname, string outputName)
|
||||
{
|
||||
Classname = classname;
|
||||
OutputName = outputName;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Events;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CounterStrikeSharp.API.Modules.Config;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
@@ -36,6 +37,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
public BasePlugin()
|
||||
{
|
||||
RegisterListener<Listeners.OnMapEnd>(() =>
|
||||
{
|
||||
foreach (KeyValuePair<Delegate, EntityIO.EntityOutputCallback> callback in EntitySingleOutputHooks)
|
||||
{
|
||||
UnhookSingleEntityOutputInternal(callback.Value.Classname, callback.Value.Output, callback.Value.Handler);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public abstract string ModuleName { get; }
|
||||
@@ -108,6 +116,12 @@ namespace CounterStrikeSharp.API.Core
|
||||
public readonly Dictionary<Delegate, CallbackSubscriber> Listeners =
|
||||
new Dictionary<Delegate, CallbackSubscriber>();
|
||||
|
||||
public readonly Dictionary<Delegate, CallbackSubscriber> EntityOutputHooks =
|
||||
new Dictionary<Delegate, CallbackSubscriber>();
|
||||
|
||||
internal readonly Dictionary<Delegate, EntityIO.EntityOutputCallback> EntitySingleOutputHooks =
|
||||
new Dictionary<Delegate, EntityIO.EntityOutputCallback>();
|
||||
|
||||
public readonly List<Timer> Timers = new List<Timer>();
|
||||
|
||||
public delegate HookResult GameEventHandler<T>(T @event, GameEventInfo info) where T : GameEvent;
|
||||
@@ -354,6 +368,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
this.RegisterAttributeHandlers(instance);
|
||||
this.RegisterConsoleCommandAttributeHandlers(instance);
|
||||
this.RegisterEntityOutputAttributeHandlers(instance);
|
||||
}
|
||||
|
||||
public void InitializeConfig(object instance, Type pluginType)
|
||||
@@ -430,6 +445,75 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterEntityOutputAttributeHandlers(object instance)
|
||||
{
|
||||
var handlers = instance.GetType()
|
||||
.GetMethods()
|
||||
.Where(method => method.GetCustomAttributes<EntityOutputHookAttribute>().Any())
|
||||
.ToArray();
|
||||
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
var attributes = handler.GetCustomAttributes<EntityOutputHookAttribute>();
|
||||
foreach (var outputInfo in attributes)
|
||||
{
|
||||
HookEntityOutput(outputInfo.Classname, outputInfo.OutputName, handler.CreateDelegate<EntityIO.EntityOutputHandler>(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HookEntityOutput(string classname, string outputName, EntityIO.EntityOutputHandler handler)
|
||||
{
|
||||
var subscriber = new CallbackSubscriber(handler, handler,
|
||||
() => UnhookEntityOutput(classname, outputName, handler));
|
||||
|
||||
NativeAPI.HookEntityOutput(classname, outputName, subscriber.GetInputArgument());
|
||||
EntityOutputHooks[handler] = subscriber;
|
||||
}
|
||||
|
||||
public void UnhookEntityOutput(string classname, string outputName, EntityIO.EntityOutputHandler handler)
|
||||
{
|
||||
if (!EntityOutputHooks.TryGetValue(handler, out var subscriber)) return;
|
||||
|
||||
NativeAPI.UnhookEntityOutput(classname, outputName, subscriber.GetInputArgument());
|
||||
FunctionReference.Remove(subscriber.GetReferenceIdentifier());
|
||||
EntityOutputHooks.Remove(handler);
|
||||
}
|
||||
|
||||
public void HookSingleEntityOutput(CEntityInstance entityInstance, string outputName, EntityIO.EntityOutputHandler handler)
|
||||
{
|
||||
// since we wrap around the plugin handler we need to do this to ensure that the plugin callback is only called
|
||||
// if the entity instance is the same.
|
||||
EntityIO.EntityOutputHandler internalHandler = (string name, CEntityInstance activator, CEntityInstance caller, float delay) =>
|
||||
{
|
||||
if (caller == entityInstance)
|
||||
{
|
||||
handler(name, activator, caller, delay);
|
||||
}
|
||||
};
|
||||
|
||||
HookEntityOutput(entityInstance.DesignerName, outputName, internalHandler);
|
||||
|
||||
// because of ^ we would not be able to unhook since we passed the 'internalHandler' and that's what is being stored, not the original handler
|
||||
// but the plugin could only pass the original handler for unhooking.
|
||||
// (this dictionary does not needed to be cleared on dispose as it has no unmanaged reference and those are already being disposed, but on map end)
|
||||
// (the internal class is needed to be able to remove them on map start)
|
||||
EntitySingleOutputHooks[handler] = new EntityIO.EntityOutputCallback(entityInstance.DesignerName, outputName, internalHandler);
|
||||
}
|
||||
|
||||
public void UnhookSingleEntityOutput(CEntityInstance entityInstance, string outputName, EntityIO.EntityOutputHandler handler)
|
||||
{
|
||||
UnhookSingleEntityOutputInternal(entityInstance.DesignerName, outputName, handler);
|
||||
}
|
||||
|
||||
private void UnhookSingleEntityOutputInternal(string classname, string outputName, EntityIO.EntityOutputHandler handler)
|
||||
{
|
||||
if (!EntitySingleOutputHooks.TryGetValue(handler, out var internalHandler)) return;
|
||||
|
||||
UnhookEntityOutput(classname, outputName, internalHandler.Handler);
|
||||
EntitySingleOutputHooks.Remove(handler);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
@@ -464,6 +548,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
subscriber.Dispose();
|
||||
}
|
||||
|
||||
foreach (var subscriber in EntityOutputHooks.Values)
|
||||
{
|
||||
subscriber.Dispose();
|
||||
}
|
||||
|
||||
foreach (var timer in Timers)
|
||||
{
|
||||
timer.Kill();
|
||||
|
||||
@@ -126,26 +126,5 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// <param name="hostname">New hostname of the server</param>
|
||||
[ListenerName("OnHostNameChanged")]
|
||||
public delegate void OnHostNameChanged(string hostname);
|
||||
|
||||
/// <summary>
|
||||
/// Called before the server enters fatal shutdown.
|
||||
/// </summary>
|
||||
[ListenerName("OnPreFatalShutdown")]
|
||||
public delegate void OnServerPreFatalShutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the server is in a loading stage.
|
||||
/// </summary>
|
||||
/// <param name="frameTime"></param>
|
||||
[ListenerName("OnUpdateWhenNotInGame")]
|
||||
public delegate void OnUpdateWhenNotInGame(float frameTime);
|
||||
|
||||
/// <summary>
|
||||
/// Called before the world updates.
|
||||
/// This seems to be called even when the server is hibernating.
|
||||
/// </summary>
|
||||
/// <param name="simulating"><see langword="true"/> if simulating, <see langword="false"/> otherwise</param>
|
||||
[ListenerName("OnServerPreWorldUpdate")]
|
||||
public delegate void OnServerPreWorldUpdate(bool simulating);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
@@ -52,15 +50,13 @@ public partial class CCSPlayerController
|
||||
{
|
||||
VirtualFunctions.ClientPrint(this.Handle, HudDestination.Center, message, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void PrintToCenterHtml(string message) => PrintToCenterHtml(message, 5);
|
||||
|
||||
public void PrintToCenterHtml(string message, int duration)
|
||||
public void PrintToCenterHtml(string message)
|
||||
{
|
||||
var @event = new EventShowSurvivalRespawnStatus(true)
|
||||
{
|
||||
LocToken = message,
|
||||
Duration = duration,
|
||||
Duration = 5,
|
||||
Userid = this
|
||||
};
|
||||
@event.FireEventToClient(this);
|
||||
@@ -144,57 +140,6 @@ public partial class CCSPlayerController
|
||||
team);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a ConVar value for given player
|
||||
/// </summary>
|
||||
/// <param name="conVar">Name of the convar to retrieve</param>
|
||||
/// <returns>ConVar string value</returns>
|
||||
public string GetConVarValue(string conVar)
|
||||
{
|
||||
return NativeAPI.GetClientConvarValue(this.Slot, conVar);
|
||||
}
|
||||
|
||||
public string GetConVarValue(ConVar? conVar)
|
||||
{
|
||||
if (conVar == null)
|
||||
{
|
||||
throw new Exception("Invalid convar passed to 'GetConVarValue'");
|
||||
}
|
||||
|
||||
return GetConVarValue(conVar.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a ConVar value on a fake client (bot).
|
||||
/// </summary>
|
||||
/// <param name="conVar">Console variable name</param>
|
||||
/// <param name="value">String value to set</param>
|
||||
/// <exception cref="InvalidOperationException">Player is not a bot</exception>
|
||||
public void SetFakeClientConVar(string conVar, string value)
|
||||
{
|
||||
if (!IsBot)
|
||||
{
|
||||
throw new InvalidOperationException("'SetFakeClientConVar' can only be called for fake clients (bots)");
|
||||
}
|
||||
|
||||
NativeAPI.SetFakeClientConvarValue(this.Slot, conVar, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="SetFakeClientConVar(string,string)"/>
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException"><paramref name="conVar"/> is <see langword="null"/></exception>
|
||||
/// <inheritdoc cref="SetFakeClientConVar(string,string)" select="exception"/>
|
||||
public void SetFakeClientConVar(ConVar conVar, string value)
|
||||
{
|
||||
if (conVar == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid convar passed to 'SetFakeClientConVar'");
|
||||
}
|
||||
|
||||
SetFakeClientConVar(conVar.Name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active pawns button state. Will work even if the player is dead or observing.
|
||||
/// </summary>
|
||||
@@ -218,20 +163,4 @@ public partial class CCSPlayerController
|
||||
return (SteamID)authorizedSteamId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the IP address (and possibly port) of this player.
|
||||
/// <remarks>Returns 127.0.0.1 if the player is a bot.</remarks>
|
||||
/// </summary>
|
||||
public string? IpAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.IsValid) return null;
|
||||
var ipAddress = NativeAPI.GetPlayerIpAddress(this.Slot);
|
||||
if (string.IsNullOrWhiteSpace(ipAddress)) return null;
|
||||
|
||||
return ipAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,12 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
|
||||
public bool Equals(CEntityInstance? other)
|
||||
{
|
||||
return this.EntityHandle.Equals(other?.EntityHandle);
|
||||
return this.EntityHandle == other?.EntityHandle;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is CEntityInstance other && Equals(other);
|
||||
return ReferenceEquals(this, obj) || obj is CEntityInstance other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
|
||||
@@ -73,14 +73,11 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
|
||||
_fileWatcher.Deleted += async (s, e) =>
|
||||
{
|
||||
Server.NextWorldUpdate(() =>
|
||||
if (e.FullPath == path)
|
||||
{
|
||||
if (e.FullPath == path)
|
||||
{
|
||||
_logger.LogInformation("Plugin {Name} has been deleted, unloading...", Plugin.ModuleName);
|
||||
Unload(true);
|
||||
}
|
||||
});
|
||||
_logger.LogInformation("Plugin {Name} has been deleted, unloading...", Plugin.ModuleName);
|
||||
Unload(true);
|
||||
}
|
||||
};
|
||||
|
||||
_fileWatcher.Filter = "*.dll";
|
||||
@@ -91,14 +88,11 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
|
||||
private Task OnReloadedAsync(object sender, PluginReloadedEventArgs eventargs)
|
||||
{
|
||||
Server.NextWorldUpdate(() =>
|
||||
{
|
||||
_logger.LogInformation("Reloading plugin {Name}", Plugin.ModuleName);
|
||||
Loader = eventargs.Loader;
|
||||
Unload(hotReload: true);
|
||||
Load(hotReload: true);
|
||||
});
|
||||
|
||||
_logger.LogInformation("Reloading plugin {Name}", Plugin.ModuleName);
|
||||
Loader = eventargs.Loader;
|
||||
Unload(hotReload: true);
|
||||
Load(hotReload: true);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// The server console should have access to all commands, regardless of groups.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.Groups.IsSupersetOf(groups) ?? false;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
AddPlayerToGroup((SteamID)player.AuthorizedSteamID, groups);
|
||||
AddPlayerToGroup((SteamID)player.SteamID, groups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,7 +161,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
RemovePlayerFromGroup((SteamID)player.AuthorizedSteamID, true, groups);
|
||||
RemovePlayerFromGroup((SteamID)player.SteamID, true, groups);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData(player.AuthorizedSteamID);
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.Flags.IsSupersetOf(flags) ?? false;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.CommandOverrides.ContainsKey(command) ?? false;
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return true;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
|
||||
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
|
||||
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
|
||||
return playerData?.CommandOverrides.GetValueOrDefault(command) ?? false;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
// The server console should have access to all commands, regardless of permissions.
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
|
||||
SetPlayerCommandOverride((SteamID)player.AuthorizedSteamID, command, state);
|
||||
SetPlayerCommandOverride((SteamID)player.SteamID, command, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -235,7 +235,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
AddPlayerPermissions((SteamID)player.AuthorizedSteamID, flags);
|
||||
AddPlayerPermissions((SteamID)player.SteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -278,7 +278,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
RemovePlayerPermissions((SteamID)player.AuthorizedSteamID, flags);
|
||||
RemovePlayerPermissions((SteamID)player.SteamID, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -306,7 +306,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
ClearPlayerPermissions((SteamID)player.AuthorizedSteamID);
|
||||
ClearPlayerPermissions((SteamID)player.SteamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -335,7 +335,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
if (player == null) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
|
||||
|
||||
SetPlayerImmunity((SteamID)player.AuthorizedSteamID, value);
|
||||
SetPlayerImmunity((SteamID)player.SteamID, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -366,10 +366,10 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
if (target == null) return false;
|
||||
if (!target.IsValid || target.Connected != PlayerConnectedState.PlayerConnected) return false;
|
||||
|
||||
var callerData = GetPlayerAdminData((SteamID)caller.AuthorizedSteamID);
|
||||
var callerData = GetPlayerAdminData((SteamID)caller.SteamID);
|
||||
if (callerData == null) return false;
|
||||
|
||||
var targetData = GetPlayerAdminData((SteamID)target.AuthorizedSteamID);
|
||||
var targetData = GetPlayerAdminData((SteamID)target.SteamID);
|
||||
if (targetData == null) return true;
|
||||
|
||||
return callerData.Immunity >= targetData.Immunity;
|
||||
|
||||
@@ -31,8 +31,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
// If we have a command in the "command_overrides" section in "configs/admins.json",
|
||||
// we skip the checks below and just return the defined value.
|
||||
if (caller?.AuthorizedSteamID == null) return false;
|
||||
var adminData = AdminManager.GetPlayerAdminData(caller.AuthorizedSteamID);
|
||||
var adminData = AdminManager.GetPlayerAdminData((SteamID)caller.SteamID);
|
||||
if (adminData == null) return false;
|
||||
if (adminData.CommandOverrides.ContainsKey(Command))
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
var groupPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.GroupPermissionChar));
|
||||
var userPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.UserPermissionChar));
|
||||
|
||||
var adminData = AdminManager.GetPlayerAdminData(caller.AuthorizedSteamID);
|
||||
var adminData = AdminManager.GetPlayerAdminData((SteamID)caller.SteamID);
|
||||
if (adminData == null) return false;
|
||||
return (groupPermissions.Intersect(adminData.Groups).Count() + userPermissions.Intersect(adminData.Flags).Count()) > 0;
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the instance types for a Steam account.
|
||||
/// </summary>
|
||||
public enum SteamAccountInstance
|
||||
{
|
||||
/// <summary>
|
||||
/// Invalid instance.
|
||||
/// </summary>
|
||||
Invalid = -1,
|
||||
|
||||
/// <summary>
|
||||
/// All instances.
|
||||
/// </summary>
|
||||
All = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Desktop instance.
|
||||
/// </summary>
|
||||
Desktop = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Console instance.
|
||||
/// </summary>
|
||||
Console = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Web instance.
|
||||
/// </summary>
|
||||
Web = 4
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the types of Steam accounts.
|
||||
/// </summary>
|
||||
public enum SteamAccountType
|
||||
{
|
||||
/// <summary>
|
||||
/// Invalid account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "I")]
|
||||
Invalid = 0,
|
||||
/// <summary>
|
||||
/// Individual account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "U")]
|
||||
Individual,
|
||||
/// <summary>
|
||||
/// MultiSeat account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "M")]
|
||||
MultiSeat,
|
||||
/// <summary>
|
||||
/// Game Server account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "G")]
|
||||
GameServer,
|
||||
/// <summary>
|
||||
/// Anonymous Game Server account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "A")]
|
||||
AnonGameServer,
|
||||
/// <summary>
|
||||
/// Pending account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "P")]
|
||||
Pending,
|
||||
/// <summary>
|
||||
/// Content Server account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "C")]
|
||||
ContentServer,
|
||||
/// <summary>
|
||||
/// Clan account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "g")]
|
||||
Clan,
|
||||
/// <summary>
|
||||
/// Chat account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "T")]
|
||||
Chat,
|
||||
/// <summary>
|
||||
/// Console user account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "c")]
|
||||
ConsoleUser,
|
||||
/// <summary>
|
||||
/// P2P Super Seeder account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = " ")]
|
||||
P2PSuperSeeder,
|
||||
/// <summary>
|
||||
/// Anonymous user account type.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "a")]
|
||||
AnonUser,
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
namespace CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the universe of a Steam account.
|
||||
/// </summary>
|
||||
public enum SteamAccountUniverse
|
||||
{
|
||||
/// <summary>
|
||||
/// Individual / unspecified universe.
|
||||
/// </summary>
|
||||
Unspecified = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Public universe.
|
||||
/// </summary>
|
||||
Public = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Beta universe.
|
||||
/// </summary>
|
||||
Beta = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Internal universe.
|
||||
/// </summary>
|
||||
Internal = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Development universe.
|
||||
/// </summary>
|
||||
Dev = 4,
|
||||
}
|
||||
39
managed/CounterStrikeSharp.API/Modules/Entities/EntityIO.cs
Normal file
39
managed/CounterStrikeSharp.API/Modules/Entities/EntityIO.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||
*/
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities
|
||||
{
|
||||
public class EntityIO
|
||||
{
|
||||
public delegate void EntityOutputHandler(string name, CEntityInstance activator, CEntityInstance caller, float delay);
|
||||
|
||||
internal class EntityOutputCallback
|
||||
{
|
||||
public string Classname;
|
||||
|
||||
public string Output;
|
||||
|
||||
public EntityOutputHandler Handler;
|
||||
|
||||
public EntityOutputCallback(string classname, string output, EntityOutputHandler handler)
|
||||
{
|
||||
Classname = classname;
|
||||
Output = output;
|
||||
Handler = handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using System;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Entities
|
||||
{
|
||||
@@ -13,7 +12,6 @@ namespace CounterStrikeSharp.API.Modules.Entities
|
||||
|
||||
public static explicit operator SteamID(ulong u) => new(u);
|
||||
public static explicit operator SteamID(string s) => new(s);
|
||||
|
||||
ulong ParseId(string id)
|
||||
{
|
||||
var parts = id.Split(':');
|
||||
@@ -36,7 +34,7 @@ namespace CounterStrikeSharp.API.Modules.Entities
|
||||
|
||||
public string SteamId3
|
||||
{
|
||||
get => $"[{EnumUtils.GetEnumMemberAttributeValue(AccountType)}:{(int)AccountUniverse}:{SteamId64 - Base}]";
|
||||
get => $"[U:1:{SteamId64 - Base}]";
|
||||
set => SteamId64 = ParseId3(value);
|
||||
}
|
||||
|
||||
@@ -46,54 +44,20 @@ namespace CounterStrikeSharp.API.Modules.Entities
|
||||
set => SteamId64 = (ulong)value + Base;
|
||||
}
|
||||
|
||||
public int AccountId => (int)((SteamId64 >> 0) & 0xFFFFFFFF);
|
||||
|
||||
public SteamAccountInstance AccountInstance =>
|
||||
(SteamAccountInstance)((SteamId64 >> 32) & 0xFFFFF);
|
||||
|
||||
public SteamAccountType AccountType =>
|
||||
(SteamAccountType)((SteamId64 >> 52) & 0xF);
|
||||
|
||||
public SteamAccountUniverse AccountUniverse =>
|
||||
(SteamAccountUniverse)((SteamId64 >> 56) & 0xF);
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
if (AccountUniverse == SteamAccountUniverse.Unspecified
|
||||
|| AccountType == SteamAccountType.Invalid
|
||||
|| AccountInstance == SteamAccountInstance.Invalid)
|
||||
return false;
|
||||
if (AccountType == SteamAccountType.Individual
|
||||
&& (AccountId == 0 || AccountInstance != SteamAccountInstance.Desktop))
|
||||
return false;
|
||||
if (AccountType == SteamAccountType.Clan
|
||||
&& (AccountId == 0 || AccountInstance != SteamAccountInstance.All))
|
||||
return false;
|
||||
if (AccountType == SteamAccountType.GameServer && AccountId == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString() => $"[SteamID {SteamId64}, {SteamId2}, {SteamId3}]";
|
||||
|
||||
public Uri ToCommunityUrl()
|
||||
{
|
||||
string url = string.Empty;
|
||||
if (AccountType == SteamAccountType.Individual)
|
||||
url = "https://steamcommunity.com/profiles/" + SteamId64;
|
||||
if (AccountType == SteamAccountType.Clan)
|
||||
url = "https://steamcommunity.com/gid/" + SteamId64;
|
||||
return new Uri(url);
|
||||
}
|
||||
|
||||
public bool Equals(SteamID? other)
|
||||
{
|
||||
return other != null && SteamId64 == other.SteamId64;
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return SteamId64 == other.SteamId64;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj?.GetType() != this.GetType()) return false;
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((SteamID)obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ public partial class VirtualFunctionVoid<TArg1, TArg2, TArg3>
|
||||
{
|
||||
this.Function = VirtualFunction.CreateVoid<TArg1, TArg2, TArg3>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionVoid(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.CreateVoid<TArg1, TArg2, TArg3>(signature, binarypath);
|
||||
|
||||
@@ -99,7 +99,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TResult>
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TResult>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionWithReturn(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TResult>(signature, binarypath);
|
||||
@@ -124,7 +124,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TResu
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TResult>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionWithReturn(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TResult>(signature, binarypath);
|
||||
@@ -149,7 +149,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TResult>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionWithReturn(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TResult>(signature, binarypath);
|
||||
@@ -174,7 +174,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionWithReturn(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>(signature, binarypath);
|
||||
@@ -199,7 +199,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionWithReturn(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>(signature, binarypath);
|
||||
@@ -224,7 +224,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionWithReturn(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult>(signature, binarypath);
|
||||
@@ -249,7 +249,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionWithReturn(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult>(signature, binarypath);
|
||||
@@ -274,7 +274,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult>(signature);
|
||||
}
|
||||
|
||||
|
||||
public VirtualFunctionWithReturn(string signature, string binarypath)
|
||||
{
|
||||
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult>(signature, binarypath);
|
||||
|
||||
@@ -62,12 +62,17 @@ public class CHandle<T> : IEquatable<CHandle<T>> where T : NativeEntity
|
||||
|
||||
public bool Equals(CHandle<T>? other)
|
||||
{
|
||||
return other != null && Raw == other.Raw;
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Raw == other.Raw;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return Equals(obj as CHandle<T>);
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((CHandle<T>)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
|
||||
@@ -45,28 +45,12 @@ namespace CounterStrikeSharp.API
|
||||
// Currently only used to keep the delegate from being garbage collected
|
||||
private static List<Action> nextFrameTasks = new List<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Queue a task to be executed on the next game frame.
|
||||
/// <remarks>Does not execute if the server is hibernating.</remarks>
|
||||
/// </summary>
|
||||
public static void NextFrame(Action task)
|
||||
{
|
||||
nextFrameTasks.Add(task);
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(task);
|
||||
NativeAPI.QueueTaskForNextFrame(ptr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a task to be executed on the next pre world update.
|
||||
/// <remarks>Executes if the server is hibernating.</remarks>
|
||||
/// </summary>
|
||||
/// <param name="task"></param>
|
||||
public static void NextWorldUpdate(Action task)
|
||||
{
|
||||
nextFrameTasks.Add(task);
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(task);
|
||||
NativeAPI.QueueTaskForNextWorldUpdate(ptr);
|
||||
}
|
||||
|
||||
public static void PrintToChatAll(string message)
|
||||
{
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace CounterStrikeSharp.API
|
||||
|
||||
public static CCSPlayerController? GetPlayerFromSteamId(ulong steamId)
|
||||
{
|
||||
return Utilities.GetPlayers().FirstOrDefault(player => player.AuthorizedSteamID == (SteamID)steamId);
|
||||
return Utilities.GetPlayers().FirstOrDefault(player => player.SteamID == steamId);
|
||||
}
|
||||
|
||||
public static TargetResult ProcessTargetString(string pattern, CCSPlayerController player)
|
||||
|
||||
@@ -22,8 +22,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithCommands", "..\examples
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithGameEventHandlers", "..\examples\WithGameEventHandlers\WithGameEventHandlers.csproj", "{3032F3FA-E20A-4581-9A08-2FB5FF1524F4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithDatabaseDapper", "..\examples\WithDatabaseDapper\WithDatabaseDapper.csproj", "{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -70,10 +68,6 @@ Global
|
||||
{3032F3FA-E20A-4581-9A08-2FB5FF1524F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3032F3FA-E20A-4581-9A08-2FB5FF1524F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3032F3FA-E20A-4581-9A08-2FB5FF1524F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{57E64289-5D69-4AA1-BEF0-D0D96A55EE8F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
@@ -83,6 +77,5 @@ Global
|
||||
{E497E40C-A7B4-41A7-A1C6-2EC6698FF3BF} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{EA2F596E-2236-4999-B476-B1FDA287674A} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{3032F3FA-E20A-4581-9A08-2FB5FF1524F4} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace TestPlugin
|
||||
SetupListeners();
|
||||
SetupCommands();
|
||||
SetupMenus();
|
||||
SetupEntityOutputHooks();
|
||||
|
||||
// ValveInterface provides pointers to loaded modules via Interface Name exposed from the engine (e.g. Source2Server001)
|
||||
var server = ValveInterface.Server;
|
||||
@@ -406,6 +407,14 @@ namespace TestPlugin
|
||||
});
|
||||
}
|
||||
|
||||
private void SetupEntityOutputHooks()
|
||||
{
|
||||
HookEntityOutput("weapon_knife", "OnPlayerPickup", (string name, CEntityInstance activator, CEntityInstance caller, float delay) =>
|
||||
{
|
||||
Logger.LogInformation("weapon_knife called OnPlayerPickup ({name}, {activator}, {caller}, {delay})", name, activator.DesignerName, caller.DesignerName, delay);
|
||||
});
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerConnect(EventPlayerConnect @event, GameEventInfo info)
|
||||
{
|
||||
@@ -544,5 +553,11 @@ namespace TestPlugin
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
[EntityOutputHook("weapon_knife", "OnPlayerPickup")]
|
||||
public void OnKnifePickup(string name, CEntityInstance activator, CEntityInstance caller, float delay)
|
||||
{
|
||||
Logger.LogInformation("[EntityOutputHook Attribute] weapon_knife called OnPlayerPickup ({name}, {activator}, {caller}, {delay})", name, activator.DesignerName, caller.DesignerName, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,6 @@ ChatManager chatManager;
|
||||
ServerManager serverManager;
|
||||
|
||||
GetLegacyGameEventListener_t* GetLegacyGameEventListener = nullptr;
|
||||
std::thread::id gameThreadId;
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#undef protected
|
||||
#undef private
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "ISmmAPI.h"
|
||||
#include "eiface.h"
|
||||
#include "iserver.h"
|
||||
@@ -109,7 +107,6 @@ extern CGameConfig* gameConfig;
|
||||
typedef IGameEventListener2 *GetLegacyGameEventListener_t(CPlayerSlot slot);
|
||||
|
||||
extern GetLegacyGameEventListener_t* GetLegacyGameEventListener;
|
||||
extern std::thread::id gameThreadId;
|
||||
|
||||
void Initialize();
|
||||
// Should only be called within the active game loop (i e map should be loaded
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
|
||||
#include "core/managers/entity_manager.h"
|
||||
#include "core/gameconfig.h"
|
||||
#include "core/log.h"
|
||||
|
||||
#include <funchook.h>
|
||||
|
||||
#include <public/eiface.h>
|
||||
#include "scripting/callback_manager.h"
|
||||
@@ -30,6 +34,19 @@ void EntityManager::OnAllInitialized() {
|
||||
on_entity_created_callback = globals::callbackManager.CreateCallback("OnEntityCreated");
|
||||
on_entity_deleted_callback = globals::callbackManager.CreateCallback("OnEntityDeleted");
|
||||
on_entity_parent_changed_callback = globals::callbackManager.CreateCallback("OnEntityParentChanged");
|
||||
|
||||
m_pFireOutputInternal = reinterpret_cast<FireOutputInternal>(
|
||||
modules::server->FindSignature(globals::gameConfig->GetSignature("CEntityIOOutput_FireOutputInternal")));
|
||||
|
||||
if (m_pFireOutputInternal == nullptr) {
|
||||
CSSHARP_CORE_ERROR("Failed to find signature for \'CEntityIOOutput_FireOutputInternal\'");
|
||||
return;
|
||||
}
|
||||
|
||||
auto m_hook = funchook_create();
|
||||
funchook_prepare(m_hook, (void**)&m_pFireOutputInternal, (void*)&DetourFireOutputInternal);
|
||||
funchook_install(m_hook, 0);
|
||||
|
||||
// Listener is added in ServerStartup as entity system is not initialised at this stage.
|
||||
}
|
||||
|
||||
@@ -79,4 +96,68 @@ void CEntityListener::OnEntityParentChanged(CEntityInstance *pEntity, CEntityIns
|
||||
}
|
||||
}
|
||||
|
||||
void EntityManager::HookEntityOutput(const char* szClassname, const char* szOutput,
|
||||
CallbackT fnCallback)
|
||||
{
|
||||
auto outputKey = OutputKey_t(szClassname, szOutput);
|
||||
counterstrikesharp::ScriptCallback* callback;
|
||||
|
||||
auto search = m_pHookMap.find(outputKey);
|
||||
if (search == m_pHookMap.end()) {
|
||||
callback = globals::callbackManager.CreateCallback("");
|
||||
m_pHookMap[outputKey] = callback;
|
||||
}
|
||||
else
|
||||
callback = search->second;
|
||||
|
||||
callback->AddListener(fnCallback);
|
||||
}
|
||||
|
||||
void EntityManager::UnhookEntityOutput(const char* szClassname, const char* szOutput,
|
||||
CallbackT fnCallback)
|
||||
{
|
||||
auto outputKey = OutputKey_t(szClassname, szOutput);
|
||||
counterstrikesharp::ScriptCallback* callback;
|
||||
|
||||
auto search = m_pHookMap.find(outputKey);
|
||||
if (search != m_pHookMap.end()) {
|
||||
auto callback = search->second;
|
||||
callback->RemoveListener(fnCallback);
|
||||
|
||||
if (!callback->GetFunctionCount()) {
|
||||
globals::callbackManager.ReleaseCallback(callback);
|
||||
m_pHookMap.erase(outputKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DetourFireOutputInternal(CEntityIOOutput* const pThis, CEntityInstance* pActivator,
|
||||
CEntityInstance* pCaller, const CVariant* const value, float flDelay)
|
||||
{
|
||||
if (pCaller) {
|
||||
CSSHARP_CORE_TRACE("[EntityManager][FireOutputHook] - {}, {}", pThis->m_pDesc->m_pName,
|
||||
pCaller->GetClassname());
|
||||
|
||||
auto outputKey = OutputKey_t(pCaller->GetClassname(), pThis->m_pDesc->m_pName);
|
||||
auto& hookMap = globals::entityManager.m_pHookMap;
|
||||
|
||||
auto search = hookMap.find(outputKey);
|
||||
if (search != hookMap.end()) {
|
||||
auto callback = search->second;
|
||||
|
||||
if (callback && callback->GetFunctionCount()) {
|
||||
callback->ScriptContext().Reset();
|
||||
callback->ScriptContext().Push(pThis->m_pDesc->m_pName);
|
||||
callback->ScriptContext().Push(pActivator);
|
||||
callback->ScriptContext().Push(pCaller);
|
||||
callback->ScriptContext().Push(flDelay);
|
||||
callback->Execute();
|
||||
}
|
||||
}
|
||||
} else
|
||||
CSSHARP_CORE_TRACE("[EntityManager][FireOutputHook] - {}, unknown caller", pThis->m_pDesc->m_pName);
|
||||
|
||||
m_pFireOutputInternal(pThis, pActivator, pCaller, value, flDelay);
|
||||
}
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
@@ -24,9 +24,16 @@
|
||||
#include "scripting/script_engine.h"
|
||||
#include "entitysystem.h"
|
||||
|
||||
// variant.h depends on ivscript.h, lets not include the whole thing
|
||||
DECLARE_POINTER_HANDLE(HSCRIPT);
|
||||
|
||||
#include <variant.h>
|
||||
|
||||
namespace counterstrikesharp {
|
||||
class ScriptCallback;
|
||||
|
||||
typedef std::pair<std::string, std::string> OutputKey_t;
|
||||
|
||||
class CEntityListener : public IEntityListener {
|
||||
void OnEntitySpawned(CEntityInstance *pEntity) override;
|
||||
void OnEntityCreated(CEntityInstance *pEntity) override;
|
||||
@@ -41,7 +48,10 @@ public:
|
||||
~EntityManager();
|
||||
void OnAllInitialized() override;
|
||||
void OnShutdown() override;
|
||||
void HookEntityOutput(const char* szClassname, const char* szOutput, CallbackT fnCallback);
|
||||
void UnhookEntityOutput(const char* szClassname, const char* szOutput, CallbackT fnCallback);
|
||||
CEntityListener entityListener;
|
||||
std::map<OutputKey_t, counterstrikesharp::ScriptCallback*> m_pHookMap;
|
||||
private:
|
||||
ScriptCallback *on_entity_spawned_callback;
|
||||
ScriptCallback *on_entity_created_callback;
|
||||
@@ -49,4 +59,58 @@ private:
|
||||
ScriptCallback *on_entity_parent_changed_callback;
|
||||
};
|
||||
|
||||
|
||||
enum EntityIOTargetType_t
|
||||
{
|
||||
ENTITY_IO_TARGET_INVALID = 0xFFFFFFFF,
|
||||
ENTITY_IO_TARGET_CLASSNAME = 0x0,
|
||||
ENTITY_IO_TARGET_CLASSNAME_DERIVES_FROM = 0x1,
|
||||
ENTITY_IO_TARGET_ENTITYNAME = 0x2,
|
||||
ENTITY_IO_TARGET_CONTAINS_COMPONENT = 0x3,
|
||||
ENTITY_IO_TARGET_SPECIAL_ACTIVATOR = 0x4,
|
||||
ENTITY_IO_TARGET_SPECIAL_CALLER = 0x5,
|
||||
ENTITY_IO_TARGET_EHANDLE = 0x6,
|
||||
ENTITY_IO_TARGET_ENTITYNAME_OR_CLASSNAME = 0x7,
|
||||
};
|
||||
|
||||
struct EntityIOConnectionDesc_t
|
||||
{
|
||||
string_t m_targetDesc;
|
||||
string_t m_targetInput;
|
||||
string_t m_valueOverride;
|
||||
CEntityHandle m_hTarget;
|
||||
EntityIOTargetType_t m_nTargetType;
|
||||
int32 m_nTimesToFire;
|
||||
float m_flDelay;
|
||||
};
|
||||
|
||||
struct EntityIOConnection_t : EntityIOConnectionDesc_t
|
||||
{
|
||||
bool m_bMarkedForRemoval;
|
||||
EntityIOConnection_t* m_pNext;
|
||||
};
|
||||
|
||||
struct EntityIOOutputDesc_t
|
||||
{
|
||||
const char* m_pName;
|
||||
uint32 m_nFlags;
|
||||
uint32 m_nOutputOffset;
|
||||
};
|
||||
|
||||
class CEntityIOOutput
|
||||
{
|
||||
public:
|
||||
void* vtable;
|
||||
EntityIOConnection_t* m_pConnections;
|
||||
EntityIOOutputDesc_t* m_pDesc;
|
||||
};
|
||||
|
||||
typedef void (*FireOutputInternal)(CEntityIOOutput* const, CEntityInstance*, CEntityInstance*,
|
||||
const CVariant* const, float);
|
||||
|
||||
static void DetourFireOutputInternal(CEntityIOOutput* const pThis, CEntityInstance* pActivator,
|
||||
CEntityInstance* pCaller, const CVariant* const value, float flDelay);
|
||||
|
||||
static FireOutputInternal m_pFireOutputInternal = nullptr;
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
@@ -103,6 +103,7 @@ bool EventManager::HookEvent(const char* szName, CallbackT fnCallback, bool bPos
|
||||
} else {
|
||||
if (!pHook->m_pPreHook) {
|
||||
pHook->m_pPreHook = globals::callbackManager.CreateCallback("");
|
||||
;
|
||||
}
|
||||
|
||||
pHook->m_pPreHook->AddListener(fnCallback);
|
||||
@@ -129,18 +130,19 @@ bool EventManager::UnhookEvent(const char* szName, CallbackT fnCallback, bool bP
|
||||
pCallback = pHook->m_pPreHook;
|
||||
}
|
||||
|
||||
pCallback->RemoveListener(fnCallback);
|
||||
|
||||
if (pCallback->GetFunctionCount() == 0) {
|
||||
globals::callbackManager.ReleaseCallback(pCallback);
|
||||
|
||||
if (bPost) {
|
||||
pHook->m_pPostHook = nullptr;
|
||||
} else {
|
||||
pHook->m_pPreHook = nullptr;
|
||||
}
|
||||
// Remove from function list
|
||||
if (pCallback == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bPost) {
|
||||
pHook->m_pPostHook = nullptr;
|
||||
} else {
|
||||
pHook->m_pPreHook = nullptr;
|
||||
}
|
||||
|
||||
// TODO: Clean up callback if theres noone left attached.
|
||||
|
||||
CSSHARP_CORE_TRACE("Unhooking event: {0} with callback pointer: {1}", szName, (void*)fnCallback);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -23,9 +23,6 @@ SH_DECL_HOOK1_void(ISource2Server, ServerHibernationUpdate, SH_NOATTRIB, 0, bool
|
||||
SH_DECL_HOOK0_void(ISource2Server, GameServerSteamAPIActivated, SH_NOATTRIB, 0);
|
||||
SH_DECL_HOOK0_void(ISource2Server, GameServerSteamAPIDeactivated, SH_NOATTRIB, 0);
|
||||
SH_DECL_HOOK1_void(ISource2Server, OnHostNameChanged, SH_NOATTRIB, 0, const char*);
|
||||
SH_DECL_HOOK0_void(ISource2Server, PreFatalShutdown, const, 0);
|
||||
SH_DECL_HOOK1_void(ISource2Server, UpdateWhenNotInGame, SH_NOATTRIB, 0, float);
|
||||
SH_DECL_HOOK1_void(ISource2Server, PreWorldUpdate, SH_NOATTRIB, 0, bool);
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
@@ -42,20 +39,11 @@ void ServerManager::OnAllInitialized() {
|
||||
SH_MEMBER(this, &ServerManager::GameServerSteamAPIDeactivated), true);
|
||||
SH_ADD_HOOK(ISource2Server, OnHostNameChanged, globals::server,
|
||||
SH_MEMBER(this, &ServerManager::OnHostNameChanged), true);
|
||||
SH_ADD_HOOK(ISource2Server, PreFatalShutdown, globals::server,
|
||||
SH_MEMBER(this, &ServerManager::PreFatalShutdown), true);
|
||||
SH_ADD_HOOK(ISource2Server, UpdateWhenNotInGame, globals::server,
|
||||
SH_MEMBER(this, &ServerManager::UpdateWhenNotInGame), true);
|
||||
SH_ADD_HOOK(ISource2Server, PreWorldUpdate, globals::server,
|
||||
SH_MEMBER(this, &ServerManager::PreWorldUpdate), true);
|
||||
|
||||
on_server_hibernation_update_callback = globals::callbackManager.CreateCallback("OnServerHibernationUpdate");
|
||||
on_server_steam_api_activated_callback = globals::callbackManager.CreateCallback("OnGameServerSteamAPIActivated");
|
||||
on_server_steam_api_deactivated_callback = globals::callbackManager.CreateCallback("OnGameServerSteamAPIDeactivated");
|
||||
on_server_hostname_changed_callback = globals::callbackManager.CreateCallback("OnHostNameChanged");
|
||||
on_server_pre_fatal_shutdown = globals::callbackManager.CreateCallback("OnPreFatalShutdown");
|
||||
on_server_update_when_not_in_game = globals::callbackManager.CreateCallback("OnUpdateWhenNotInGame");
|
||||
on_server_pre_world_update = globals::callbackManager.CreateCallback("OnServerPreWorldUpdate");
|
||||
}
|
||||
|
||||
void ServerManager::OnShutdown() {
|
||||
@@ -67,20 +55,11 @@ void ServerManager::OnShutdown() {
|
||||
SH_MEMBER(this, &ServerManager::GameServerSteamAPIDeactivated), true);
|
||||
SH_REMOVE_HOOK(ISource2Server, OnHostNameChanged, globals::server,
|
||||
SH_MEMBER(this, &ServerManager::OnHostNameChanged), true);
|
||||
SH_REMOVE_HOOK(ISource2Server, PreFatalShutdown, globals::server,
|
||||
SH_MEMBER(this, &ServerManager::PreFatalShutdown), true);
|
||||
SH_REMOVE_HOOK(ISource2Server, UpdateWhenNotInGame, globals::server,
|
||||
SH_MEMBER(this, &ServerManager::UpdateWhenNotInGame), true);
|
||||
SH_REMOVE_HOOK(ISource2Server, PreWorldUpdate, globals::server,
|
||||
SH_MEMBER(this, &ServerManager::PreWorldUpdate), true);
|
||||
|
||||
globals::callbackManager.ReleaseCallback(on_server_hibernation_update_callback);
|
||||
globals::callbackManager.ReleaseCallback(on_server_steam_api_activated_callback);
|
||||
globals::callbackManager.ReleaseCallback(on_server_steam_api_deactivated_callback);
|
||||
globals::callbackManager.ReleaseCallback(on_server_hostname_changed_callback);
|
||||
globals::callbackManager.ReleaseCallback(on_server_pre_fatal_shutdown);
|
||||
globals::callbackManager.ReleaseCallback(on_server_update_when_not_in_game);
|
||||
globals::callbackManager.ReleaseCallback(on_server_pre_world_update);
|
||||
}
|
||||
|
||||
void* ServerManager::GetEconItemSystem()
|
||||
@@ -88,11 +67,6 @@ void* ServerManager::GetEconItemSystem()
|
||||
return globals::server->GetEconItemSystem();
|
||||
}
|
||||
|
||||
bool ServerManager::IsPaused()
|
||||
{
|
||||
return globals::server->IsPaused();
|
||||
}
|
||||
|
||||
void ServerManager::ServerHibernationUpdate(bool bHibernating)
|
||||
{
|
||||
CSSHARP_CORE_TRACE("Server hibernation update {0}", bHibernating);
|
||||
@@ -143,58 +117,4 @@ void ServerManager::OnHostNameChanged(const char *pHostname)
|
||||
}
|
||||
}
|
||||
|
||||
void ServerManager::PreFatalShutdown()
|
||||
{
|
||||
CSSHARP_CORE_TRACE("Pre fatal shutdown");
|
||||
|
||||
auto callback = globals::serverManager.on_server_pre_fatal_shutdown;
|
||||
|
||||
if (callback && callback->GetFunctionCount()) {
|
||||
callback->ScriptContext().Reset();
|
||||
callback->Execute();
|
||||
}
|
||||
}
|
||||
|
||||
void ServerManager::UpdateWhenNotInGame(float flFrameTime)
|
||||
{
|
||||
CSSHARP_CORE_TRACE("Update when not in game {}", flFrameTime);
|
||||
|
||||
auto callback = globals::serverManager.on_server_update_when_not_in_game;
|
||||
|
||||
if (callback && callback->GetFunctionCount()) {
|
||||
callback->ScriptContext().Reset();
|
||||
callback->ScriptContext().Push(flFrameTime);
|
||||
callback->Execute();
|
||||
}
|
||||
}
|
||||
|
||||
void ServerManager::PreWorldUpdate(bool bSimulating)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_nextWorldUpdateTasksLock);
|
||||
|
||||
if (!m_nextWorldUpdateTasks.empty()) {
|
||||
CSSHARP_CORE_TRACE("Executing queued tasks of size: {0} at time {1}", m_nextWorldUpdateTasks.size(),
|
||||
globals::getGlobalVars()->curtime);
|
||||
|
||||
for (size_t i = 0; i < m_nextWorldUpdateTasks.size(); i++) {
|
||||
m_nextWorldUpdateTasks[i]();
|
||||
}
|
||||
|
||||
m_nextWorldUpdateTasks.clear();
|
||||
}
|
||||
|
||||
auto callback = globals::serverManager.on_server_pre_world_update;
|
||||
|
||||
if (callback && callback->GetFunctionCount()) {
|
||||
callback->ScriptContext().Reset();
|
||||
callback->ScriptContext().Push(bSimulating);
|
||||
callback->Execute();
|
||||
}
|
||||
}
|
||||
|
||||
void ServerManager::AddTaskForNextWorldUpdate(std::function<void()>&& task)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_nextWorldUpdateTasksLock);
|
||||
m_nextWorldUpdateTasks.push_back(std::forward<decltype(task)>(task));
|
||||
}
|
||||
} // namespace counterstrikesharp
|
||||
@@ -30,29 +30,17 @@ public:
|
||||
void OnAllInitialized() override;
|
||||
void OnShutdown() override;
|
||||
void* GetEconItemSystem();
|
||||
bool IsPaused();
|
||||
void AddTaskForNextWorldUpdate(std::function<void()> &&task);
|
||||
|
||||
private:
|
||||
void ServerHibernationUpdate(bool bHibernating);
|
||||
void GameServerSteamAPIActivated();
|
||||
void GameServerSteamAPIDeactivated();
|
||||
void OnHostNameChanged(const char *pHostname);
|
||||
void PreFatalShutdown();
|
||||
void UpdateWhenNotInGame(float flFrameTime);
|
||||
void PreWorldUpdate(bool bSimulating);
|
||||
|
||||
|
||||
ScriptCallback *on_server_hibernation_update_callback;
|
||||
ScriptCallback *on_server_steam_api_activated_callback;
|
||||
ScriptCallback *on_server_steam_api_deactivated_callback;
|
||||
ScriptCallback *on_server_hostname_changed_callback;
|
||||
ScriptCallback *on_server_pre_fatal_shutdown;
|
||||
ScriptCallback *on_server_update_when_not_in_game;
|
||||
ScriptCallback *on_server_pre_world_update;
|
||||
|
||||
std::vector<std::function<void()>> m_nextWorldUpdateTasks;
|
||||
std::mutex m_nextWorldUpdateTasksLock;
|
||||
};
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
@@ -41,17 +41,6 @@ DLL_EXPORT void InvokeNative(counterstrikesharp::fxNativeContext& context)
|
||||
if (context.nativeIdentifier == 0)
|
||||
return;
|
||||
|
||||
if (context.nativeIdentifier != counterstrikesharp::hash_string_const("QUEUE_TASK_FOR_NEXT_FRAME") &&
|
||||
context.nativeIdentifier != counterstrikesharp::hash_string_const("QUEUE_TASK_FOR_NEXT_WORLD_UPDATE") &&
|
||||
counterstrikesharp::globals::gameThreadId != std::this_thread::get_id())
|
||||
{
|
||||
counterstrikesharp::ScriptContextRaw scriptContext(context);
|
||||
scriptContext.ThrowNativeError("Invoked on a non-main thread");
|
||||
|
||||
CSSHARP_CORE_CRITICAL("Native {:x} was invoked on a non-main thread", context.nativeIdentifier);
|
||||
return;
|
||||
}
|
||||
|
||||
counterstrikesharp::ScriptEngine::InvokeNative(context);
|
||||
}
|
||||
|
||||
@@ -78,7 +67,6 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
|
||||
{
|
||||
PLUGIN_SAVEVARS();
|
||||
globals::ismm = ismm;
|
||||
globals::gameThreadId = std::this_thread::get_id();
|
||||
|
||||
Log::Init();
|
||||
|
||||
@@ -176,8 +164,6 @@ void CounterStrikeSharpMMPlugin::AllPluginsLoaded()
|
||||
|
||||
void CounterStrikeSharpMMPlugin::AddTaskForNextFrame(std::function<void()>&& task)
|
||||
{
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_nextTasksLock);
|
||||
m_nextTasks.push_back(std::forward<decltype(task)>(task));
|
||||
}
|
||||
|
||||
@@ -191,8 +177,6 @@ void CounterStrikeSharpMMPlugin::Hook_GameFrame(bool simulating, bool bFirstTick
|
||||
*/
|
||||
globals::timerSystem.OnGameFrame(simulating);
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_nextTasksLock);
|
||||
|
||||
if (m_nextTasks.empty())
|
||||
return;
|
||||
|
||||
@@ -241,4 +225,4 @@ const char* CounterStrikeSharpMMPlugin::GetURL()
|
||||
{
|
||||
return "https://github.com/roflmuffin/CounterStrikeSharp";
|
||||
}
|
||||
} // namespace counterstrikesharp
|
||||
} // namespace counterstrikesharp
|
||||
@@ -61,7 +61,6 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<std::function<void()>> m_nextTasks;
|
||||
std::mutex m_nextTasksLock;
|
||||
};
|
||||
|
||||
static ScriptCallback *on_activate_callback;
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
OnServerHibernationUpdate: isHibernating:bool
|
||||
OnGameServerSteamAPIActivated:
|
||||
OnGameServerSteamAPIDeactivated:
|
||||
OnHostNameChanged: hostname:string
|
||||
PreFatalShutdown:
|
||||
UpdateWhenNotInGame: frameTime:float
|
||||
PreWorldUpdate: simulating:bool
|
||||
OnHostNameChanged: hostname:string
|
||||
@@ -124,23 +124,6 @@ static void IssueClientCommand(ScriptContext& script_context)
|
||||
globals::engine->ClientCommand(CPlayerSlot(entity_index), command);
|
||||
}
|
||||
|
||||
static const char* GetClientConVarValue(ScriptContext& script_context)
|
||||
{
|
||||
auto playerSlot = script_context.GetArgument<int>(0);
|
||||
auto convarName = script_context.GetArgument<const char*>(1);
|
||||
|
||||
return globals::engine->GetClientConVarValue(CPlayerSlot(playerSlot), convarName);
|
||||
}
|
||||
|
||||
static void SetFakeClientConVarValue(ScriptContext& script_context)
|
||||
{
|
||||
auto playerSlot = script_context.GetArgument<int>(0);
|
||||
auto convarName = script_context.GetArgument<const char*>(1);
|
||||
auto convarValue = script_context.GetArgument<const char*>(2);
|
||||
|
||||
globals::engine->SetFakeClientConVarValue(CPlayerSlot(playerSlot), convarName, convarValue);
|
||||
}
|
||||
|
||||
ConVar* FindConVar(ScriptContext& script_context)
|
||||
{
|
||||
auto name = script_context.GetArgument<const char*>(0);
|
||||
@@ -180,7 +163,5 @@ REGISTER_NATIVES(commands, {
|
||||
ScriptEngine::RegisterNativeHandler("SET_CONVAR_STRING_VALUE", SetConVarStringValue);
|
||||
|
||||
ScriptEngine::RegisterNativeHandler("ISSUE_CLIENT_COMMAND", IssueClientCommand);
|
||||
ScriptEngine::RegisterNativeHandler("GET_CLIENT_CONVAR_VALUE", GetClientConVarValue);
|
||||
ScriptEngine::RegisterNativeHandler("SET_FAKE_CLIENT_CONVAR_VALUE", SetFakeClientConVarValue);
|
||||
})
|
||||
} // namespace counterstrikesharp
|
||||
|
||||
@@ -8,6 +8,4 @@ COMMAND_GET_COMMAND_STRING: command:pointer -> string
|
||||
COMMAND_GET_ARG_BY_INDEX: command:pointer,index:int -> string
|
||||
ISSUE_CLIENT_COMMAND: clientIndex:int,command:string -> void
|
||||
FIND_CONVAR: name:string -> pointer
|
||||
SET_CONVAR_STRING_VALUE: convar:pointer,value:string -> void
|
||||
GET_CLIENT_CONVAR_VALUE: clientIndex:int,convarName:string -> string
|
||||
SET_FAKE_CLIENT_CONVAR_VALUE: clientIndex:int,convarName:string,convarValue:string -> void
|
||||
SET_CONVAR_STRING_VALUE: convar:pointer,value:string -> void
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "core/memory.h"
|
||||
#include "core/log.h"
|
||||
#include "core/function.h"
|
||||
#include "core/managers/server_manager.h"
|
||||
// clang-format on
|
||||
|
||||
#if _WIN32
|
||||
@@ -218,14 +217,6 @@ void QueueTaskForNextFrame(ScriptContext& script_context)
|
||||
globals::mmPlugin->AddTaskForNextFrame([func]() { reinterpret_cast<voidfunc*>(func)(); });
|
||||
}
|
||||
|
||||
void QueueTaskForNextWorldUpdate(ScriptContext& script_context)
|
||||
{
|
||||
auto func = script_context.GetArgument<void*>(0);
|
||||
|
||||
typedef void(voidfunc)(void);
|
||||
globals::serverManager.AddTaskForNextWorldUpdate([func]() { reinterpret_cast<voidfunc*>(func)(); });
|
||||
}
|
||||
|
||||
enum InterfaceType
|
||||
{
|
||||
Engine,
|
||||
@@ -317,7 +308,6 @@ REGISTER_NATIVES(engine, {
|
||||
ScriptEngine::RegisterNativeHandler("TRACE_RAY", TraceRay);
|
||||
ScriptEngine::RegisterNativeHandler("GET_TICKED_TIME", GetTickedTime);
|
||||
ScriptEngine::RegisterNativeHandler("QUEUE_TASK_FOR_NEXT_FRAME", QueueTaskForNextFrame);
|
||||
ScriptEngine::RegisterNativeHandler("QUEUE_TASK_FOR_NEXT_WORLD_UPDATE", QueueTaskForNextWorldUpdate);
|
||||
ScriptEngine::RegisterNativeHandler("GET_VALVE_INTERFACE", GetValveInterface);
|
||||
ScriptEngine::RegisterNativeHandler("GET_COMMAND_PARAM_VALUE", GetCommandParamValue);
|
||||
ScriptEngine::RegisterNativeHandler("PRINT_TO_SERVER_CONSOLE", PrintToServerConsole);
|
||||
|
||||
@@ -21,7 +21,6 @@ TRACE_FILTER_PROXY_SET_SHOULD_HIT_ENTITY_CALLBACK: trace_filter:pointer, callbac
|
||||
NEW_TRACE_RESULT: -> pointer
|
||||
GET_TICKED_TIME: -> double
|
||||
QUEUE_TASK_FOR_NEXT_FRAME: callback:pointer -> void
|
||||
QUEUE_TASK_FOR_NEXT_WORLD_UPDATE: callback:pointer -> void
|
||||
GET_VALVE_INTERFACE: interfaceType:int, interfaceName:string -> pointer
|
||||
GET_COMMAND_PARAM_VALUE: param:string, dataType:DataType_t, defaultValue:any -> any
|
||||
PRINT_TO_SERVER_CONSOLE: msg:string -> void
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "core/memory.h"
|
||||
#include "core/log.h"
|
||||
#include "core/managers/player_manager.h"
|
||||
#include "core/managers/entity_manager.h"
|
||||
|
||||
#include <public/entity2/entitysystem.h>
|
||||
|
||||
@@ -132,15 +133,20 @@ unsigned long GetPlayerAuthorizedSteamID(ScriptContext& script_context) {
|
||||
return pSteamId->ConvertToUint64();
|
||||
}
|
||||
|
||||
const char* GetPlayerIpAddress(ScriptContext& script_context) {
|
||||
auto iSlot = script_context.GetArgument<int>(0);
|
||||
void HookEntityOutput(ScriptContext& script_context)
|
||||
{
|
||||
auto szClassname = script_context.GetArgument<const char*>(0);
|
||||
auto szOutput = script_context.GetArgument<const char*>(1);
|
||||
auto callback = script_context.GetArgument<CallbackT>(2);
|
||||
globals::entityManager.HookEntityOutput(szClassname, szOutput, callback);
|
||||
}
|
||||
|
||||
auto pPlayer = globals::playerManager.GetPlayerBySlot(iSlot);
|
||||
if (pPlayer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pPlayer->GetIpAddress();
|
||||
void UnhookEntityOutput(ScriptContext& script_context)
|
||||
{
|
||||
auto szClassname = script_context.GetArgument<const char*>(0);
|
||||
auto szOutput = script_context.GetArgument<const char*>(1);
|
||||
auto callback = script_context.GetArgument<CallbackT>(2);
|
||||
globals::entityManager.UnhookEntityOutput(szClassname, szOutput, callback);
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(entities, {
|
||||
@@ -156,6 +162,7 @@ REGISTER_NATIVES(entities, {
|
||||
ScriptEngine::RegisterNativeHandler("PRINT_TO_CONSOLE", PrintToConsole);
|
||||
ScriptEngine::RegisterNativeHandler("GET_FIRST_ACTIVE_ENTITY", GetFirstActiveEntity);
|
||||
ScriptEngine::RegisterNativeHandler("GET_PLAYER_AUTHORIZED_STEAMID", GetPlayerAuthorizedSteamID);
|
||||
ScriptEngine::RegisterNativeHandler("GET_PLAYER_IP_ADDRESS", GetPlayerIpAddress);
|
||||
ScriptEngine::RegisterNativeHandler("HOOK_ENTITY_OUTPUT", HookEntityOutput);
|
||||
ScriptEngine::RegisterNativeHandler("UNHOOK_ENTITY_OUTPUT", UnhookEntityOutput);
|
||||
})
|
||||
} // namespace counterstrikesharp
|
||||
@@ -9,4 +9,5 @@ IS_REF_VALID_ENTITY: entityRef:uint -> bool
|
||||
PRINT_TO_CONSOLE: index:int, message:string -> void
|
||||
GET_FIRST_ACTIVE_ENTITY: -> pointer
|
||||
GET_PLAYER_AUTHORIZED_STEAMID: slot:int -> uint64
|
||||
GET_PLAYER_IP_ADDRESS: slot:int -> string
|
||||
HOOK_ENTITY_OUTPUT: classname:string, outputName:string, callback:func -> void
|
||||
UNHOOK_ENTITY_OUTPUT: classname:string, outputName:string, callback:func -> void
|
||||
@@ -24,14 +24,8 @@ static void *GetEconItemSystem(ScriptContext& scriptContext) {
|
||||
return globals::serverManager.GetEconItemSystem();
|
||||
}
|
||||
|
||||
static bool IsServerPaused(ScriptContext& scriptContext)
|
||||
{
|
||||
return globals::serverManager.IsPaused();
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(server, {
|
||||
ScriptEngine::RegisterNativeHandler("GET_ECON_ITEM_SYSTEM", GetEconItemSystem);
|
||||
ScriptEngine::RegisterNativeHandler("IS_SERVER_PAUSED", IsServerPaused);
|
||||
})
|
||||
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
GET_ECON_ITEM_SYSTEM: -> pointer
|
||||
IS_SERVER_PAUSED: -> bool
|
||||
GET_ECON_ITEM_SYSTEM: -> pointer
|
||||
@@ -55,18 +55,6 @@ inline uint32_t hash_string(const char *string) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
constexpr uint32_t hash_string_const(char const (&string)[N])
|
||||
{
|
||||
unsigned long result = 5381;
|
||||
|
||||
for (size_t i = 0; i < N-1; i++) {
|
||||
result = ((result << 5) + result) ^ string[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct fxNativeContext {
|
||||
int numArguments;
|
||||
int numResults;
|
||||
|
||||
Reference in New Issue
Block a user