Compare commits

...

18 Commits

Author SHA1 Message Date
B3none
289f95a6b7 Add DarkRed to ChatColors class to follow naming convention (#245) 2024-01-09 16:20:51 +10:00
roflmuffin
7b45a884d4 chore: remove schema::GetOffset warning message 2023-12-28 18:01:44 +10:00
Michael Wilson
6ea6d0a22d feat: add state changed and network state changed handler (#229) 2023-12-27 20:56:38 +10:00
roflmuffin
12523455c0 fix: bad max player count 2023-12-27 15:30:57 +10:00
Michael Wilson
db63fdc00c feat: add AcceptInput method to CEntityInstance (#228) 2023-12-27 14:32:37 +10:00
roflmuffin
57747f2e1c fix: update GetPlayers to use slot access 2023-12-27 14:00:11 +10:00
roflmuffin
66b5f77a2d feat: add GetMaxClients native, fixes #184 2023-12-27 13:40:06 +10:00
Michael Wilson
8dbcb6d531 chore: update hl2sdk (#227) 2023-12-27 13:26:59 +10:00
roflmuffin
2f0d34b271 chore: disable compat suppression file by default 2023-12-26 17:36:55 +10:00
roflmuffin
2a59544fbc feat: add ApiCompat checker to determine breaking API changes 2023-12-26 17:30:17 +10:00
pedrotski
f80f2ae949 Fix getting started image (#220) 2023-12-26 17:09:32 +10:00
roflmuffin
f68a0abc61 fix: ignore null designer names in FindAllEntitiesByDesignerName Fixes #212 2023-12-26 17:00:58 +10:00
Sürat ÜNLÜGÜN
a07dd9d7d4 Fix CanPlayerTarget Immu (#222) 2023-12-25 23:26:34 +10:00
B3none
d527038fba Added a parameter for people to optionally remove the entity when calling RemoveItemByDesignerName (#214) 2023-12-24 19:40:23 +10:00
pedrotski
ca85922270 Update Getting Started Guide (#217) 2023-12-24 10:02:41 +10:00
B3none
b837479f98 Added links to referenced projects in the credits for the README.md (#210) 2023-12-19 11:15:30 +10:00
Hackmastr
1e42f72655 Docs: Using DOTNET_SYSTEM_GLOBALIZATION_INVARIANT is no longer valid. (#209)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2023-12-19 11:14:31 +10:00
Michael Wilson
1ad1828e30 feat: Add SchemaMember attribute to schema objects` (#208) 2023-12-17 13:12:26 +10:00
31 changed files with 9491 additions and 4800 deletions

View File

@@ -18,6 +18,7 @@ SET(SOURCE_FILES
src/mm_plugin.h
libraries/hl2sdk-cs2/tier1/convar.cpp
libraries/hl2sdk-cs2/tier1/generichash.cpp
libraries/hl2sdk-cs2/entity2/entityidentity.cpp
libraries/hl2sdk-cs2/entity2/entitysystem.cpp
libraries/dotnet/hostfxr.h
libraries/dotnet/coreclr_delegates.h

View File

@@ -43,7 +43,7 @@ These features are the core of the platform and work pretty well/have a low risk
- [Read the docs](https://docs.cssharp.dev/): Getting started guide, hello world plugin example
- [Issue tracker](https://github.com/roflmuffin/CounterStrikeSharp/issues): Raise any issues here
- [Builds](https://github.com/roflmuffin/CounterStrikeSharp/actions): Download latest unstable dev snapshot
- [Install Docs](https://docs.cssharp.dev/guides/getting-started/): Installation instructions
- [Install Docs](https://docs.cssharp.dev/docs/guides/getting-started.html): Installation instructions
- [Example Plugin](managed/TestPlugin/TestPlugin.cs): Test plugin with basic functionality
## Examples
@@ -91,8 +91,8 @@ public class HelloWorldPlugin : BasePlugin
## Credits
A lot of code has been borrowed from SourceMod as well as Source.Python, two pioneering source engine plugin frameworks which this project lends a lot of its credit to.
I've also used the scripting context & native system that is implemented in FiveM for GTA5. Also shoutout to the [CS2Fixes](https://github.com/Source2ZE/CS2Fixes) project for providing good reverse-engineering information so shortly after CS2 release.
A lot of code has been borrowed from [SourceMod](https://github.com/alliedmodders/sourcemod) as well as [Source.Python](https://github.com/Source-Python-Dev-Team/Source.Python), two pioneering source engine plugin frameworks which this project lends a lot of its credit to.
I've also used the scripting context & native system that is implemented in [FiveM](https://github.com/citizenfx/fivem) for GTA5. Also shoutout to the [CS2Fixes](https://github.com/Source2ZE/CS2Fixes) project for providing good reverse-engineering information so shortly after CS2 release.
## How to Build

View File

@@ -113,6 +113,13 @@
"linux": "\\x48\\x85\\xFF\\x74\\x4B\\x55\\x48\\x89\\xE5\\x41\\x56"
}
},
"CEntityInstance_AcceptInput": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x74\\x24\\x18\\x57\\x48\\x83\\xEC\\x40\\x49\\x8B\\xF0",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x49\\x89\\xFF\\x41\\x56\\x48\\x8D\\x7D\\xC0"
}
},
"LegacyGameEventListener": {
"signatures": {
"library": "server",
@@ -160,6 +167,20 @@
"linux": "\\x55\\xBA\\xFF\\xFF\\xFF\\xFF\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x49"
}
},
"StateChanged": {
"signatures": {
"library": "server",
"windows": "\\x40\\x55\\x53\\x56\\x41\\x55\\x41\\x57\\x48\\x8D\\x6C\\x24\\xB0",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x53\\x89\\xD3"
}
},
"NetworkStateChanged": {
"signatures": {
"library": "server",
"windows": "\\x4C\\x8B\\xC9\\x48\\x8B\\x09\\x48\\x85\\xC9\\x74\\x2A\\x48\\x8B\\x41\\x10",
"linux": "\\x4C\\x8B\\x07\\x4D\\x85\\xC0\\x74\\x2A\\x49\\x8B\\x40\\x10"
}
},
"GameEntitySystem": {
"offsets": {
"windows": 88,

View File

@@ -5,31 +5,52 @@ description: How to get started installing & using CounterStrikeSharp.
# Getting Started
How to get started installing & using CounterStrikeSharp.
In this guide you will learn how to install CounterStrikeSharp onto your vanilla Counter-Strike 2 server. `CounterStrikeSharp` uses `Metamod:Source` as its main way of communicating with the game server, so both frameworks will need to be installed.
If you're more of a visual person, here is a <a href="https://www.youtube.com/watch?v=FlsKzStHJuY" target="_blank">Youtube video</a> that covers everything.
## Prerequisites
- <a href="https://www.sourcemm.net/downloads.php/?branch=master" target="_blank">Metamod: Source 2.X Dev Build</a>
- <a href="https://github.com/roflmuffin/CounterStrikeSharp/releases" target="_blank">CounterStrikeSharp With Runtime</a>
## Installing Metamod
`CounterStrikeSharp` uses `Metamod:Source` as its main way of communicating with the game server. To install it, you can follow the detailed instructions found <a href="https://cs2.poggu.me/metamod/installation/" target="_blank">here</a>.
1. Extract Metamod and copy the `/addons/` directory to `/game/csgo/`.
2. Inside `/game/csgo/`, locate `gameinfo.gi`.
3. Create a new line underneath `Game_LowViolence csgo_lv` and add `Game csgo/addons/metamod`.
4. Restart your game server.
Your `gameinfo.gi` should look like <a href="../../images/gameinfogi-example.png" target="_blank">this</a>. Type `meta list` in your server console to see if Metamod is loaded.
## Installing CounterStrikeSharp
Download the latest release of CounterStrikeSharp from <a href="https://github.com/roflmuffin/CounterStrikeSharp/releases/latest" target="_blank">GitHub releases pages</a>.
1. Extract CounterStrikeSharp and copy the `/addons/` directory to `/game/csgo/`.
2. Restart your game server.
Running the command `meta list` in the console should show 1 plugin loaded 🎉
```shell
meta list
Listing 1 plugin:
[01] CounterStrikeSharp (0.1.0) by Roflmuffin
```
> [!CAUTION]
> If this is your first time installing, you will need to download the `with-runtime` version. This includes a copy of the .NET runtime, which is required to run the plugin.
> Depending on the os you might also either need to install `libicu` / `icu-libs` / `libicu-dev` using your package manager for .NET to run or setting `DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true` in your servers environment variables. You can find more infos about that <a href="https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-invariant-mode.md#enabling-the-invariant-mode" target="_blank">here</a>
>
> Subsequent upgrades will not require the runtime, unless a version bump of the .NET runtime is required (i.e. from 7.0.x to 8.0.x). We will inform you when this occurs.
> For Windows servers, you must have <a href="https://aka.ms/vs/17/release/vc_redist.x64.exe" target="_blank">Visual Studio Redistributables</a> installed otherwise CounterStrikeSharp will not work.
> [!CAUTION]
> For Windows users, you must ensure that you have installed **Visual Studio Redistributables**.
> If not, you can download it here: <a href="https://aka.ms/vs/17/release/vc_redist.x64.exe" target="_blank">Download</a>
> > This link will download VC Redistributable directly.
>
> You must install it before starting the server, otherwise CSS will not work!
## Upgrading CounterStrikeSharp
To upgrade CounterStrikeSharp you simply need to download the latest release and copy it to your server, the same as the original installation.
Extract the `addons` folder to the `/csgo/` directory of the dedicated server. The contents of your addons folder should contain both the `counterstrikesharp` folder and the `metamod` folder as seen below.
CounterStrikeSharp is designed in a way where your configuration files will not be overwritten if you do this. As CounterStrikeSharp is already installed, you may download the non `with-runtime` build, but you will need to ensure your .NET runtime is up-to-date yourself.
## Troubleshooting
- If this is your first time installing, you **MUST** download the `with-runtime` version. This includes a copy of the .NET runtime, which is required to run the plugin.
- Depending on your OS you might also either need to install `libicu` / `icu-libs` / `libicu-dev` using your package manager for .NET to run.
- If you get `Unknown Command` when typing `meta list` into your console, double-check the folders are copied over correctly and that your `gameinfo.gi` file is correctly modified.
Your folder structure should look like this:
```shell
<server_path>/game/csgo/addons > tree -L 2
@@ -49,15 +70,3 @@ addons
├── metamod.vdf
└── metamod_x64.vdf
```
## Start the Server
Launch your CS2 dedicated server as normal. If everything is working correctly, you should see a message in the console that says `CSSharp: CounterStrikeSharp.API Loaded Successfully.`.
Running the command `meta list` in the console should show 1 plugin loaded 🎉
```shell
meta list
Listing 1 plugin:
[01] CounterStrikeSharp (0.1.0) by Roflmuffin
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

View File

@@ -14,8 +14,11 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING
# TODO: Use C++20 instead.
set(CMAKE_CXX_STANDARD 17)
Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
Set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
if (LINUX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
endif()
set(CMAKE_STATIC_LIBRARY_PREFIX "")
set(SOURCESDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2)
@@ -51,8 +54,4 @@ include_directories(
libraries
)
include(${CMAKE_CURRENT_LIST_DIR}/metamod/configure_metamod.cmake)
if (LINUX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
endif()
include(${CMAKE_CURRENT_LIST_DIR}/metamod/configure_metamod.cmake)

Binary file not shown.

View File

@@ -312,6 +312,16 @@ namespace CounterStrikeSharp.API.Core
}
}
public static int GetMaxClients(){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.SetIdentifier(0x5DF2E20D);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int));
}
}
public static void IssueServerCommand(string command){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -1064,6 +1074,18 @@ namespace CounterStrikeSharp.API.Core
}
}
public static bool IsSchemaFieldNetworked(string classname, string propname){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(classname);
ScriptContext.GlobalScriptContext.Push(propname);
ScriptContext.GlobalScriptContext.SetIdentifier(0xFE413B0C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool));
}
}
public static T GetSchemaValueByName<T>(IntPtr instance, int returntype, string classname, string propname){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();

View File

@@ -0,0 +1,14 @@
namespace CounterStrikeSharp.API.Core.Attributes;
[AttributeUsage(AttributeTargets.Property)]
public class SchemaMemberAttribute : Attribute
{
public string ClassName { get; }
public string MemberName { get; }
public SchemaMemberAttribute(string className, string memberName)
{
ClassName = className;
MemberName = memberName;
}
}

View File

@@ -58,6 +58,24 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
{
return !Equals(left, right);
}
/// <summary>
/// Calls a named input method on an entity.
/// <example>
/// <code>
/// entity.AcceptInput("Break");
/// </code>
/// </example>
/// </summary>
/// <param name="inputName">Input action name</param>
/// <param name="activator">Entity which initiated the action, <see langword="null"/> for no entity</param>
/// <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>
public void AcceptInput(string inputName, CEntityInstance? activator = null, CEntityInstance? caller = null, string value = "", int outputId = 0)
{
VirtualFunctions.AcceptInput(Handle, inputName, activator?.Handle ?? IntPtr.Zero, caller?.Handle ?? IntPtr.Zero, value, 0);
}
}
public partial class CEntityIdentity

File diff suppressed because it is too large Load Diff

View File

@@ -4,9 +4,8 @@
<EnableDynamicLoading>true</EnableDynamicLoading>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnablePackageValidation>true</EnablePackageValidation>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn);CS1591;CP0003</NoWarn>
<Nullable>enable</Nullable>
<Authors>Roflmuffin</Authors>
<Description>Official server side runtime assembly for CounterStrikeSharp</Description>
<PackageProjectUrl>http://docs.cssharp.dev/</PackageProjectUrl>
@@ -16,6 +15,10 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<PropertyGroup>
<ApiCompatValidateAssemblies>true</ApiCompatValidateAssemblies>
<ApiCompatContractAssembly>.\ApiCompat\v133.dll</ApiCompatContractAssembly>
</PropertyGroup>
<ItemGroup>
<None Remove="Modules\Commands\CommandInfo" />
<None Remove="Modules\Disabled\**" />
@@ -23,6 +26,7 @@
<ItemGroup>
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.DotNet.ApiCompat.Task" Version="7.0.404" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.3" />

View File

@@ -494,7 +494,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
var callerData = GetPlayerAdminData(caller.AuthorizedSteamID);
if (callerData == null) return false;
var targetData = GetPlayerAdminData(caller.AuthorizedSteamID);
var targetData = GetPlayerAdminData(target.AuthorizedSteamID);
if (targetData == null) return true;
return callerData.Immunity >= targetData.Immunity;
@@ -514,7 +514,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
var callerData = GetPlayerAdminData(caller);
if (callerData == null) return false;
var targetData = GetPlayerAdminData(caller);
var targetData = GetPlayerAdminData(target);
if (targetData == null) return true;
return callerData.Immunity >= targetData.Immunity;

View File

@@ -76,8 +76,7 @@ namespace CounterStrikeSharp.API.Modules.Events
SetInt(name, i);
break;
case var _ when value is CCSPlayerController player:
// When I was testing this, the code seems to expect a slot, even though it is called index
SetEntityIndex(name, (int)player.Index - 1);
NativeAPI.SetEventPlayerController(Handle, name, player.Handle);
break;
case var _ when value is string s:
SetString(name, s);

View File

@@ -69,6 +69,11 @@ public class Schema
return offset;
}
public static bool IsSchemaFieldNetworked(string className, string propertyName)
{
return NativeAPI.IsSchemaFieldNetworked(className, propertyName);
}
public static T GetSchemaValue<T>(IntPtr handle, string className, string propertyName)
{

View File

@@ -80,4 +80,15 @@ public static class VirtualFunctions
public static MemoryFunctionVoid<IntPtr, IntPtr> RemovePlayerItemFunc =
new(GameData.GetSignature("CBasePlayerPawn_RemovePlayerItem"));
public static Action<IntPtr, IntPtr> RemovePlayerItemVirtual = RemovePlayerItemFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, string, IntPtr, IntPtr, string, int> AcceptInputFunc = new(GameData.GetSignature("CEntityInstance_AcceptInput"));
public static Action<IntPtr, string, IntPtr, IntPtr, string, int> AcceptInput = AcceptInputFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, IntPtr, int, short, short> StateChangedFunc =
new(GameData.GetSignature("StateChanged"));
public static Action<IntPtr, IntPtr, int, short, short> StateChanged = StateChangedFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, int, long> NetworkStateChangedFunc = new("NetworkStateChanged");
public static Action<IntPtr, int, long> NetworkStateChanged = NetworkStateChangedFunc.Invoke;
}

View File

@@ -20,7 +20,7 @@ public class ChatColors
{
public static char Default = '\x01';
public static char White = '\x01';
public static char Darkred = '\x02';
public static char DarkRed = '\x02';
public static char Green = '\x04';
public static char LightYellow = '\x09';
public static char LightBlue = '\x0B';
@@ -39,4 +39,7 @@ public class ChatColors
public static char Magenta = '\x0E';
public static char LightRed = '\x0F';
public static char Orange = '\x10';
}
[Obsolete("Use ChatColors.DarkRed instead.")]
public static char Darkred = '\x02';
}

View File

@@ -75,7 +75,7 @@ namespace CounterStrikeSharp.API
public static string GameDirectory => NativeAPI.GetGameDirectory();
public static int MaxPlayers => NativeAPI.GetCommandParamValue("-maxplayers", DataType.DATA_TYPE_INT, 64);
public static int MaxPlayers => NativeAPI.GetMaxClients();
public static bool IsMapValid(string mapName) => NativeAPI.IsMapValid(mapName);

View File

@@ -21,8 +21,10 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using CounterStrikeSharp.API.Core.Logging;
using CounterStrikeSharp.API.Modules.Commands.Targeting;
using CounterStrikeSharp.API.Modules.Entities;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API
{
@@ -76,6 +78,11 @@ namespace CounterStrikeSharp.API
}
public static bool RemoveItemByDesignerName(this CCSPlayerController player, string designerName)
{
return RemoveItemByDesignerName(player, designerName, false);
}
public static bool RemoveItemByDesignerName(this CCSPlayerController player, string designerName, bool shouldRemoveEntity)
{
CHandle<CBasePlayerWeapon>? item = null;
if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.WeaponServices == null) return false;
@@ -90,9 +97,15 @@ namespace CounterStrikeSharp.API
item = weapon;
}
if(item != null && item.Value != null)
if (item != null && item.Value != null)
{
player.PlayerPawn.Value.RemovePlayerItem(item.Value);
if (shouldRemoveEntity)
{
item.Value.Remove();
}
return true;
}
@@ -104,7 +117,7 @@ namespace CounterStrikeSharp.API
var pEntity = new CEntityIdentity(EntitySystem.FirstActiveEntity);
for (; pEntity != null && pEntity.Handle != IntPtr.Zero; pEntity = pEntity.Next)
{
if (!pEntity.DesignerName.Contains(designerName)) continue;
if (pEntity.DesignerName == null || !pEntity.DesignerName.Contains(designerName)) continue;
yield return new PointerTo<T>(pEntity.Handle).Value;
}
}
@@ -125,9 +138,9 @@ namespace CounterStrikeSharp.API
{
List<CCSPlayerController> players = new();
for (int i = 1; i <= Server.MaxPlayers; i++)
for (int i = 0; i < Server.MaxPlayers; i++)
{
var controller = GetPlayerFromIndex(i);
var controller = GetPlayerFromSlot(i);
if (!controller.IsValid || controller.UserId == -1)
continue;
@@ -185,5 +198,38 @@ namespace CounterStrikeSharp.API
return (T)Activator.CreateInstance(typeof(T), pointerTo)!;
}
private static int FindSchemaChain(string className) => Schema.GetSchemaOffset(className, "__m_pChainEntity");
/// <summary>
/// Marks a field as changed for network transmission.
/// Not all schema fields are network enabled, so please check the schema before using this.
/// </summary>
/// <param name="entity">Entity to update</param>
/// <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>
public static void SetStateChanged(CBaseEntity entity, string className, string fieldName, int extraOffset = 0)
{
if (!Schema.IsSchemaFieldNetworked(className, fieldName))
{
Application.Instance.Logger.LogWarning("Field {ClassName}:{FieldName} is not networked, but SetStateChanged was called on it.", className, fieldName);
return;
}
int offset = Schema.GetSchemaOffset(className, fieldName);
int chainOffset = FindSchemaChain(className);
if (chainOffset != 0)
{
VirtualFunctions.NetworkStateChanged(entity.Handle + chainOffset, offset + extraOffset, 0xFFFFFFFF);
return;
}
VirtualFunctions.StateChanged(entity.NetworkTransmitComponent.Handle, entity.Handle, offset + extraOffset, -1, -1);
entity.LastNetworkChange = Server.CurrentTime;
entity.IsSteadyState.Clear();
}
}
}

