mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-06 08:03:12 -08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64d1c0a9f4 | ||
|
|
a6de51c444 | ||
|
|
2535ac0575 | ||
|
|
bc61323315 | ||
|
|
241817b7f2 | ||
|
|
fbcdce34fc | ||
|
|
daf0d25f36 | ||
|
|
12485be29f | ||
|
|
983d91491d | ||
|
|
71507b1e25 | ||
|
|
cfe14b35fe | ||
|
|
5a6cdf0da3 | ||
|
|
a5399dd5fe | ||
|
|
ab996c34e9 | ||
|
|
6e2e25b96e | ||
|
|
1142c9f063 | ||
|
|
72178c9598 |
20
.github/workflows/issues-triage-removal.yml
vendored
Normal file
20
.github/workflows/issues-triage-removal.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Label new issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- milestoned
|
||||
|
||||
jobs:
|
||||
label_issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Remove label
|
||||
run: gh issue edit "$NUMBER" --remove-label "$LABELS"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
LABELS: untriaged
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -14,12 +14,12 @@
|
||||
[submodule "libraries/dyncall"]
|
||||
path = libraries/dyncall
|
||||
url = https://github.com/LWJGL-CI/dyncall
|
||||
[submodule "libraries/GameTracking-CS2"]
|
||||
path = libraries/GameTracking-CS2
|
||||
url = https://github.com/SteamDatabase/GameTracking-CS2
|
||||
[submodule "libraries/DynoHook"]
|
||||
path = libraries/DynoHook
|
||||
url = https://github.com/qubka/DynoHook
|
||||
[submodule "libraries/asmjit"]
|
||||
path = libraries/asmjit
|
||||
url = https://github.com/asmjit/asmjit
|
||||
[submodule "libraries/Protobufs"]
|
||||
path = libraries/Protobufs
|
||||
url = https://github.com/SteamDatabase/Protobufs
|
||||
|
||||
@@ -90,6 +90,8 @@ SET(SOURCE_FILES
|
||||
src/core/managers/voice_manager.cpp
|
||||
src/core/managers/voice_manager.h
|
||||
src/scripting/natives/natives_dynamichooks.cpp
|
||||
src/core/game_system.h
|
||||
src/core/game_system.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -101,8 +103,8 @@ if (LINUX)
|
||||
)
|
||||
endif()
|
||||
|
||||
set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs)
|
||||
file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/GameTracking-CS2/Protobufs/*.proto")
|
||||
set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/Protobufs/csgo)
|
||||
file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/Protobufs/csgo/*.proto")
|
||||
|
||||
## Generate protobuf source & headers
|
||||
if (LINUX)
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
},
|
||||
"CCSPlayerController_Respawn": {
|
||||
"offsets": {
|
||||
"windows": 245,
|
||||
"linux": 247
|
||||
"windows": 244,
|
||||
"linux": 246
|
||||
}
|
||||
},
|
||||
"CBasePlayerController_SetPawn": {
|
||||
@@ -206,5 +206,18 @@
|
||||
"windows": "\\x4C\\x89\\x4C\\x24\\x20\\x53\\x55\\x57\\x41\\x54\\x41\\x56\\x48\\x81\\xEC",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x49\\x89\\xD4\\x53\\x48\\x89\\xF3\\x48\\x83\\xEC\\x58"
|
||||
}
|
||||
},
|
||||
"IGameSystem_InitAllSystems_pFirst": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x8B\\x1D\\x2A\\x2A\\x2A\\x2A\\x48\\x85\\xDB\\x0F\\x84\\x2A\\x2A\\x2A\\x2A\\xBE\\x2A\\x2A\\x2A\\x2A\\x0F\\x1F\\x00\\x48\\x8B\\x7B\\x10",
|
||||
"linux": "\\x4C\\x8B\\x35\\x2A\\x2A\\x2A\\x2A\\x4D\\x85\\xF6\\x75\\x2D\\xE9\\x2A\\x2A\\x2A\\x2A\\x0F\\x1F\\x40\\x00\\x48\\x8B\\x05"
|
||||
}
|
||||
},
|
||||
"CEntityResourceManifest_AddResource": {
|
||||
"offsets": {
|
||||
"windows": 2,
|
||||
"linux": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
configs/addons/counterstrikesharp/shared/README.md
Normal file
1
configs/addons/counterstrikesharp/shared/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This folder should contain any shared APIs, in the same DLL structure as the plugins folder (MySharedApi/MySharedApi.dll)
|
||||
67
docfx/docs/features/shared-plugin-api.md
Normal file
67
docfx/docs/features/shared-plugin-api.md
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Shared Plugin API (Capabilities)
|
||||
description: How to add inter-plugin communication to CounterStrikeSharp plugins.
|
||||
---
|
||||
|
||||
# Shared Plugin API
|
||||
|
||||
How to expose and use shared plugin APIs between multiple plugins.
|
||||
|
||||
## Creating a Contract Library
|
||||
|
||||
Inter-plugin communication requires a contract/shared library that simply exposes the shape of the API, using simple interfaces. e.g.
|
||||
|
||||
```csharp
|
||||
public interface IBalanceHandler
|
||||
{
|
||||
decimal Balance { get; }
|
||||
|
||||
// These are just here to show that you can have methods on your shared types.
|
||||
// You could also add a Setter to the Balance property.
|
||||
public decimal Add(decimal amount);
|
||||
public decimal Subtract(decimal amount);
|
||||
}
|
||||
```
|
||||
|
||||
This library ideally should not contain any business logic, and simply define the schema for callers.
|
||||
|
||||
This library should be placed in the `shared` subfolder, in the same folder layout as the plugins folder. So if a contract DLL is named `MySharedApi.dll` its file path should be: `shared/MySharedApi/MySharedApi.dll`.
|
||||
|
||||
## Creating a Capability
|
||||
|
||||
A capability can be declared with a simple static variable in your plugin class. A `PlayerCapability` is a player specific capability, and a `PluginCapability` is generic functionality that a plugin can expose.
|
||||
|
||||
```csharp
|
||||
public static PlayerCapability<IBalanceHandler> BalanceCapability { get; } = new("myplugin:balance");
|
||||
|
||||
public static PluginCapability<IBalanceService> BalanceServiceCapability { get; } = new("myplugin:balance_service");
|
||||
```
|
||||
|
||||
For every plugin that wishes to use this new "Balance API", they must ensure they create a capability using the shared API interface (`IBalanceHandler`), as well as use the same name (`myplugin:balance`).
|
||||
|
||||
## Registering a Capability
|
||||
|
||||
If you are the plugin that is expected to provide the basis of the API (i.e. you are providing a currency/balance plugin which does nothing except store users balances), then you will need to provide the implementation that other callers will use. This is done through the use of static members on the `Capabilities` class:
|
||||
|
||||
```csharp
|
||||
// Player capabilities are given the calling player context
|
||||
Capabilities.RegisterPlayerCapability(BalanceCapability, player => new BalanceHandler(player));
|
||||
|
||||
// Plugin capabilities can simply return an instance of the interface
|
||||
Capabilities.RegisterPluginCapability(BalanceServiceCapability, () => new BalanceService());
|
||||
```
|
||||
|
||||
### Using Capabilities
|
||||
|
||||
To utilise a capability, simply call the `.Get()` method provided on the static capability you declared earlier, i.e.
|
||||
|
||||
```csharp
|
||||
var balance = BalanceCapability.Get(player);
|
||||
var balanceService = BalanceServiceCapability.Get();
|
||||
|
||||
if (balance == null) return;
|
||||
|
||||
balance.Add(500);
|
||||
```
|
||||
|
||||
This value _MUST_ be checked for null, as if there are no plugins providing implementations for a given capability, this method will return null, and you must handle this flow in your plugin.
|
||||
@@ -9,3 +9,6 @@
|
||||
|
||||
- name: Global Listeners
|
||||
href: global-listeners.md
|
||||
|
||||
- name: Shared Plugin API
|
||||
href: shared-plugin-api.md
|
||||
|
||||
5
docfx/examples/WithFakeConvars.md
Normal file
5
docfx/examples/WithFakeConvars.md
Normal file
@@ -0,0 +1,5 @@
|
||||
[!INCLUDE [WithFakeConvars](../../examples/WithFakeConvars/README.md)]
|
||||
|
||||
<a href="https://github.com/roflmuffin/CounterStrikeSharp/tree/main/examples/WithFakeConvars" class="btn btn-secondary">View project on Github <i class="bi bi-github"></i></a>
|
||||
|
||||
[!code-csharp[](../../examples/WithFakeConvars/WithFakeConvarsPlugin.cs)]
|
||||
11
docfx/examples/WithSharedTypes.md
Normal file
11
docfx/examples/WithSharedTypes.md
Normal file
@@ -0,0 +1,11 @@
|
||||
[!INCLUDE [WithSharedTypes](../../examples/WithSharedTypes/README.md)]
|
||||
|
||||
<a href="https://github.com/roflmuffin/CounterStrikeSharp/tree/main/examples/WithSharedTypes" class="btn btn-secondary">View project on Github <i class="bi bi-github"></i></a>
|
||||
|
||||
[!code-csharp[](../../examples/WithSharedTypes/WithSharedTypesPlugin.cs)]
|
||||
|
||||
[!INCLUDE [WithSharedTypesConsumer](../../examples/WithSharedTypesConsumer/README.md)]
|
||||
|
||||
<a href="https://github.com/roflmuffin/CounterStrikeSharp/tree/main/examples/WithSharedTypesConsumer" class="btn btn-secondary">View project on Github <i class="bi bi-github"></i></a>
|
||||
|
||||
[!code-csharp[](../../examples/WithSharedTypesConsumer/WithSharedTypesConsumerPlugin.cs)]
|
||||
@@ -3,6 +3,8 @@ items:
|
||||
href: HelloWorld.md
|
||||
- name: Commands
|
||||
href: WithCommands.md
|
||||
- name: Fake ConVars
|
||||
href: WithFakeConvars.md
|
||||
- name: Config
|
||||
href: WithConfig.md
|
||||
- name: Dependency Injection
|
||||
@@ -13,6 +15,8 @@ items:
|
||||
href: WithGameEventHandlers.md
|
||||
- name: Database (Dapper)
|
||||
href: WithDatabase.md
|
||||
- name: Shared Plugin Types
|
||||
href: WithSharedTypes.md
|
||||
- name: Translations
|
||||
href: WithTranslations.md
|
||||
- name: Voice Overrides
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace MySharedTypes.Contracts;
|
||||
|
||||
public interface IBalanceHandler
|
||||
{
|
||||
decimal Balance { get; }
|
||||
|
||||
// These are just here to show that you can have methods on your shared types.
|
||||
// You could also add a Setter to the Balance property.
|
||||
public decimal Add(decimal amount);
|
||||
public decimal Subtract(decimal amount);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace MySharedTypes.Contracts;
|
||||
|
||||
public interface IBalanceService
|
||||
{
|
||||
public void ClearAllBalances();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
9
examples/WithFakeConvars/ConVars.cs
Normal file
9
examples/WithFakeConvars/ConVars.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
|
||||
namespace WithFakeConvars;
|
||||
|
||||
public static class ConVars
|
||||
{
|
||||
// This convar is registered from the plugin instance but can be used anywhere.
|
||||
public static FakeConVar<int> ExampleStaticCvar = new("example_static", "An example static cvar");
|
||||
}
|
||||
21
examples/WithFakeConvars/EvenNumberValidator.cs
Normal file
21
examples/WithFakeConvars/EvenNumberValidator.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace WithFakeConvars;
|
||||
|
||||
// This is an example of a custom validator that checks if a number is even.
|
||||
public class EvenNumberValidator : IValidator<int>
|
||||
{
|
||||
public bool Validate(int value, out string? errorMessage)
|
||||
{
|
||||
if (value % 2 == 0)
|
||||
{
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "Value must be an even number";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
examples/WithFakeConvars/README.md
Normal file
2
examples/WithFakeConvars/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# With Fake Convars
|
||||
This is an example that shows how to register "fake" convars, which are actually console commands that track their internal state.
|
||||
12
examples/WithFakeConvars/WithFakeConvars.csproj
Normal file
12
examples/WithFakeConvars/WithFakeConvars.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
58
examples/WithFakeConvars/WithFakeConvarsPlugin.cs
Normal file
58
examples/WithFakeConvars/WithFakeConvarsPlugin.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace WithFakeConvars;
|
||||
|
||||
[MinimumApiVersion(175)]
|
||||
public class WithFakeConvarsPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: With Fake Convars";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A simple plugin that registers some console variables";
|
||||
|
||||
// FakeConVar is a class that can be used to create custom console variables.
|
||||
// You can specify a name, description, default value, and custom validators.
|
||||
public FakeConVar<bool> BoolCvar = new("example_bool", "An example boolean cvar", true);
|
||||
|
||||
// Range validator is an inbuilt validator that can be used to ensure that a value is within a certain range.
|
||||
public FakeConVar<int> ExampleIntCvar = new("example_int", "An example integer cvar", 10, flags: ConVarFlags.FCVAR_NONE, new RangeValidator<int>(0, 100));
|
||||
|
||||
public FakeConVar<float> ExampleFloatCvar = new("example_float", "An example float cvar", 10, flags: ConVarFlags.FCVAR_NONE, new RangeValidator<float>(5, 20));
|
||||
public FakeConVar<string> ExampleStringCvar = new("example_string", "An example string cvar", "default");
|
||||
|
||||
// Replicated, Cheat & Protected flags are supported.
|
||||
public FakeConVar<float> ExamplePublicCvar = new("example_public_float", "An example public float cvar", 5,
|
||||
ConVarFlags.FCVAR_REPLICATED);
|
||||
|
||||
// Can only be changed if sv_cheats is enabled.
|
||||
public FakeConVar<float> ExampleCheatCvar = new("example_cheat_float", "An example cheat float cvar", 5,
|
||||
ConVarFlags.FCVAR_CHEAT);
|
||||
|
||||
// Protected cvars do not output their value when queried.
|
||||
public FakeConVar<float> ExampleProtectedCvar = new("example_protected_float", "An example cheat float cvar", 5,
|
||||
ConVarFlags.FCVAR_PROTECTED);
|
||||
|
||||
// You can create your own custom validators by implementing the IValidator interface.
|
||||
public FakeConVar<int> ExampleEvenNumberCvar = new("example_even_number", "An example even number cvar", 0, flags: ConVarFlags.FCVAR_NONE, new EvenNumberValidator());
|
||||
|
||||
public FakeConVar<int> RequiresRestartCvar = new("example_requires_restart", "A cvar that requires a restart when changed");
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
// You can subscribe to the ValueChanged event to execute code when the value of a cvar changes.
|
||||
// In this example, we restart the game when the value of RequiresRestartCvar is greater than 5.
|
||||
RequiresRestartCvar.ValueChanged += (sender, value) =>
|
||||
{
|
||||
if (value > 5)
|
||||
{
|
||||
Server.ExecuteCommand("mp_restartgame 1");
|
||||
}
|
||||
};
|
||||
|
||||
RegisterFakeConVars(typeof(ConVars));
|
||||
}
|
||||
}
|
||||
33
examples/WithSharedTypes/BalanceHandler.cs
Normal file
33
examples/WithSharedTypes/BalanceHandler.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using MySharedTypes.Contracts;
|
||||
|
||||
namespace WithSharedTypes;
|
||||
|
||||
public class BalanceHandler : IBalanceHandler
|
||||
{
|
||||
private readonly CCSPlayerController _player;
|
||||
|
||||
// This could be a database, a file, or a dictionary like this.
|
||||
internal static readonly Dictionary<CCSPlayerController, decimal> Balances = new();
|
||||
|
||||
public BalanceHandler(CCSPlayerController player)
|
||||
{
|
||||
_player = player;
|
||||
}
|
||||
|
||||
public decimal Balance
|
||||
{
|
||||
get => Balances.TryGetValue(_player, out var balance) ? balance : 0;
|
||||
set => Balances[_player] = value;
|
||||
}
|
||||
|
||||
public decimal Add(decimal amount)
|
||||
{
|
||||
return Balance += amount;
|
||||
}
|
||||
|
||||
public decimal Subtract(decimal amount)
|
||||
{
|
||||
return Balance -= amount;
|
||||
}
|
||||
}
|
||||
11
examples/WithSharedTypes/BalanceService.cs
Normal file
11
examples/WithSharedTypes/BalanceService.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using MySharedTypes.Contracts;
|
||||
|
||||
namespace WithSharedTypes;
|
||||
|
||||
public class BalanceService : IBalanceService
|
||||
{
|
||||
public void ClearAllBalances()
|
||||
{
|
||||
BalanceHandler.Balances.Clear();
|
||||
}
|
||||
}
|
||||
5
examples/WithSharedTypes/README.md
Normal file
5
examples/WithSharedTypes/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# With Shared Types (Capabilities)
|
||||
|
||||
An example plugin that exposes a balance contract library, to use as a shared library between multiple plugins.
|
||||
|
||||
This allows one plugin to expose a capability for a player or plugin, and other plugins to use the exposed API.
|
||||
13
examples/WithSharedTypes/WithSharedTypes.csproj
Normal file
13
examples/WithSharedTypes/WithSharedTypes.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
<ProjectReference Include="..\MySharedTypes.Contracts\MySharedTypes.Contracts\MySharedTypes.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
57
examples/WithSharedTypes/WithSharedTypesPlugin.cs
Normal file
57
examples/WithSharedTypes/WithSharedTypesPlugin.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using MySharedTypes.Contracts;
|
||||
|
||||
namespace WithSharedTypes;
|
||||
|
||||
[MinimumApiVersion(143)]
|
||||
public class WithSharedTypesPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: Shared Types";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A simple plugin that shares types between multiple plugins";
|
||||
|
||||
// Declares a player capability, that stores some sort of functionality for a player.
|
||||
// In this case, it's a balance handler, which is used to store a player's balance.
|
||||
// Note that we use the same name for the capability as the one in the other plugin.
|
||||
// IBalanceHandler is defined in MySharedTypes.Contracts, which is a shared library and placed in the `shared/` subfolder.
|
||||
public static PlayerCapability<IBalanceHandler> BalanceCapability { get; } = new("myplugin:balance");
|
||||
|
||||
// Declares a player capability of a primitive type, in this case, a decimal.
|
||||
public static PlayerCapability<Decimal> BalanceCapabilityDecimal { get; } = new("myplugin:balance_decimal");
|
||||
|
||||
// Plugin capabilities are similar to player capabilities, but they are not tied to a player, and are just generic APIs
|
||||
// that are exposed by a plugin. In this case, we expose a balance service, which is used to clear all balances.
|
||||
public static PluginCapability<IBalanceService> BalanceServiceCapability { get; } = new("myplugin:balance_service");
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
// Register the capability implementations here. Note that plugins don't need to register an implementation if it is already implemented in another plugin.
|
||||
Capabilities.RegisterPlayerCapability(BalanceCapability, player => new BalanceHandler(player));
|
||||
Capabilities.RegisterPluginCapability(BalanceServiceCapability, () => new BalanceService());
|
||||
Capabilities.RegisterPlayerCapability(BalanceCapabilityDecimal, (player) => new BalanceHandler(player).Balance);
|
||||
|
||||
AddCommand("css_balance", "Gets your current balance", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
player.PrintToChat($"Your balance is {BalanceCapability.Get(player)?.Balance}");
|
||||
});
|
||||
|
||||
AddCommand("css_give", "Gives you money", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
|
||||
var balance = BalanceCapability.Get(player);
|
||||
if (balance == null) return;
|
||||
|
||||
balance.Add(100);
|
||||
player.PrintToChat($"Your balance is now {balance.Balance}");
|
||||
});
|
||||
}
|
||||
|
||||
public override void Unload(bool hotReload)
|
||||
{
|
||||
}
|
||||
}
|
||||
2
examples/WithSharedTypesConsumer/README.md
Normal file
2
examples/WithSharedTypesConsumer/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# With Shared Types (Consumer Plugin)
|
||||
Uses the decimal balance shared library.
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
|
||||
<ProjectReference Include="..\MySharedTypes.Contracts\MySharedTypes.Contracts\MySharedTypes.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,56 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using CounterStrikeSharp.API.Core.Plugin;
|
||||
using MySharedTypes.Contracts;
|
||||
|
||||
namespace WithSharedTypesConsumer;
|
||||
|
||||
[MinimumApiVersion(142)]
|
||||
public class WithSharedTypesConsumerPlugin : BasePlugin
|
||||
{
|
||||
public override string ModuleName => "Example: Shared Types (Consumer)";
|
||||
public override string ModuleVersion => "1.0.0";
|
||||
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
|
||||
public override string ModuleDescription => "A simple plugin that utilises the balance api from another plugin";
|
||||
|
||||
public static PlayerCapability<IBalanceHandler> BalanceCapability { get; } = new("myplugin:balance");
|
||||
public static PlayerCapability<Decimal> BalanceCapabilityDecimal { get; } = new("myplugin:balance_decimal");
|
||||
|
||||
public static PluginCapability<IBalanceService> BalanceServiceCapability { get; } = new("myplugin:balance_service");
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
AddCommand("css_subtract", "Subtracts 50 from your balance", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
var balance = BalanceCapability.Get(player);
|
||||
if (balance == null) return;
|
||||
balance.Subtract(50);
|
||||
player.PrintToChat($"Your balance is now {balance.Balance}");
|
||||
});
|
||||
|
||||
AddCommand("css_clearbalances", "Clears all balances", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
var service = BalanceServiceCapability.Get();
|
||||
if (service == null) return;
|
||||
|
||||
service.ClearAllBalances();
|
||||
|
||||
var balance = BalanceCapability.Get(player);
|
||||
if (balance == null) return;
|
||||
player.PrintToChat($"Your balance is now {balance.Balance}");
|
||||
});
|
||||
|
||||
AddCommand("css_decimalbalance", "Gets your current balance", (player, info) =>
|
||||
{
|
||||
if (player == null) return;
|
||||
player.PrintToChat($"Your balance is {BalanceCapabilityDecimal.Get(player)}");
|
||||
});
|
||||
}
|
||||
|
||||
public override void Unload(bool hotReload)
|
||||
{
|
||||
}
|
||||
}
|
||||
Submodule libraries/GameTracking-CS2 deleted from 3556d2a923
1
libraries/Protobufs
Submodule
1
libraries/Protobufs
Submodule
Submodule libraries/Protobufs added at 686a0628e6
@@ -102,9 +102,18 @@ public class AdminTests
|
||||
[Fact]
|
||||
public void ShouldAddCommandPermissionOverridesAtRuntime()
|
||||
{
|
||||
Assert.False(AdminManager.CommandIsOverriden("runtime_command"));
|
||||
AdminManager.AddPermissionOverride("runtime_command", "@runtime/override");
|
||||
Assert.True(AdminManager.CommandIsOverriden("runtime_command"));
|
||||
Assert.Equal("@runtime/override", AdminManager.GetPermissionOverrides("runtime_command").Single());
|
||||
Assert.False(AdminManager.CommandIsOverriden("runtime_command_a"));
|
||||
AdminManager.AddPermissionOverride("runtime_command_a", "@runtime/override");
|
||||
Assert.True(AdminManager.CommandIsOverriden("runtime_command_a"));
|
||||
Assert.Equal("@runtime/override", AdminManager.GetPermissionOverrides("runtime_command_a").Single());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldAddCommandPermissionOverridesWithEmpty()
|
||||
{
|
||||
Assert.False(AdminManager.CommandIsOverriden("runtime_command_b"));
|
||||
AdminManager.AddPermissionOverride("runtime_command_b");
|
||||
Assert.True(AdminManager.CommandIsOverriden("runtime_command_b"));
|
||||
Assert.False(AdminManager.GetPermissionOverrides("runtime_command_b").Any());
|
||||
}
|
||||
}
|
||||
@@ -427,6 +427,18 @@
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:CounterStrikeSharp.API.Core.NativeAPI.QueueTaskForNextFrame(System.IntPtr)</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:CounterStrikeSharp.API.Core.NativeAPI.QueueTaskForNextWorldUpdate(System.IntPtr)</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:CounterStrikeSharp.API.Core.Plugin.Host.PluginManager.#ctor(CounterStrikeSharp.API.Core.Hosting.IScriptHostConfiguration,Microsoft.Extensions.Logging.ILogger{CounterStrikeSharp.API.Core.Plugin.Host.PluginManager},System.IServiceProvider)</Target>
|
||||
@@ -439,6 +451,18 @@
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>M:CounterStrikeSharp.API.Core.IPlugin.OnAllPluginsLoaded(System.Boolean)</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>P:CounterStrikeSharp.API.Core.Hosting.IScriptHostConfiguration.SharedPath</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0006</DiagnosticId>
|
||||
<Target>P:CounterStrikeSharp.API.Core.IPlugin.CommandManager</Target>
|
||||
@@ -1039,4 +1063,16 @@
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP1002</DiagnosticId>
|
||||
<Target>Microsoft.Extensions.Localization.Abstractions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP1002</DiagnosticId>
|
||||
<Target>Serilog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10</Target>
|
||||
<Left>.\ApiCompat\v151.dll</Left>
|
||||
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
|
||||
</Suppression>
|
||||
</Suppressions>
|
||||
@@ -23,6 +23,7 @@ namespace CounterStrikeSharp.API
|
||||
[Flags]
|
||||
public enum ConVarFlags : Int64
|
||||
{
|
||||
FCVAR_NONE = 0,
|
||||
FCVAR_LINKED_CONCOMMAND = (1 << 0),
|
||||
|
||||
FCVAR_DEVELOPMENTONLY =
|
||||
|
||||
@@ -502,20 +502,20 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static void QueueTaskForNextFrame(IntPtr callback){
|
||||
public static void QueueTaskForNextFrame(InputArgument callback){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(callback);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0x9FE394D8);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
}
|
||||
}
|
||||
|
||||
public static void QueueTaskForNextWorldUpdate(IntPtr callback){
|
||||
public static void QueueTaskForNextWorldUpdate(InputArgument callback){
|
||||
lock (ScriptContext.GlobalScriptContext.Lock) {
|
||||
ScriptContext.GlobalScriptContext.Reset();
|
||||
ScriptContext.GlobalScriptContext.Push(callback);
|
||||
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
|
||||
ScriptContext.GlobalScriptContext.SetIdentifier(0xAD51A0C9);
|
||||
ScriptContext.GlobalScriptContext.Invoke();
|
||||
ScriptContext.GlobalScriptContext.CheckErrors();
|
||||
|
||||
@@ -130,11 +130,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat(" [#{0}:{1}]: \"{2}\" ({3})", plugin.PluginId,
|
||||
plugin.State.ToString().ToUpper(), plugin.Plugin.ModuleName,
|
||||
plugin.Plugin.ModuleVersion);
|
||||
if (!string.IsNullOrEmpty(plugin.Plugin.ModuleAuthor))
|
||||
plugin.State.ToString().ToUpper(), plugin.Plugin?.ModuleName ?? "Unknown",
|
||||
plugin.Plugin?.ModuleVersion ?? "Unknown");
|
||||
if (!string.IsNullOrEmpty(plugin.Plugin?.ModuleAuthor))
|
||||
sb.AppendFormat(" by {0}", plugin.Plugin.ModuleAuthor);
|
||||
if (!string.IsNullOrEmpty(plugin.Plugin.ModuleDescription))
|
||||
if (!string.IsNullOrEmpty(plugin.Plugin?.ModuleDescription))
|
||||
{
|
||||
sb.Append("\n");
|
||||
sb.Append(" ");
|
||||
@@ -175,6 +175,8 @@ namespace CounterStrikeSharp.API.Core
|
||||
try
|
||||
{
|
||||
_pluginManager.LoadPlugin(path);
|
||||
plugin = _pluginContextQueryHandler.FindPluginByModulePath(path);
|
||||
plugin.Plugin.OnAllPluginsLoaded(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -185,6 +187,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
else
|
||||
{
|
||||
plugin.Load(false);
|
||||
plugin.Plugin.OnAllPluginsLoaded(false);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -233,6 +236,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
plugin.Unload(true);
|
||||
plugin.Load(true);
|
||||
plugin.Plugin.OnAllPluginsLoaded(true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -245,20 +249,17 @@ namespace CounterStrikeSharp.API.Core
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[CommandHelper(usage: "[language code, e.g. \"de\", \"pl\", \"en\"]",
|
||||
whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
|
||||
private void OnLangCommand(CCSPlayerController? player, CommandInfo command)
|
||||
{
|
||||
if (player == null) return;
|
||||
|
||||
SteamID steamId = (SteamID)player.SteamID;
|
||||
var steamId = (SteamID)player.SteamID;
|
||||
|
||||
if (command.ArgCount == 1)
|
||||
{
|
||||
var language = _playerLanguageManager.GetLanguage(steamId);
|
||||
command.ReplyToCommand(string.Format("Current language is \"{0}\" ({1})", language.Name,
|
||||
language.NativeName));
|
||||
command.ReplyToCommand($"Current language is \"{language.Name}\" ({language.NativeName})");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -297,6 +298,11 @@ namespace CounterStrikeSharp.API.Core
|
||||
" stop / unload - Unloads a plugin currently loaded.\n" +
|
||||
" restart / reload - Reloads a plugin currently loaded.",
|
||||
});
|
||||
_commandManager.RegisterCommand(new("css_lang", "Set Counter-Strike Sharp language.", OnLangCommand)
|
||||
{
|
||||
ExecutableBy = CommandUsage.CLIENT_AND_SERVER,
|
||||
UsageHint = "[language code, e.g. \"de\", \"pl\", \"en\"]",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,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.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -72,6 +73,10 @@ namespace CounterStrikeSharp.API.Core
|
||||
public virtual void Unload(bool hotReload)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnAllPluginsLoaded(bool hotReload)
|
||||
{
|
||||
}
|
||||
|
||||
public class CallbackSubscriber : IDisposable
|
||||
{
|
||||
@@ -308,6 +313,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
this.RegisterAttributeHandlers(instance);
|
||||
this.RegisterConsoleCommandAttributeHandlers(instance);
|
||||
this.RegisterEntityOutputAttributeHandlers(instance);
|
||||
this.RegisterFakeConVars(instance);
|
||||
}
|
||||
|
||||
public void InitializeConfig(object instance, Type pluginType)
|
||||
@@ -410,6 +416,43 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterFakeConVars(Type type, object instance = null)
|
||||
{
|
||||
var convars = type
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
|
||||
.Where(prop => prop.FieldType.IsGenericType &&
|
||||
prop.FieldType.GetGenericTypeDefinition() == typeof(FakeConVar<>));
|
||||
|
||||
foreach (var prop in convars)
|
||||
{
|
||||
object propValue = prop.GetValue(instance); // FakeConvar<?> instance
|
||||
var propValueType = prop.FieldType.GenericTypeArguments[0];
|
||||
var name = prop.FieldType.GetProperty("Name", BindingFlags.Public | BindingFlags.Instance)
|
||||
.GetValue(propValue);
|
||||
|
||||
var description = prop.FieldType.GetProperty("Description", BindingFlags.Public | BindingFlags.Instance)
|
||||
.GetValue(propValue);
|
||||
|
||||
MethodInfo executeCommandMethod = prop.FieldType
|
||||
.GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
this.AddCommand((string)name, (string) description, (caller, command) =>
|
||||
{
|
||||
executeCommandMethod.Invoke(propValue, new object[] {caller, command});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to bind a fake ConVar to a plugin command. Only required for ConVars that are not public properties of the plugin class.
|
||||
/// </summary>
|
||||
/// <param name="convar"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public void RegisterFakeConVars(object instance)
|
||||
{
|
||||
RegisterFakeConVars(instance.GetType(), instance);
|
||||
}
|
||||
|
||||
public void HookEntityOutput(string classname, string outputName, EntityIO.EntityOutputHandler handler, HookMode mode = HookMode.Pre)
|
||||
{
|
||||
var subscriber = new CallbackSubscriber(handler, handler,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace CounterStrikeSharp.API.Core.Capabilities;
|
||||
|
||||
public static class Capabilities
|
||||
{
|
||||
public static void RegisterPluginCapability<T>(PluginCapability<T> capability, Func<T> supplier)
|
||||
{
|
||||
if (!PluginCapability<T>.Providers.ContainsKey(capability.Name))
|
||||
{
|
||||
PluginCapability<T>.Providers.Add(capability.Name, new());
|
||||
}
|
||||
|
||||
PluginCapability<T>.Providers[capability.Name].Add(supplier);
|
||||
}
|
||||
|
||||
public static void RegisterPlayerCapability<T>(PlayerCapability<T> capability,
|
||||
Func<CCSPlayerController, T> supplier)
|
||||
{
|
||||
if (!PlayerCapability<T>.Providers.ContainsKey(capability.Name))
|
||||
{
|
||||
PlayerCapability<T>.Providers.Add(capability.Name, new());
|
||||
}
|
||||
|
||||
PlayerCapability<T>.Providers[capability.Name].Add(supplier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Capabilities;
|
||||
|
||||
public sealed class PlayerCapability<T>
|
||||
{
|
||||
public string Name { get; }
|
||||
internal static readonly Dictionary<string, List<Func<CCSPlayerController, T>>> Providers = new();
|
||||
|
||||
public PlayerCapability(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public T? Get(CCSPlayerController entity)
|
||||
{
|
||||
foreach (var provider in Providers[Name])
|
||||
{
|
||||
return provider(entity);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core.Capabilities;
|
||||
|
||||
public sealed class PluginCapability<T>
|
||||
{
|
||||
public string Name { get; }
|
||||
internal static readonly Dictionary<string, List<Func<T>>> Providers = new();
|
||||
|
||||
public PluginCapability(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public T? Get()
|
||||
{
|
||||
foreach (var provider in Providers[Name])
|
||||
{
|
||||
return provider();
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,11 @@ public class CommandManager : ICommandManager
|
||||
|
||||
foreach (var attr in permissionsToCheck)
|
||||
{
|
||||
if (attr.Permissions.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
attr.Command = name;
|
||||
if (!attr.CanExecuteCommand(caller))
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -22,9 +23,22 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the lifetime of a function reference.
|
||||
/// </summary>
|
||||
public enum FunctionLifetime
|
||||
{
|
||||
/// <summary>Delegate will be removed after the first invocation.</summary>
|
||||
SingleUse,
|
||||
/// <summary>Delegate will remain in memory for the lifetime of the application.</summary>
|
||||
Permanent
|
||||
}
|
||||
|
||||
public class FunctionReference
|
||||
{
|
||||
private readonly Delegate m_method;
|
||||
|
||||
public FunctionLifetime Lifetime { get; set; } = FunctionLifetime.Permanent;
|
||||
|
||||
public unsafe delegate void CallbackDelegate(fxScriptContext* context);
|
||||
private CallbackDelegate s_callback;
|
||||
@@ -59,12 +73,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
if (typeof(NativeObject).IsAssignableFrom(param.ParameterType))
|
||||
{
|
||||
obj = Activator.CreateInstance(param.ParameterType,
|
||||
new[] {scriptContext.GetArgument(typeof(IntPtr), i)});
|
||||
new[] { scriptContext.GetArgument(typeof(IntPtr), i) });
|
||||
}
|
||||
else
|
||||
{
|
||||
obj = scriptContext.GetArgument(param.ParameterType, i);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}).ToArray();
|
||||
|
||||
@@ -79,6 +94,15 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
Application.Instance.Logger.LogError(e, "Error invoking callback");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Lifetime == FunctionLifetime.SingleUse)
|
||||
{
|
||||
Remove(Identifier);
|
||||
if (references.ContainsKey(m_method))
|
||||
references.Remove(m_method, out _);
|
||||
}
|
||||
}
|
||||
});
|
||||
s_callback = dg;
|
||||
}
|
||||
@@ -89,24 +113,24 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
public static FunctionReference Create(Delegate method)
|
||||
{
|
||||
if (references.ContainsKey(method))
|
||||
if (references.TryGetValue(method, out var existingReference))
|
||||
{
|
||||
return references[method];
|
||||
return existingReference;
|
||||
}
|
||||
|
||||
var reference = new FunctionReference(method);
|
||||
var referenceId = Register(reference);
|
||||
|
||||
reference.Identifier = referenceId;
|
||||
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
private static Dictionary<int, FunctionReference> ms_references = new Dictionary<int, FunctionReference>();
|
||||
private static ConcurrentDictionary<int, FunctionReference> ms_references = new ConcurrentDictionary<int, FunctionReference>();
|
||||
private static int ms_referenceId;
|
||||
|
||||
private static Dictionary<Delegate, FunctionReference> references =
|
||||
new Dictionary<Delegate, FunctionReference>();
|
||||
private static ConcurrentDictionary<Delegate, FunctionReference> references =
|
||||
new ConcurrentDictionary<Delegate, FunctionReference>();
|
||||
|
||||
private static int Register(FunctionReference reference)
|
||||
{
|
||||
@@ -139,7 +163,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
if (ms_references.TryGetValue(reference, out var funcRef))
|
||||
{
|
||||
ms_references.Remove(reference);
|
||||
ms_references.Remove(reference, out _);
|
||||
|
||||
Application.Instance.Logger.LogDebug("Removing function/callback reference: {Reference}", reference);
|
||||
}
|
||||
|
||||
@@ -7119,6 +7119,15 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
|
||||
[EventName("warmup_end")]
|
||||
public class EventWarmupEnd : GameEvent
|
||||
{
|
||||
public EventWarmupEnd(IntPtr pointer) : base(pointer){}
|
||||
public EventWarmupEnd(bool force) : base("warmup_end", force){}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[EventName("weapon_fire")]
|
||||
public class EventWeaponFire : GameEvent
|
||||
{
|
||||
|
||||
@@ -18,6 +18,12 @@ public interface IScriptHostConfiguration
|
||||
/// </summary>
|
||||
string PluginPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the directory that contains CounterStrikeSharp plugin shared APIS.
|
||||
/// e.g. /game/csgo/addons/counterstrikesharp/shared
|
||||
/// </summary>
|
||||
string SharedPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the directory that contains CounterStrikeSharp configs.
|
||||
/// e.g. /game/csgo/addons/counterstrikesharp/configs
|
||||
|
||||
@@ -7,12 +7,14 @@ internal sealed class ScriptHostConfiguration : IScriptHostConfiguration
|
||||
{
|
||||
public string RootPath { get; }
|
||||
public string PluginPath { get; }
|
||||
public string SharedPath { get; }
|
||||
public string ConfigsPath { get; }
|
||||
public string GameDataPath { get; }
|
||||
|
||||
public ScriptHostConfiguration(IHostEnvironment hostEnvironment)
|
||||
{
|
||||
RootPath = Path.Join(new[] { hostEnvironment.ContentRootPath });
|
||||
SharedPath = Path.Join(new[] { hostEnvironment.ContentRootPath, "shared" });
|
||||
PluginPath = Path.Join(new[] { hostEnvironment.ContentRootPath, "plugins" });
|
||||
ConfigsPath = Path.Join(new[] { hostEnvironment.ContentRootPath, "configs" });
|
||||
GameDataPath = Path.Join(new[] { hostEnvironment.ContentRootPath, "gamedata" });
|
||||
|
||||
@@ -53,6 +53,14 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// </summary>
|
||||
void Unload(bool hotReload);
|
||||
|
||||
/// <summary>
|
||||
/// Will be called by CounterStrikeSharp after all plugins have been loaded.
|
||||
/// This will also be called for convenience after a reload or a late l oad, so that you don't have to handle
|
||||
/// re-wiring everything.
|
||||
/// </summary>
|
||||
/// <param name="hotReload"></param>
|
||||
void OnAllPluginsLoaded(bool hotReload);
|
||||
|
||||
string ModulePath { get; internal set; }
|
||||
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Core
|
||||
{
|
||||
@@ -154,5 +155,13 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// <param name="simulating"><see langword="true"/> if simulating, <see langword="false"/> otherwise</param>
|
||||
[ListenerName("OnServerPreWorldUpdate")]
|
||||
public delegate void OnServerPreWorldUpdate(bool simulating);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called when the server precaching resources (only when initial startup / changing map).
|
||||
/// </summary>
|
||||
/// <param name="manifest">Resource Manifest</param>
|
||||
[ListenerName("OnServerPrecacheResources")]
|
||||
public delegate void OnServerPrecacheResources(ResourceManifest manifest);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
@@ -6,14 +7,20 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBaseEntity
|
||||
{
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void Teleport(Vector position, QAngle angles, Vector velocity)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunction.CreateVoid<IntPtr, IntPtr, IntPtr, IntPtr>(Handle, GameData.GetOffset("CBaseEntity_Teleport"))(
|
||||
Handle, position.Handle, angles.Handle, velocity.Handle);
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void DispatchSpawn()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.CBaseEntity_DispatchSpawn(Handle, IntPtr.Zero);
|
||||
}
|
||||
|
||||
@@ -25,7 +32,13 @@ public partial class CBaseEntity
|
||||
/// <summary>
|
||||
/// Shorthand for accessing an entity's CBodyComponent?.SceneNode?.AbsRotation;
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public QAngle? AbsRotation => CBodyComponent?.SceneNode?.AbsRotation;
|
||||
|
||||
public T? GetVData<T>() where T : CEntitySubclassVDataBase => (T)Activator.CreateInstance(typeof(T), Marshal.ReadIntPtr(SubclassID.Handle + 4));
|
||||
public T? GetVData<T>() where T : CEntitySubclassVDataBase
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
return (T) Activator.CreateInstance(typeof(T), Marshal.ReadIntPtr(SubclassID.Handle + 4));
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,11 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBaseModelEntity
|
||||
{
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void SetModel(string model)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.SetModel(Handle, model);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
|
||||
using CounterStrikeSharp.API.Modules.Cvars;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
@@ -10,10 +9,13 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public partial class CBasePlayerController
|
||||
{
|
||||
public void SetPawn(CBasePlayerPawn? pawn)
|
||||
{
|
||||
if (pawn is null) return;
|
||||
if (!pawn.IsValid) return;
|
||||
VirtualFunctions.CBasePlayerController_SetPawnFunc.Invoke(this, pawn, true, false);
|
||||
}
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void SetPawn(CBasePlayerPawn? pawn)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
if (pawn is null) return;
|
||||
if (!pawn.IsValid) return;
|
||||
VirtualFunctions.CBasePlayerController_SetPawnFunc.Invoke(this, pawn, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,24 @@ public partial class CBasePlayerPawn
|
||||
/// </summary>
|
||||
/// <param name="explode"></param>
|
||||
/// <param name="force"></param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void CommitSuicide(bool explode, bool force)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunction.CreateVoid<IntPtr, bool, bool>(Handle, GameData.GetOffset("CBasePlayerPawn_CommitSuicide"))(Handle, explode, force);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remove Player Item
|
||||
/// </summary>
|
||||
/// <param name="weapon"></param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void RemovePlayerItem(CBasePlayerWeapon weapon)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
Guard.IsValidEntity(weapon);
|
||||
|
||||
VirtualFunctions.RemovePlayerItemVirtual(Handle, weapon.Handle);
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,11 @@ public partial class CCSPlayerController
|
||||
public int? UserId => NativeAPI.GetUseridFromIndex((int)Index);
|
||||
public CsTeam Team => (CsTeam)TeamNum;
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public IntPtr GiveNamedItem(string item)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
if (!PlayerPawn.IsValid) return 0;
|
||||
if (PlayerPawn.Value == null) return 0;
|
||||
if (!PlayerPawn.Value.IsValid) return 0;
|
||||
@@ -34,25 +37,37 @@ public partial class CCSPlayerController
|
||||
return GiveNamedItem(itemString);
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void PrintToConsole(string message)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.PrintToConsole((int)Index, $"{message}\n\0");
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void PrintToChat(string message)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.ClientPrint(Handle, HudDestination.Chat, message, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void PrintToCenter(string message)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.ClientPrint(Handle, HudDestination.Center, message, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void PrintToCenterHtml(string message) => PrintToCenterHtml(message, 5);
|
||||
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void PrintToCenterHtml(string message, int duration)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
var @event = new EventShowSurvivalRespawnStatus(true)
|
||||
{
|
||||
LocToken = message,
|
||||
@@ -65,8 +80,10 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Drops the active player weapon on the ground.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void DropActiveWeapon()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
@@ -83,8 +100,10 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Removes every weapon from the player.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void RemoveWeapons()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
@@ -99,8 +118,10 @@ public partial class CCSPlayerController
|
||||
/// </summary>
|
||||
/// <param name="explode"></param>
|
||||
/// <param name="force"></param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void CommitSuicide(bool explode, bool force)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
@@ -111,8 +132,10 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Respawn player
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void Respawn()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
if (!PlayerPawn.IsValid) return;
|
||||
if (PlayerPawn.Value == null) return;
|
||||
if (!PlayerPawn.Value.IsValid) return;
|
||||
@@ -128,8 +151,11 @@ public partial class CCSPlayerController
|
||||
/// Forcibly switches the team of the player, the player will remain alive and keep their weapons.
|
||||
/// </summary>
|
||||
/// <param name="team">The team to switch to</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void SwitchTeam(CsTeam team)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.SwitchTeam(Handle, (byte)team);
|
||||
}
|
||||
|
||||
@@ -140,8 +166,11 @@ public partial class CCSPlayerController
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
/// <param name="team">The team to change to</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void ChangeTeam(CsTeam team)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunction.CreateVoid<IntPtr, CsTeam>(Handle, GameData.GetOffset("CCSPlayerController_ChangeTeam"))(Handle,
|
||||
team);
|
||||
}
|
||||
@@ -151,8 +180,11 @@ public partial class CCSPlayerController
|
||||
/// </summary>
|
||||
/// <param name="conVar">Name of the convar to retrieve</param>
|
||||
/// <returns>ConVar string value</returns>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public string GetConVarValue(string conVar)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
return NativeAPI.GetClientConvarValue(Slot, conVar);
|
||||
}
|
||||
|
||||
@@ -171,13 +203,12 @@ public partial class CCSPlayerController
|
||||
/// </summary>
|
||||
/// <param name="conVar">Console variable name</param>
|
||||
/// <param name="value">String value to set</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
/// <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)");
|
||||
}
|
||||
Guard.IsValidEntity(this);
|
||||
if (!IsBot) throw new InvalidOperationException("'SetFakeClientConVar' can only be called for fake clients (bots)");
|
||||
|
||||
NativeAPI.SetFakeClientConvarValue(Slot, conVar, value);
|
||||
}
|
||||
@@ -207,27 +238,45 @@ public partial class CCSPlayerController
|
||||
/// Note: Only works for some commands, marked with the FCVAR_CLIENT_CAN_EXECUTE flag (not many).
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
public void ExecuteClientCommand(string command) => NativeAPI.IssueClientCommand(Slot, command);
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void ExecuteClientCommand(string command)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.IssueClientCommand(Slot, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issue the specified command directly from the server (mimics the server executing the command with the given player context).
|
||||
/// <remarks>Works with server commands like `kill`, `explode`, `noclip`, etc. </remarks>
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
public void ExecuteClientCommandFromServer(string command) => NativeAPI.IssueClientCommandFromServer(Slot, command);
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void ExecuteClientCommandFromServer(string command)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.IssueClientCommandFromServer(Slot, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides who a player can hear in voice chat.
|
||||
/// </summary>
|
||||
/// <param name="sender">Player talking in the voice chat</param>
|
||||
/// <param name="override">Whether the talker should be heard</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void SetListenOverride(CCSPlayerController sender, ListenOverride @override)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.SetClientListening(Handle, sender.Handle, (Byte)@override);
|
||||
}
|
||||
|
||||
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public ListenOverride GetListenOverride(CCSPlayerController sender)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
return NativeAPI.GetClientListening(Handle, sender.Handle);
|
||||
}
|
||||
|
||||
@@ -236,27 +285,31 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Returns the authorized SteamID of this user which has been validated with the SteamAPI.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public SteamID? AuthorizedSteamID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid) return null;
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
var authorizedSteamId = NativeAPI.GetPlayerAuthorizedSteamid(Slot);
|
||||
if ((long)authorizedSteamId == -1) return null;
|
||||
|
||||
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>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public string? IpAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid) return null;
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
var ipAddress = NativeAPI.GetPlayerIpAddress(Slot);
|
||||
if (string.IsNullOrWhiteSpace(ipAddress)) return null;
|
||||
|
||||
@@ -267,9 +320,20 @@ public partial class CCSPlayerController
|
||||
/// <summary>
|
||||
/// Determines how the player interacts with voice chat.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public VoiceFlags VoiceFlags
|
||||
{
|
||||
get => (VoiceFlags)NativeAPI.GetClientVoiceFlags(Handle);
|
||||
set => NativeAPI.SetClientVoiceFlags(Handle, (Byte)value);
|
||||
get
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
return (VoiceFlags)NativeAPI.GetClientVoiceFlags(Handle);
|
||||
}
|
||||
set
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
NativeAPI.SetClientVoiceFlags(Handle, (Byte)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,26 @@ public partial class CCSPlayer_ItemServices
|
||||
/// <summary>
|
||||
/// Drops the active player weapon on the ground.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">ItemServices points to null</exception>
|
||||
public void DropActivePlayerWeapon(CBasePlayerWeapon activeWeapon)
|
||||
{
|
||||
if(Handle == IntPtr.Zero)
|
||||
throw new InvalidOperationException("ItemServices points to null.");
|
||||
|
||||
Guard.IsValidEntity(activeWeapon);
|
||||
|
||||
VirtualFunction.CreateVoid<nint, nint>(Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon"))(Handle, activeWeapon.Handle);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes every weapon from the player.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">ItemServices points to null</exception>
|
||||
public void RemoveWeapons()
|
||||
{
|
||||
if (Handle == IntPtr.Zero)
|
||||
throw new InvalidOperationException("ItemServices points to null.");
|
||||
|
||||
VirtualFunction.CreateVoid<nint>(Handle, GameData.GetOffset("CCSPlayer_ItemServices_RemoveWeapons"))(Handle);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,12 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
|
||||
public string DesignerName => IsValid ? Entity?.DesignerName : null;
|
||||
|
||||
public void Remove() => VirtualFunctions.UTIL_Remove(this.Handle);
|
||||
public void Remove()
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.UTIL_Remove(this.Handle);
|
||||
}
|
||||
|
||||
public bool Equals(CEntityInstance? other)
|
||||
{
|
||||
@@ -58,7 +63,7 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calls a named input method on an entity.
|
||||
/// <example>
|
||||
@@ -72,8 +77,11 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
|
||||
/// <param name="caller">Entity that is sending the event, <see langword="null"/> for no entity</param>
|
||||
/// <param name="value">String variant value to send with the event</param>
|
||||
/// <param name="outputId">Unknown, defaults to 0</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public void AcceptInput(string inputName, CEntityInstance? activator = null, CEntityInstance? caller = null, string value = "", int outputId = 0)
|
||||
{
|
||||
Guard.IsValidEntity(this);
|
||||
|
||||
VirtualFunctions.AcceptInput(Handle, inputName, activator?.Handle ?? IntPtr.Zero, caller?.Handle ?? IntPtr.Zero, value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,11 @@ public partial class CGameSceneNode
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CSkeletonInstance"/> instance from the node.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">GameSceneNode points to null</exception>
|
||||
public CSkeletonInstance GetSkeletonInstance()
|
||||
{
|
||||
if (Handle == IntPtr.Zero) throw new InvalidOperationException("GameSceneNode points to null.");
|
||||
|
||||
return new CSkeletonInstance(VirtualFunction.Create<nint, nint>(Handle, GameData.GetOffset("CGameSceneNode_GetSkeletonInstance"))(Handle));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using CounterStrikeSharp.API.Core.Commands;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using McMaster.NETCore.Plugins;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -14,8 +18,11 @@ public class PluginManager : IPluginManager
|
||||
private readonly ICommandManager _commandManager;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<PluginManager> _logger;
|
||||
private readonly Dictionary<string, Assembly> _sharedAssemblies = new();
|
||||
private bool _loadedSharedLibs = false;
|
||||
|
||||
public PluginManager(IScriptHostConfiguration scriptHostConfiguration, ICommandManager commandManager, ILogger<PluginManager> logger, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory)
|
||||
public PluginManager(IScriptHostConfiguration scriptHostConfiguration, ICommandManager commandManager,
|
||||
ILogger<PluginManager> logger, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_scriptHostConfiguration = scriptHostConfiguration;
|
||||
_commandManager = commandManager;
|
||||
@@ -23,6 +30,36 @@ public class PluginManager : IPluginManager
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
private void LoadLibrary(string path)
|
||||
{
|
||||
var loader = PluginLoader.CreateFromAssemblyFile(path, new[] { typeof(IPlugin), typeof(PluginCapability<>), typeof(PlayerCapability<>) },
|
||||
config => { config.PreferSharedTypes = true; });
|
||||
var assembly = loader.LoadDefaultAssembly();
|
||||
|
||||
_sharedAssemblies[assembly.GetName().FullName] = assembly;
|
||||
}
|
||||
|
||||
private void LoadSharedLibraries()
|
||||
{
|
||||
var sharedDirectory = Directory.GetDirectories(_scriptHostConfiguration.SharedPath);
|
||||
var sharedAssemblyPaths = sharedDirectory
|
||||
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
|
||||
.Where(File.Exists)
|
||||
.ToArray();
|
||||
|
||||
foreach (var sharedAssemblyPath in sharedAssemblyPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadLibrary(sharedAssemblyPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Failed to load shared assembly from {Path}", sharedAssemblyPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
var pluginDirectories = Directory.GetDirectories(_scriptHostConfiguration.PluginPath);
|
||||
@@ -31,6 +68,22 @@ public class PluginManager : IPluginManager
|
||||
.Where(File.Exists)
|
||||
.ToArray();
|
||||
|
||||
AssemblyLoadContext.Default.Resolving += (context, name) =>
|
||||
{
|
||||
if (!_loadedSharedLibs)
|
||||
{
|
||||
LoadSharedLibraries();
|
||||
_loadedSharedLibs = true;
|
||||
}
|
||||
|
||||
if (!_sharedAssemblies.TryGetValue(name.FullName, out var assembly))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return assembly;
|
||||
};
|
||||
|
||||
foreach (var path in pluginAssemblyPaths)
|
||||
{
|
||||
try
|
||||
@@ -42,6 +95,11 @@ public class PluginManager : IPluginManager
|
||||
_logger.LogError(e, "Failed to load plugin from {Path}", path);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var plugin in _loadedPluginContexts)
|
||||
{
|
||||
plugin.Plugin.OnAllPluginsLoaded(false);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<PluginContext> GetLoadedPlugins()
|
||||
@@ -51,7 +109,8 @@ public class PluginManager : IPluginManager
|
||||
|
||||
public void LoadPlugin(string path)
|
||||
{
|
||||
var plugin = new PluginContext(_serviceProvider, _commandManager, _scriptHostConfiguration, path, _loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
|
||||
var plugin = new PluginContext(_serviceProvider, _commandManager, _scriptHostConfiguration, path,
|
||||
_loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
|
||||
_loadedPluginContexts.Add(plugin);
|
||||
plugin.Load();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using CounterStrikeSharp.API.Core.Commands;
|
||||
using CounterStrikeSharp.API.Core.Hosting;
|
||||
using CounterStrikeSharp.API.Core.Logging;
|
||||
@@ -64,7 +65,7 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
_hostConfiguration = hostConfiguration;
|
||||
_path = path;
|
||||
PluginId = id;
|
||||
|
||||
|
||||
Loader = PluginLoader.CreateFromAssemblyFile(path,
|
||||
new[]
|
||||
{
|
||||
@@ -76,7 +77,7 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
config.IsUnloadable = true;
|
||||
config.PreferSharedTypes = true;
|
||||
});
|
||||
|
||||
|
||||
if (CoreConfig.PluginHotReloadEnabled)
|
||||
{
|
||||
_fileWatcher = new FileSystemWatcher
|
||||
@@ -110,6 +111,7 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
Loader = eventargs.Loader;
|
||||
Unload(hotReload: true);
|
||||
Load(hotReload: true);
|
||||
Plugin.OnAllPluginsLoaded(hotReload: true);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
@@ -118,12 +120,12 @@ namespace CounterStrikeSharp.API.Core.Plugin
|
||||
public void Load(bool hotReload = false)
|
||||
{
|
||||
if (State == PluginState.Loaded) return;
|
||||
|
||||
|
||||
using (Loader.EnterContextualReflection())
|
||||
{
|
||||
var defaultAssembly = Loader.LoadDefaultAssembly();
|
||||
|
||||
Type pluginType = defaultAssembly.GetTypes()
|
||||
Type pluginType = defaultAssembly.GetExportedTypes()
|
||||
.FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
|
||||
|
||||
if (pluginType == null) throw new Exception("Unable to find plugin in assembly");
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<!-- <GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile> -->
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApiCompatValidateAssemblies>true</ApiCompatValidateAssemblies>
|
||||
|
||||
11
managed/CounterStrikeSharp.API/Guard.cs
Normal file
11
managed/CounterStrikeSharp.API/Guard.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace CounterStrikeSharp.API
|
||||
{
|
||||
public static class Guard
|
||||
{
|
||||
public static void IsValidEntity(CEntityInstance ent)
|
||||
{
|
||||
if (!ent.IsValid)
|
||||
throw new InvalidOperationException("Entity is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
{
|
||||
CommandOverrides.TryGetValue(commandName, out var overrideDef);
|
||||
if (overrideDef == null) return false;
|
||||
return overrideDef.Enabled && overrideDef?.Flags.Count() > 0;
|
||||
return overrideDef.Enabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -132,7 +132,6 @@ namespace CounterStrikeSharp.API.Modules.Admin
|
||||
foreach (var adminDef in adminsFromFile.Values)
|
||||
{
|
||||
adminDef.InitalizeFlags();
|
||||
Console.WriteLine($"Domains: {adminDef.Flags.Count}");
|
||||
|
||||
if (SteamID.TryParse(adminDef.Identity, out var steamId))
|
||||
{
|
||||
|
||||
140
managed/CounterStrikeSharp.API/Modules/Cvars/FakeConVar.cs
Normal file
140
managed/CounterStrikeSharp.API/Modules/Cvars/FakeConVar.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using CounterStrikeSharp.API.Modules.Commands;
|
||||
using CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Cvars;
|
||||
|
||||
public class FakeConVar<T> where T : IComparable<T>
|
||||
{
|
||||
private readonly IEnumerable<IValidator<T>>? _customValidators;
|
||||
|
||||
public FakeConVar(string name, string description, T defaultValue = default(T), ConVarFlags flags = ConVarFlags.FCVAR_NONE,
|
||||
params IValidator<T>[] customValidators)
|
||||
{
|
||||
_customValidators = customValidators;
|
||||
Name = name;
|
||||
Description = description;
|
||||
Value = defaultValue;
|
||||
Flags = flags;
|
||||
}
|
||||
|
||||
public ConVarFlags Flags { get; set; }
|
||||
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
|
||||
public event EventHandler<T> ValueChanged;
|
||||
|
||||
private T _value;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get => _value;
|
||||
set => SetValue(value);
|
||||
}
|
||||
|
||||
internal void ExecuteCommand(CCSPlayerController? player, CommandInfo args)
|
||||
{
|
||||
if (player != null && !Flags.HasFlag(ConVarFlags.FCVAR_REPLICATED))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.ArgCount < 2)
|
||||
{
|
||||
if (Flags.HasFlag(ConVarFlags.FCVAR_PROTECTED) && player != null)
|
||||
{
|
||||
args.ReplyToCommand($"{args.GetArg(0)} = <protected>");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.ReplyToCommand($"{args.GetArg(0)} = {Value.ToString()}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Flags.HasFlag(ConVarFlags.FCVAR_CHEAT))
|
||||
{
|
||||
var cheats = ConVar.Find("sv_cheats")!.GetPrimitiveValue<bool>();
|
||||
if (!cheats)
|
||||
{
|
||||
args.ReplyToCommand($"SV: Convar '{Name}' is cheat protected, change ignored");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// TODO(dotnet8): Replace with IParsable<T>
|
||||
bool success = true;
|
||||
T parsedValue = default(T);
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (converter.CanConvertFrom(typeof(string)))
|
||||
{
|
||||
try
|
||||
{
|
||||
parsedValue = (T)converter.ConvertFromString(args.ArgString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
success = typeof(T) == typeof(bool) && TryConvertCustomBoolean(args.ArgString, out parsedValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
args.ReplyToCommand($"Error: String '{args.GetArg(1)}' can't be converted to {typeof(T).Name}");
|
||||
args.ReplyToCommand($"Failed to parse input ConVar '{Name}' from string '{args.GetArg(1)}'");
|
||||
return;
|
||||
}
|
||||
|
||||
SetValue(parsedValue);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
args.ReplyToCommand($"Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryConvertCustomBoolean(string input, out T result)
|
||||
{
|
||||
input = input.Trim().ToLowerInvariant();
|
||||
if (input == "1" || input == "true")
|
||||
{
|
||||
result = (T)(object)true;
|
||||
return true;
|
||||
}
|
||||
else if (input == "0" || input == "false")
|
||||
{
|
||||
result = (T)(object)false;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetValue(T value)
|
||||
{
|
||||
if (_customValidators != null)
|
||||
{
|
||||
foreach (var validator in _customValidators)
|
||||
{
|
||||
if (!validator.Validate(value, out var error))
|
||||
{
|
||||
throw new ArgumentException($"{error ?? "Invalid value provided"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_value = value;
|
||||
ValueChanged?.Invoke(this, _value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
public interface IValidator<in T>
|
||||
{
|
||||
bool Validate(T value, out string? errorMessage);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace CounterStrikeSharp.API.Modules.Cvars.Validators;
|
||||
|
||||
public class RangeValidator<T> : IValidator<T> where T : IComparable<T>
|
||||
{
|
||||
private readonly T _min;
|
||||
private readonly T _max;
|
||||
|
||||
public RangeValidator(T min, T max)
|
||||
{
|
||||
_min = min;
|
||||
_max = max;
|
||||
}
|
||||
|
||||
public bool Validate(T value, out string? errorMessage)
|
||||
{
|
||||
if (value.CompareTo(_min) >= 0 && value.CompareTo(_max) <= 0)
|
||||
{
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = $"Value must be between {_min} and {_max}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Extensions;
|
||||
|
||||
public static class PlayerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="ChatColors.ForPlayer"/>
|
||||
/// </summary>
|
||||
public static char GetChatColor(this CCSPlayerController player)
|
||||
{
|
||||
return ChatColors.ForPlayer(player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Extensions;
|
||||
|
||||
public static class TeamExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="ChatColors.ForTeam"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static char GetChatColor(this CsTeam team)
|
||||
{
|
||||
return ChatColors.ForTeam(team);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
@@ -78,11 +79,15 @@ public class Schema
|
||||
|
||||
public static T GetSchemaValue<T>(IntPtr handle, string className, string propertyName)
|
||||
{
|
||||
if (handle == IntPtr.Zero) throw new ArgumentNullException(nameof(handle), "Schema target points to null.");
|
||||
|
||||
return NativeAPI.GetSchemaValueByName<T>(handle, (int)typeof(T).ToDataType(), className, propertyName);
|
||||
}
|
||||
|
||||
public static void SetSchemaValue<T>(IntPtr handle, string className, string propertyName, T value)
|
||||
{
|
||||
if (handle == IntPtr.Zero) throw new ArgumentNullException(nameof(handle), "Schema target points to null.");
|
||||
|
||||
if (CoreConfig.FollowCS2ServerGuidelines && _cs2BadList.Contains(propertyName))
|
||||
{
|
||||
throw new Exception($"Cannot set or get '{className}::{propertyName}' with \"FollowCS2ServerGuidelines\" option enabled.");
|
||||
@@ -93,11 +98,15 @@ public class Schema
|
||||
|
||||
public static T GetDeclaredClass<T>(IntPtr pointer, string className, string memberName)
|
||||
{
|
||||
if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null.");
|
||||
|
||||
return (T)Activator.CreateInstance(typeof(T), pointer + GetSchemaOffset(className, memberName));
|
||||
}
|
||||
|
||||
public static unsafe ref T GetRef<T>(IntPtr pointer, string className, string memberName)
|
||||
{
|
||||
if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null.");
|
||||
|
||||
return ref Unsafe.AsRef<T>((void*)(pointer + GetSchemaOffset(className, memberName)));
|
||||
}
|
||||
|
||||
@@ -114,6 +123,8 @@ public class Schema
|
||||
|
||||
public static T GetPointer<T>(IntPtr pointer, string className, string memberName)
|
||||
{
|
||||
if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null.");
|
||||
|
||||
var pointerTo = Marshal.ReadIntPtr(pointer + GetSchemaOffset(className, memberName));
|
||||
if (pointerTo == IntPtr.Zero)
|
||||
{
|
||||
@@ -125,6 +136,8 @@ public class Schema
|
||||
|
||||
public static unsafe Span<T> GetFixedArray<T>(IntPtr pointer, string className, string memberName, int count)
|
||||
{
|
||||
if (pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(pointer), "Schema target points to null.");
|
||||
|
||||
Span<T> span = new((void*)(pointer + GetSchemaOffset(className, memberName)), count);
|
||||
return span;
|
||||
}
|
||||
|
||||
@@ -42,4 +42,37 @@ public class ChatColors
|
||||
|
||||
[Obsolete("Use ChatColors.DarkRed instead.")]
|
||||
public static char Darkred = '\x02';
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a chat color based on a team.
|
||||
/// <remarks>Blue for CT, Yellow for T, LightPurple for Spectator</remarks>
|
||||
/// </summary>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static char ForTeam(CsTeam team)
|
||||
{
|
||||
switch (team)
|
||||
{
|
||||
case CsTeam.None:
|
||||
return White;
|
||||
case CsTeam.Spectator:
|
||||
return LightPurple;
|
||||
case CsTeam.CounterTerrorist:
|
||||
return LightBlue;
|
||||
case CsTeam.Terrorist:
|
||||
return Yellow;
|
||||
default:
|
||||
throw new ArgumentException($"Invalid team: ${team}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a chat color for a player based on their team.
|
||||
/// <remarks>Blue for CT, Yellow for T, LightPurple for Spectator</remarks>
|
||||
/// </summary>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static char ForPlayer(CCSPlayerController player)
|
||||
{
|
||||
return ForTeam(player.Team);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CounterStrikeSharp.API.Modules.Utils
|
||||
{
|
||||
public class ResourceManifest : NativeObject
|
||||
{
|
||||
private Action<nint, string> _AddResource;
|
||||
public ResourceManifest(nint pointer) : base(pointer)
|
||||
{
|
||||
_AddResource = VirtualFunction.CreateVoid<nint, string>(Handle, GameData.GetOffset("CEntityResourceManifest_AddResource"));
|
||||
}
|
||||
|
||||
public void AddResource(string resourcePath) => _AddResource(Handle, resourcePath);
|
||||
}
|
||||
}
|
||||
@@ -40,10 +40,6 @@ namespace CounterStrikeSharp.API
|
||||
public static float GameFrameTime => NativeAPI.GetGameFrameTime();
|
||||
public static double EngineTime => NativeAPI.GetEngineTime();
|
||||
public static void PrecacheModel(string name) => NativeAPI.PrecacheModel(name);
|
||||
// public static void PrecacheSound(string name) => Sound.PrecacheSound(name);
|
||||
|
||||
// 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.
|
||||
@@ -51,9 +47,9 @@ namespace CounterStrikeSharp.API
|
||||
/// </summary>
|
||||
public static void NextFrame(Action task)
|
||||
{
|
||||
nextFrameTasks.Add(task);
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(task);
|
||||
NativeAPI.QueueTaskForNextFrame(ptr);
|
||||
var functionReference = FunctionReference.Create(task);
|
||||
functionReference.Lifetime = FunctionLifetime.SingleUse;
|
||||
NativeAPI.QueueTaskForNextFrame(functionReference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -63,9 +59,9 @@ namespace CounterStrikeSharp.API
|
||||
/// <param name="task"></param>
|
||||
public static void NextWorldUpdate(Action task)
|
||||
{
|
||||
nextFrameTasks.Add(task);
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(task);
|
||||
NativeAPI.QueueTaskForNextWorldUpdate(ptr);
|
||||
var functionReference = FunctionReference.Create(task);
|
||||
functionReference.Lifetime = FunctionLifetime.SingleUse;
|
||||
NativeAPI.QueueTaskForNextWorldUpdate(functionReference);
|
||||
}
|
||||
|
||||
public static void PrintToChatAll(string message)
|
||||
|
||||
@@ -209,8 +209,11 @@ namespace CounterStrikeSharp.API
|
||||
/// <param name="className" example="CBaseEntity">Schema field class name</param>
|
||||
/// <param name="fieldName" example="m_iHealth">Schema field name</param>
|
||||
/// <param name="extraOffset">Any additional offset to the schema field</param>
|
||||
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
||||
public static void SetStateChanged(CBaseEntity entity, string className, string fieldName, int extraOffset = 0)
|
||||
{
|
||||
Guard.IsValidEntity(entity);
|
||||
|
||||
if (!Schema.IsSchemaFieldNetworked(className, fieldName))
|
||||
{
|
||||
Application.Instance.Logger.LogWarning("Field {ClassName}:{FieldName} is not networked, but SetStateChanged was called on it.", className, fieldName);
|
||||
|
||||
@@ -32,6 +32,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithTranslations", "..\exam
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithVoiceOverrides", "..\examples\WithVoiceOverrides\WithVoiceOverrides.csproj", "{6FA3107D-42AF-42A0-BF51-2230D13268B5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithFakeConvars", "..\examples\WithFakeConvars\WithFakeConvars.csproj", "{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithSharedTypes", "..\examples\WithSharedTypes\WithSharedTypes.csproj", "{4E5289B5-E81D-421C-B340-B98B6FFE09D1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySharedTypes.Contracts", "..\examples\MySharedTypes.Contracts\MySharedTypes.Contracts\MySharedTypes.Contracts.csproj", "{A37676EA-CF2F-424D-85A1-C359D07A679D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithSharedTypesConsumer", "..\examples\WithSharedTypesConsumer\WithSharedTypesConsumer.csproj", "{76AD7BB0-A096-4336-83E2-B32CAE4E9933}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -98,6 +106,22 @@ Global
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{76AD7BB0-A096-4336-83E2-B32CAE4E9933}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{76AD7BB0-A096-4336-83E2-B32CAE4E9933}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{76AD7BB0-A096-4336-83E2-B32CAE4E9933}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{76AD7BB0-A096-4336-83E2-B32CAE4E9933}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{57E64289-5D69-4AA1-BEF0-D0D96A55EE8F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
@@ -111,5 +135,9 @@ Global
|
||||
{31EABE0B-871F-497B-BF36-37FFC6FAD15F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{BB44E08E-CCA8-4E22-A132-11B2F69D1890} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{6FA3107D-42AF-42A0-BF51-2230D13268B5} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{4E5289B5-E81D-421C-B340-B98B6FFE09D1} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{A37676EA-CF2F-424D-85A1-C359D07A679D} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
{76AD7BB0-A096-4336-83E2-B32CAE4E9933} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -35,6 +35,7 @@ using CounterStrikeSharp.API.Modules.Menu;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static CounterStrikeSharp.API.Core.Listeners;
|
||||
|
||||
namespace TestPlugin
|
||||
{
|
||||
@@ -166,6 +167,19 @@ namespace TestPlugin
|
||||
|
||||
return HookResult.Continue;
|
||||
}), HookMode.Pre);
|
||||
|
||||
// Precache resources
|
||||
RegisterListener<Listeners.OnServerPrecacheResources>((manifest) =>
|
||||
{
|
||||
manifest.AddResource("path/to/model");
|
||||
manifest.AddResource("path/to/material");
|
||||
manifest.AddResource("path/to/particle");
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnAllPluginsLoaded(bool hotReload)
|
||||
{
|
||||
Logger.LogInformation("All plugins loaded!");
|
||||
}
|
||||
|
||||
private void SetupConvars()
|
||||
|
||||
66
src/core/game_system.cpp
Normal file
66
src/core/game_system.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* CS2Fixes
|
||||
* Copyright (C) 2023-2024 Source2ZE
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program 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
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "core/log.h"
|
||||
#include "core/globals.h"
|
||||
#include "core/gameconfig.h"
|
||||
#include "core/game_system.h"
|
||||
|
||||
#include "core/managers/server_manager.h"
|
||||
|
||||
CBaseGameSystemFactory** CBaseGameSystemFactory::sm_pFirst = nullptr;
|
||||
|
||||
CGameSystem g_GameSystem;
|
||||
IGameSystemFactory* CGameSystem::sm_Factory = nullptr;
|
||||
|
||||
// This mess is needed to get the pointer to sm_pFirst so we can insert game systems
|
||||
bool InitGameSystems()
|
||||
{
|
||||
// This signature directly points to the instruction referencing sm_pFirst, and the opcode is 3
|
||||
// bytes so we skip those
|
||||
uint8* ptr = (uint8*)counterstrikesharp::globals::gameConfig->ResolveSignature("IGameSystem_InitAllSystems_pFirst") + 3;
|
||||
|
||||
if (!ptr) {
|
||||
CSSHARP_CORE_ERROR("Failed to InitGameSystems, see warnings above.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Grab the offset as 4 bytes
|
||||
uint32 offset = *(uint32*)ptr;
|
||||
|
||||
// Go to the next instruction, which is the starting point of the relative jump
|
||||
ptr += 4;
|
||||
|
||||
// Now grab our pointer
|
||||
CBaseGameSystemFactory::sm_pFirst = (CBaseGameSystemFactory**)(ptr + offset);
|
||||
|
||||
// And insert the game system(s)
|
||||
CGameSystem::sm_Factory = new CGameSystemStaticFactory<CGameSystem>("CSSharp_GameSystem", &g_GameSystem);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GS_EVENT_MEMBER(CGameSystem, BuildGameSessionManifest)
|
||||
{
|
||||
IEntityResourceManifest* pResourceManifest = msg->m_pResourceManifest;
|
||||
|
||||
CSSHARP_CORE_INFO("CGameSystem::BuildGameSessionManifest");
|
||||
|
||||
counterstrikesharp::globals::serverManager.OnPrecacheResources(pResourceManifest);
|
||||
}
|
||||
47
src/core/game_system.h
Normal file
47
src/core/game_system.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* =============================================================================
|
||||
* CS2Fixes
|
||||
* Copyright (C) 2023-2024 Source2ZE
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program 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
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/log.h"
|
||||
#include "entitysystem.h"
|
||||
#include "igamesystemfactory.h"
|
||||
|
||||
bool InitGameSystems();
|
||||
|
||||
class CGameSystem : public CBaseGameSystem
|
||||
{
|
||||
public:
|
||||
GS_EVENT(BuildGameSessionManifest);
|
||||
|
||||
void Shutdown() override
|
||||
{
|
||||
CSSHARP_CORE_INFO("CGameSystem::Shutdown");
|
||||
delete sm_Factory;
|
||||
}
|
||||
|
||||
void SetGameSystemGlobalPtrs(void* pValue) override
|
||||
{
|
||||
if (sm_Factory)
|
||||
sm_Factory->SetGlobalPtr(pValue);
|
||||
}
|
||||
|
||||
bool DoesGameSystemReallocate() override { return sm_Factory->ShouldAutoAdd(); }
|
||||
|
||||
static IGameSystemFactory* sm_Factory;
|
||||
};
|
||||
@@ -19,6 +19,8 @@
|
||||
#include "core/log.h"
|
||||
#include "scripting/callback_manager.h"
|
||||
|
||||
#include "core/game_system.h"
|
||||
|
||||
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);
|
||||
@@ -56,6 +58,8 @@ void ServerManager::OnAllInitialized() {
|
||||
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");
|
||||
|
||||
on_server_precache_resources = globals::callbackManager.CreateCallback("OnServerPrecacheResources");
|
||||
}
|
||||
|
||||
void ServerManager::OnShutdown() {
|
||||
@@ -81,6 +85,8 @@ void ServerManager::OnShutdown() {
|
||||
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);
|
||||
|
||||
globals::callbackManager.ReleaseCallback(on_server_precache_resources);
|
||||
}
|
||||
|
||||
void* ServerManager::GetEconItemSystem()
|
||||
@@ -197,4 +203,16 @@ void ServerManager::AddTaskForNextWorldUpdate(std::function<void()>&& task)
|
||||
std::lock_guard<std::mutex> lock(m_nextWorldUpdateTasksLock);
|
||||
m_nextWorldUpdateTasks.push_back(std::forward<decltype(task)>(task));
|
||||
}
|
||||
|
||||
void ServerManager::OnPrecacheResources(IEntityResourceManifest* pResourceManifest)
|
||||
{
|
||||
CSSHARP_CORE_TRACE("Precache resources");
|
||||
auto callback = globals::serverManager.on_server_precache_resources;
|
||||
if (callback && callback->GetFunctionCount()) {
|
||||
callback->ScriptContext().Reset();
|
||||
callback->ScriptContext().Push(pResourceManifest);
|
||||
callback->Execute();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace counterstrikesharp
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "core/global_listener.h"
|
||||
#include "scripting/script_engine.h"
|
||||
|
||||
#include "core/game_system.h"
|
||||
|
||||
namespace counterstrikesharp {
|
||||
class ScriptCallback;
|
||||
|
||||
@@ -31,7 +33,8 @@ public:
|
||||
void OnShutdown() override;
|
||||
void* GetEconItemSystem();
|
||||
bool IsPaused();
|
||||
void AddTaskForNextWorldUpdate(std::function<void()> &&task);
|
||||
void AddTaskForNextWorldUpdate(std::function<void()>&& task);
|
||||
void OnPrecacheResources(IEntityResourceManifest* pResourceManifest);
|
||||
|
||||
private:
|
||||
void ServerHibernationUpdate(bool bHibernating);
|
||||
@@ -51,6 +54,8 @@ private:
|
||||
ScriptCallback *on_server_update_when_not_in_game;
|
||||
ScriptCallback *on_server_pre_world_update;
|
||||
|
||||
ScriptCallback *on_server_precache_resources;
|
||||
|
||||
std::vector<std::function<void()>> m_nextWorldUpdateTasks;
|
||||
std::mutex m_nextWorldUpdateTasksLock;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "core/log.h"
|
||||
#include "core/coreconfig.h"
|
||||
#include "core/gameconfig.h"
|
||||
#include "core/game_system.h"
|
||||
#include "core/timer_system.h"
|
||||
#include "core/utils.h"
|
||||
#include "core/managers/entity_manager.h"
|
||||
@@ -144,6 +145,12 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
|
||||
CSSHARP_CORE_ERROR("Failed to initialize .NET runtime");
|
||||
}
|
||||
|
||||
if (!InitGameSystems()) {
|
||||
CSSHARP_CORE_ERROR("Failed to initialize GameSystem!");
|
||||
return false;
|
||||
}
|
||||
CSSHARP_CORE_INFO("Initialized GameSystem.");
|
||||
|
||||
CSSHARP_CORE_INFO("Hooks added.");
|
||||
|
||||
// Used by Metamod Console Commands
|
||||
|
||||
@@ -4,7 +4,7 @@ rm -rf temp/ generated/
|
||||
mkdir -p temp generated
|
||||
mkdir -p generated
|
||||
|
||||
cp ../../libraries/GameTracking-CS2/Protobufs/*.proto temp/
|
||||
cp ../../libraries/Protobufs/csgo/*.proto temp/
|
||||
cp -r google/ temp/
|
||||
|
||||
for file in temp/*.proto; do
|
||||
|
||||
@@ -21,8 +21,8 @@ TRACE_FILTER_PROXY_SET_TRACE_TYPE_CALLBACK: trace_filter:pointer, callback:point
|
||||
TRACE_FILTER_PROXY_SET_SHOULD_HIT_ENTITY_CALLBACK: trace_filter:pointer, callback:pointer -> void
|
||||
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
|
||||
QUEUE_TASK_FOR_NEXT_FRAME: callback:func -> void
|
||||
QUEUE_TASK_FOR_NEXT_WORLD_UPDATE: callback:func -> 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
|
||||
@@ -29,6 +29,12 @@
|
||||
namespace counterstrikesharp {
|
||||
|
||||
CBaseEntity* GetEntityFromIndex(ScriptContext& script_context) {
|
||||
if (!globals::entitySystem)
|
||||
{
|
||||
script_context.ThrowNativeError("Entity system is not yet initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto entityIndex = script_context.GetArgument<int>(0);
|
||||
|
||||
return globals::entitySystem->GetBaseEntity(CEntityIndex(entityIndex));
|
||||
@@ -47,6 +53,11 @@ const char* GetDesignerName(ScriptContext& scriptContext) {
|
||||
}
|
||||
|
||||
void* GetEntityPointerFromHandle(ScriptContext& scriptContext) {
|
||||
if (!globals::entitySystem) {
|
||||
scriptContext.ThrowNativeError("Entity system is not yet initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto handle = scriptContext.GetArgument<CEntityHandle*>(0);
|
||||
|
||||
if (!handle->IsValid()) {
|
||||
@@ -57,6 +68,11 @@ void* GetEntityPointerFromHandle(ScriptContext& scriptContext) {
|
||||
}
|
||||
|
||||
void* GetEntityPointerFromRef(ScriptContext& scriptContext) {
|
||||
if (!globals::entitySystem) {
|
||||
scriptContext.ThrowNativeError("Entity system yet is not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ref = scriptContext.GetArgument<unsigned int>(0);
|
||||
|
||||
if (ref == INVALID_EHANDLE_INDEX) {
|
||||
@@ -87,6 +103,11 @@ unsigned int GetRefFromEntityPointer(ScriptContext& scriptContext) {
|
||||
}
|
||||
|
||||
bool IsRefValidEntity(ScriptContext& scriptContext) {
|
||||
if (!globals::entitySystem) {
|
||||
scriptContext.ThrowNativeError("Entity system yet is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ref = scriptContext.GetArgument<unsigned int>(0);
|
||||
|
||||
if (ref == INVALID_EHANDLE_INDEX) {
|
||||
@@ -110,10 +131,20 @@ void PrintToConsole(ScriptContext& scriptContext) {
|
||||
}
|
||||
|
||||
CEntityIdentity* GetFirstActiveEntity(ScriptContext& script_context) {
|
||||
if (!globals::entitySystem) {
|
||||
script_context.ThrowNativeError("Entity system yet is not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return globals::entitySystem->m_EntityList.m_pFirstActiveEntity;
|
||||
}
|
||||
|
||||
void* GetConcreteEntityListPointer(ScriptContext& script_context) {
|
||||
if (!globals::entitySystem) {
|
||||
script_context.ThrowNativeError("Entity system yet is not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &globals::entitySystem->m_EntityList;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace CodeGen.Natives
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Generators.GenerateNatives();
|
||||
Generators.GenerateGameEvents();
|
||||
Generators.GenerateGameEvents().GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,16 +30,25 @@ public partial class Generators
|
||||
public string? Comment { get; set; }
|
||||
}
|
||||
|
||||
private static List<GameEvent> GetGameEvents()
|
||||
private static HttpClient _httpClient = new HttpClient();
|
||||
private static string BaseUrl = "https://raw.githubusercontent.com/SteamDatabase/GameTracking-CS2/master/";
|
||||
|
||||
private static List<string> GameEventFiles = new List<string>()
|
||||
{
|
||||
"game/core/pak01_dir/resource/core.gameevents",
|
||||
"game/csgo/pak01_dir/resource/game.gameevents",
|
||||
"game/csgo/pak01_dir/resource/mod.gameevents"
|
||||
};
|
||||
|
||||
private static async Task<List<GameEvent>> GetGameEvents()
|
||||
{
|
||||
// temporary, not committing resource files directly to git for now
|
||||
var pathToSearch = @"/home/michael/Steam/cs2-ds/game/csgo/events/resource";
|
||||
if (!Directory.Exists(pathToSearch)) Environment.Exit(0);
|
||||
var allGameEvents = new Dictionary<string, GameEvent>();
|
||||
|
||||
foreach (string file in Directory.EnumerateFiles(pathToSearch, "*.gameevents", SearchOption.AllDirectories).OrderBy(Path.GetFileName))
|
||||
foreach (string url in GameEventFiles)
|
||||
// foreach (string file in Directory.EnumerateFiles(pathToSearch, "*.gameevents", SearchOption.AllDirectories).OrderBy(Path.GetFileName))
|
||||
{
|
||||
var deserialized = VdfConvert.Deserialize(File.ReadAllText(file));
|
||||
var file = await _httpClient.GetStringAsync($"{BaseUrl}/{url}");
|
||||
var deserialized = VdfConvert.Deserialize(file);
|
||||
|
||||
var properties =
|
||||
deserialized.Value.Where(x => x.Type == VTokenType.Property);
|
||||
@@ -79,12 +88,9 @@ public partial class Generators
|
||||
return allGameEvents.Values.ToList();
|
||||
}
|
||||
|
||||
public static void GenerateGameEvents()
|
||||
public static async Task GenerateGameEvents()
|
||||
{
|
||||
var pathToSearch = @"/home/michael/Steam/cs2-ds/game/csgo/events/resource";
|
||||
if (!Directory.Exists(pathToSearch)) return;
|
||||
|
||||
var allGameEvents = GetGameEvents();
|
||||
var allGameEvents = await GetGameEvents();
|
||||
|
||||
var gameEventsString = string.Join("\n", allGameEvents.OrderBy(x => x.NamePascalCase).Select(gameEvent =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user