using Microsoft.Extensions.Logging;

namespace CounterStrikeSharp.API.Core.NativeEvents;

public class NativeEvent<T> where T : Delegate
{
    public string Name { get; }
    private readonly Func<ScriptContext, object[]> _handler;
    private readonly List<T> _hooks = new();

    public NativeEvent(string name, Func<ScriptContext, object[]> handler)
    {
        _handler = handler;
        Name = name;

        var functionReference = FunctionReference.Create(Invoke);
        NativeAPI.AddListener(name, functionReference);
    }

    private void Invoke(ScriptContext context)
    {
        var args = _handler.Invoke(context);

        foreach (var hook in _hooks)
        {
            try
            {
                hook.DynamicInvoke(args);
            }
            catch (Exception e)
            {
                Application.Instance.Logger.LogError("Error while handling native event {name}: {e}", Name, e);
            }
        }
    }

    public void Hook(T handler)
    {
        _hooks.Add(handler);
    }

    public void Unhook(T handler)
    {
        _hooks.Remove(handler);
    }
}



using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Utils;

namespace CounterStrikeSharp.API.Core.NativeEvents;

public static class NativeEvents
{
    public delegate void OnEntitySpawnedDelegate(CEntityInstance entity);

    public static readonly NativeEvent<OnEntitySpawnedDelegate> OnEntitySpawned =
        new("OnEntitySpawned",
            (context) => [context.GetArgument<CEntityInstance>(0)]);

    public delegate void OnEntityCreatedDelegate(CEntityInstance entity);

    public static readonly NativeEvent<OnEntityCreatedDelegate> OnEntityCreated =
        new("OnEntityCreated",
            (context) => [context.GetArgument<CEntityInstance>(0)]);

    public delegate void OnEntityDeletedDelegate(CEntityInstance entity);

    public static readonly NativeEvent<OnEntityDeletedDelegate> OnEntityDeleted =
        new("OnEntityDeleted",
            (context) => [context.GetArgument<CEntityInstance>(0)]);

    public delegate void OnEntityParentChanged(CEntityInstance entity, CEntityInstance newParent);

    public static readonly NativeEvent<OnEntityParentChanged> OnEntityParentChangedEvent =
        new("OnEntityParentChanged",
            (context) => [context.GetArgument<CEntityInstance>(0), context.GetArgument<CEntityInstance>(1)]);

    public delegate void OnTick();

    public static readonly NativeEvent<OnTick> OnTickEvent =
        new("OnTick",
            (context) => Array.Empty<object>());

    public delegate void OnMapStart(string mapName);

    public static readonly NativeEvent<OnMapStart> OnMapStartEvent =
        new("OnMapStart",
            (context) => [context.GetArgument<string>(0)]);

    public delegate void OnMapEnd();

    public static readonly NativeEvent<OnMapEnd> OnMapEndEvent =
        new("OnMapEnd",
            (context) => Array.Empty<object>());

    public delegate void OnClientConnect(int playerSlot, string name, string ipAddress);

    public static readonly NativeEvent<OnClientConnect> OnClientConnectEvent =
        new("OnClientConnect",
            (context) => [context.GetArgument<int>(0), context.GetArgument<string>(1), context.GetArgument<string>(2)]);

    public delegate void OnClientConnected(int playerSlot);

    public static readonly NativeEvent<OnClientConnected> OnClientConnectedEvent =
        new("OnClientConnected",
            (context) => [context.GetArgument<int>(0)]);

    public delegate void OnClientPutInServer(int playerSlot);

    public static readonly NativeEvent<OnClientPutInServer> OnClientPutInServerEvent =
        new("OnClientPutInServer",
            (context) => [context.GetArgument<int>(0)]);

    public delegate void OnClientDisconnect(int playerSlot);

    public static readonly NativeEvent<OnClientDisconnect> OnClientDisconnectEvent =
        new("OnClientDisconnect",
            (context) => [context.GetArgument<int>(0)]);

    public delegate void OnClientDisconnectPost(int playerSlot);

    public static readonly NativeEvent<OnClientDisconnectPost> OnClientDisconnectPostEvent =
        new("OnClientDisconnectPost",
            (context) => [context.GetArgument<int>(0)]);

    public delegate void OnClientVoice(int playerSlot);

    public static readonly NativeEvent<OnClientVoice> OnClientVoiceEvent =
        new("OnClientVoice",
            (context) => [context.GetArgument<int>(0)]);

    public delegate void OnClientAuthorized(int playerSlot, SteamID steamId);

    public static readonly NativeEvent<OnClientAuthorized> OnClientAuthorizedEvent =
        new("OnClientAuthorized",
            (context) => [context.GetArgument<int>(0), new SteamID(context.GetArgument<ulong>(1))]);

    public delegate void OnServerHibernationUpdate(bool isHibernating);

    public static readonly NativeEvent<OnServerHibernationUpdate> OnServerHibernationUpdateEvent =
        new("OnServerHibernationUpdate",
            (context) => [context.GetArgument<bool>(0)]);

    public delegate void OnGameServerSteamAPIActivated();

    public static readonly NativeEvent<OnGameServerSteamAPIActivated> OnGameServerSteamAPIActivatedEvent =
        new("OnGameServerSteamAPIActivated",
            (context) => Array.Empty<object>());

    public delegate void OnGameServerSteamAPIDeactivated();

    public static readonly NativeEvent<OnGameServerSteamAPIDeactivated> OnGameServerSteamAPIDeactivatedEvent =
        new("OnGameServerSteamAPIDeactivated",
            (context) => Array.Empty<object>());

    public delegate void OnHostNameChanged(string hostname);

    public static readonly NativeEvent<OnHostNameChanged> OnHostNameChangedEvent =
        new("OnHostNameChanged",
            (context) => [context.GetArgument<string>(0)]);

    public delegate void OnServerPreFatalShutdown();

    public static readonly NativeEvent<OnServerPreFatalShutdown> OnServerPreFatalShutdownEvent =
        new("OnPreFatalShutdown",
            (context) => new object[] { });

    public delegate void OnUpdateWhenNotInGame(float frameTime);

    public static readonly NativeEvent<OnUpdateWhenNotInGame> OnUpdateWhenNotInGameEvent =
        new("OnUpdateWhenNotInGame",
            (context) => [context.GetArgument<float>(0)]);

    public delegate void OnServerPreWorldUpdate(bool simulating);

    public static readonly NativeEvent<OnServerPreWorldUpdate> OnServerPreWorldUpdateEvent =
        new("OnServerPreWorldUpdate",
            (context) => [context.GetArgument<bool>(0)]);

    public delegate void OnServerPrecacheResources(ResourceManifest manifest);

    public static readonly NativeEvent<OnServerPrecacheResources> OnServerPrecacheResourcesEvent =
        new("OnServerPrecacheResources",
            (context) => [context.GetArgument<ResourceManifest>(0)]);

    public delegate void CheckTransmit(CCSPlayerController player, ССheckTransmitInfo entities);

    public static readonly NativeEvent<CheckTransmit> CheckTransmitEvent =
        new("CheckTransmit",
            (context) => [Utilities.GetPlayerFromSlot(context.GetArgument<int>(0))!, context.GetArgument<ССheckTransmitInfo>(1)]);
}

make it better