View File

@@ -233,7 +233,9 @@ internal static partial class Program
continue;
// Putting these in the too hard basket for now.
if (field.Name == "m_VoteOptions" || field.Name == "m_aShootSounds") continue;
if (field.Name == "m_VoteOptions" || field.Name == "m_aShootSounds" || field.Name == "m_pVecRelationships") continue;
if (IgnoreClasses.Contains(field.Type.Name)) continue;
if (field.Type.Category == SchemaTypeCategory.Bitfield) continue;
if (field.Type is { Category: SchemaTypeCategory.Atomic, Atomic: SchemaAtomicCategory.Collection })
{
@@ -242,9 +244,10 @@ internal static partial class Program
var handleParams = $"this.Handle, \"{schemaClassName}\", \"{field.Name}\"";
builder.AppendLine($" // {field.Name}");
builder.AppendLine($"\t// {field.Name}");
builder.AppendLine($"\t[SchemaMember(\"{schemaClassName}\", \"{field.Name}\")]");
if (field.Type is { Category: SchemaTypeCategory.FixedArray, CsTypeName: "string" })
if (field.Type is { Category: SchemaTypeCategory.FixedArray, CsTypeName: "string" } or { Category: SchemaTypeCategory.Ptr, CsTypeName: "string" })
{
var getter = $"return Schema.GetString({handleParams});";
var setter = $"Schema.SetString({handleParams}, value);";

View File

@@ -4816,10 +4816,10 @@
}
},
{
"name": "m_eThrowStatus",
"name": "m_bThrowAnimating",
"type": {
"category": 6,
"name": "EGrenadeThrowState"
"category": 0,
"name": "bool"
}
},
{
@@ -4849,6 +4849,40 @@
"category": 5,
"name": "GameTime_t"
}
},
{
"name": "m_bJustPulledPin",
"type": {
"category": 0,
"name": "bool"
}
},
{
"name": "m_nNextHoldTick",
"type": {
"category": 5,
"name": "GameTick_t"
}
},
{
"name": "m_flNextHoldFrac",
"type": {
"category": 0,
"name": "float32"
}
},
{
"name": "m_hSwitchToWeaponAfterThrow",
"type": {
"atomic": 1,
"category": 4,
"inner": {
"category": 5,
"name": "CCSWeaponBase"
},
"name": "CHandle< CCSWeaponBase >",
"outer": "CHandle"
}
}
],
"parent": "CCSWeaponBase"
@@ -6312,7 +6346,7 @@
"category": 0,
"name": "char"
},
"name": "char[4096]"
"name": "char[260]"
}
},
{
@@ -6842,6 +6876,13 @@
"name": "int64"
}
},
{
"name": "m_nLastRealCommandNumberExecuted",
"type": {
"category": 0,
"name": "int32"
}
},
{
"name": "m_iIgnoreGlobalChat",
"type": {
@@ -16738,10 +16779,17 @@
}
},
{
"name": "m_bPlayerFireEventIsPrimary",
"name": "m_ePlayerFireEvent",
"type": {
"category": 0,
"name": "bool"
"category": 6,
"name": "PlayerAnimEvent_t"
}
},
{
"name": "m_ePlayerFireEventAttackType",
"type": {
"category": 6,
"name": "WeaponAttackType_t"
}
},
{
@@ -16799,7 +16847,7 @@
"category": 5,
"name": "HSequence"
},
"name": "HSequence[6]"
"name": "HSequence[7]"
}
},
{
@@ -16951,10 +16999,17 @@
}
},
{
"name": "m_flPostponeFireReadyTime",
"name": "m_nPostponeFireReadyTicks",
"type": {
"category": 5,
"name": "GameTime_t"
"name": "GameTick_t"
}
},
{
"name": "m_flPostponeFireReadyFrac",
"type": {
"category": 0,
"name": "float32"
}
},
{
@@ -32791,40 +32846,6 @@
],
"parent": "CLogicalEntity"
},
"CLogicEventListener": {
"fields": [
{
"name": "m_strEventName",
"type": {
"atomic": 0,
"category": 4,
"name": "CUtlString"
}
},
{
"name": "m_bIsEnabled",
"type": {
"category": 0,
"name": "bool"
}
},
{
"name": "m_nTeam",
"type": {
"category": 0,
"name": "int32"
}
},
{
"name": "m_OnEventFired",
"type": {
"category": 5,
"name": "CEntityIOOutput"
}
}
],
"parent": "CLogicalEntity"
},
"CLogicGameEvent": {
"fields": [
{
@@ -34452,35 +34473,7 @@
"parent": "CLogicalEntity"
},
"CMelee": {
"fields": [
{
"name": "m_flThrowAt",
"type": {
"category": 5,
"name": "GameTime_t"
}
},
{
"name": "m_hThrower",
"type": {
"atomic": 1,
"category": 4,
"inner": {
"category": 5,
"name": "CBaseEntity"
},
"name": "CHandle< CBaseEntity >",
"outer": "CHandle"
}
},
{
"name": "m_bDidThrowDamage",
"type": {
"category": 0,
"name": "bool"
}
}
],
"fields": [],
"parent": "CCSWeaponBase"
},
"CMeshletDescriptor": {
@@ -54566,10 +54559,6 @@
"fields": [],
"parent": "CBaseTrigger"
},
"CTriggerHostageReset": {
"fields": [],
"parent": "CBaseTrigger"
},
"CTriggerHurt": {
"fields": [
{
@@ -70344,7 +70333,7 @@
"category": 0,
"name": "char"
},
"name": "char[4096]"
"name": "char[260]"
}
},
{
@@ -76074,6 +76063,20 @@
"category": 0,
"name": "float32"
}
},
{
"name": "m_flTickInterval",
"type": {
"category": 0,
"name": "float32"
}
},
{
"name": "m_flTickStartTime",
"type": {
"category": 0,
"name": "float64"
}
}
]
},
@@ -90386,23 +90389,6 @@
}
]
},
"C4LightEffect_t": {
"align": 4,
"items": [
{
"name": "eLightEffectNone",
"value": 0
},
{
"name": "eLightEffectDropped",
"value": 1
},
{
"name": "eLightEffectThirdPersonHeld",
"value": 2
}
]
},
"CAnimationGraphVisualizerPrimitiveType": {
"align": 4,
"items": [
@@ -91374,23 +91360,6 @@
}
]
},
"EGrenadeThrowState": {
"align": 4,
"items": [
{
"name": "NotThrowing",
"value": 0
},
{
"name": "Throwing",
"value": 1
},
{
"name": "ThrowComplete",
"value": 2
}
]
},
"EInButtonState": {
"align": 4,
"items": [
@@ -96428,7 +96397,7 @@
},
{
"name": "ALL_CONTEXTS",
"value": 4293918720
"value": 18446744073708503040
},
{
"name": "ALL_SCENTS",

View File

@@ -40,7 +40,7 @@ public record SchemaFieldType
}
public bool IsString =>
Category == SchemaTypeCategory.FixedArray
(Category == SchemaTypeCategory.FixedArray || Category == SchemaTypeCategory.Ptr)
&& Inner!.Category == SchemaTypeCategory.Builtin
&& Inner.Name == "char";
@@ -98,7 +98,8 @@ public record SchemaFieldType
public string CsTypeName => Category switch
{
SchemaTypeCategory.Builtin => BuiltinToCsKeyword(Name),
SchemaTypeCategory.Ptr => $"{Inner!.CsTypeName}?",
SchemaTypeCategory.Ptr => IsString
? "string" : $"{Inner!.CsTypeName}?",
SchemaTypeCategory.FixedArray => IsString
? "string"
: $"{Inner!.CsTypeName}[]",

View File

@@ -83,8 +83,6 @@ namespace TestPlugin
Logger.LogInformation(
$"Test Plugin has been loaded, and the hot reload flag was {hotReload}, path is {ModulePath}");
Logger.LogWarning($"Max Players: {Server.MaxPlayers}");
VirtualFunctions.SwitchTeamFunc.Hook(hook =>
{
Logger.LogInformation("Switch team func called");
@@ -249,11 +247,13 @@ namespace TestPlugin
// Set player to random colour
player.PlayerPawn.Value.Render = Color.FromArgb(Random.Shared.Next(0, 255),
Random.Shared.Next(0, 255), Random.Shared.Next(0, 255));
Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseModelEntity", "m_clrRender");
activeWeapon.ReserveAmmo[0] = 250;
activeWeapon.Clip1 = 250;
pawn.Health += 5;
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
return HookResult.Continue;
});
@@ -503,6 +503,30 @@ namespace TestPlugin
player.Respawn();
}
[ConsoleCommand("css_break", "Breaks the breakable entities")]
public void OnBreakCommand(CCSPlayerController? player, CommandInfo command)
{
var entities = Utilities.FindAllEntitiesByDesignerName<CBreakable>("prop_dynamic")
.Concat(Utilities.FindAllEntitiesByDesignerName<CBreakable>("func_breakable"));
foreach (var entity in entities)
{
entity.AcceptInput("Break");
}
}
[ConsoleCommand("css_fov", "Sets the player's FOV")]
[CommandHelper(minArgs: 1, usage: "[fov]")]
public void OnFovCommand(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
if (!player.PlayerPawn.IsValid) return;
if (!Int32.TryParse(command.GetArg(1), out var desiredFov)) return;
player.DesiredFOV = (uint)desiredFov;
Utilities.SetStateChanged(player, "CBasePlayerController", "m_iDesiredFOV");
}
[ConsoleCommand("cssharp_attribute", "This is a custom attribute event")]
public void OnCommand(CCSPlayerController? player, CommandInfo command)
{

View File

@@ -115,7 +115,6 @@ SchemaKey schema::GetOffset(const char* className,
SchemaKeyValueMap_t* tableMap = schemaTableMap[tableMapIndex];
int16_t memberIndex = tableMap->Find(memberKey);
if (!tableMap->IsValidIndex(memberIndex)) {
Warning("schema::GetOffset(): '%s' was not found in '%s'!\n", memberName, className);
return {0, 0};
}

View File

@@ -25,9 +25,6 @@
#include "entitysystem.h"
#include "scripting/callback_manager.h"
// variant.h depends on ivscript.h, lets not include the whole thing
DECLARE_POINTER_HANDLE(HSCRIPT);
#include <variant.h>
namespace counterstrikesharp {

View File

@@ -31,9 +31,13 @@
#include "entity2/entitysystem.h"
#include "interfaces/cs2_interfaces.h"
counterstrikesharp::GlobalClass* counterstrikesharp::GlobalClass::head = nullptr;
CGameEntitySystem *GameEntitySystem()
{
return counterstrikesharp::globals::entitySystem;
}
// TODO: Workaround for windows, we __MUST__ have COUNTERSTRIKESHARP_API to handle it.
// like on windows it should be `extern "C" __declspec(dllexport)`, on linux it should be anything else.
DLL_EXPORT void InvokeNative(counterstrikesharp::fxNativeContext& context)

View File

@@ -30,6 +30,7 @@
#include "core/memory.h"
#include "core/log.h"
#include "core/function.h"
#include "core/managers/player_manager.h"
#include "core/managers/server_manager.h"
// clang-format on
@@ -74,6 +75,17 @@ float GetGameFrameTime(ScriptContext& script_context)
double GetEngineTime(ScriptContext& script_context) { return Plat_FloatTime(); }
int GetMaxClients(ScriptContext& script_context)
{
auto globalVars = globals::getGlobalVars();
if (globalVars == nullptr) {
script_context.ThrowNativeError("Global Variables not initialized yet.");
return -1;
}
return globalVars->maxClients;
}
void ServerCommand(ScriptContext& script_context)
{
auto command = script_context.GetArgument<const char*>(0);
@@ -294,6 +306,7 @@ REGISTER_NATIVES(engine, {
ScriptEngine::RegisterNativeHandler("GET_CURRENT_TIME", GetCurrentTime);
ScriptEngine::RegisterNativeHandler("GET_GAMEFRAME_TIME", GetGameFrameTime);
ScriptEngine::RegisterNativeHandler("GET_ENGINE_TIME", GetEngineTime);
ScriptEngine::RegisterNativeHandler("GET_MAX_CLIENTS", GetMaxClients);
ScriptEngine::RegisterNativeHandler("ISSUE_SERVER_COMMAND", ServerCommand);
ScriptEngine::RegisterNativeHandler("PRECACHE_MODEL", PrecacheModel);
ScriptEngine::RegisterNativeHandler("PRECACHE_SOUND", PrecacheSound);

View File

@@ -6,6 +6,7 @@ GET_CURRENT_TIME: -> float
GET_TICK_COUNT: -> int
GET_GAME_FRAME_TIME: -> float
GET_ENGINE_TIME: -> double
GET_MAX_CLIENTS: -> int
ISSUE_SERVER_COMMAND: command:string -> void
PRECACHE_MODEL: name:string -> void
PRECACHE_SOUND: name:string, preload:bool -> bool

View File

@@ -42,6 +42,18 @@ int16 GetSchemaOffset(ScriptContext& script_context)
return m_key.offset;
}
bool IsSchemaFieldNetworked(ScriptContext& script_context)
{
auto className = script_context.GetArgument<const char*>(0);
auto memberName = script_context.GetArgument<const char*>(1);
auto classKey = hash_32_fnv1a_const(className);
auto memberKey = hash_32_fnv1a_const(memberName);
const auto m_key = schema::GetOffset(className, classKey, memberName, memberKey);
return m_key.networked;
}
int GetSchemaClassSize(ScriptContext& script_context)
{
auto className = script_context.GetArgument<const char*>(0);
@@ -150,19 +162,6 @@ void SetSchemaValueByName(ScriptContext& script_context)
auto memberKey = hash_32_fnv1a_const(memberName);
const auto m_key = schema::GetOffset(className, classKey, memberName, memberKey);
const auto m_chain = schema::FindChainOffset(className);
// todo network updates
// if (m_chain != 0 && m_key.networked) {
// addresses::NetworkStateChanged((uintptr_t)(instancePointer) + m_chain, m_key.offset,
// 0xFFFFFFFF);
// } else if (m_key.networked) { /* WIP: Works fine for most props, but inlined classes in
// the
// middle of a class will need to have their this pointer
// corrected by the offset .*/
// CALL_VIRTUAL(void, 1, instancePointer, m_key.offset, 0xFFFFFFFF, 0xFFFF);
// }
switch (dataType) {
case DATA_TYPE_BOOL:
@@ -239,6 +238,7 @@ void SetSchemaValueByName(ScriptContext& script_context)
REGISTER_NATIVES(schema, {
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_OFFSET", GetSchemaOffset);
ScriptEngine::RegisterNativeHandler("IS_SCHEMA_FIELD_NETWORKED", IsSchemaFieldNetworked);
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_VALUE_BY_NAME", GetSchemaValueByName);
ScriptEngine::RegisterNativeHandler("SET_SCHEMA_VALUE_BY_NAME", SetSchemaValueByName);
ScriptEngine::RegisterNativeHandler("GET_SCHEMA_CLASS_SIZE", GetSchemaClassSize);

View File

@@ -1,4 +1,5 @@
GET_SCHEMA_OFFSET: className:string, propName:string -> short
IS_SCHEMA_FIELD_NETWORKED: className:string, propName:string -> bool
GET_SCHEMA_VALUE_BY_NAME: instance:pointer, returnType:int, className:string, propName:string -> any
SET_SCHEMA_VALUE_BY_NAME: instance:pointer, returnType:int, className:string, propName:string, value:any -> void
GET_SCHEMA_CLASS_SIZE: className:string -> int