Compare commits

...

22 Commits

Author SHA1 Message Date
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
Charles_
563a5d7b3a feat: Added RemovePlayerItem() to CBasePlayerPawn. (#200)
Co-authored-by: roflmuffin <shortguy014@gmail.com>
2023-12-16 14:24:08 +10:00
Charlie Thomson
983b673b4c feat: Add OnClientVoice listener (#204)
Co-authored-by: roflmuffin <shortguy014@gmail.com>
2023-12-16 14:18:40 +10:00
Roflmuffin
74fd0e0832 fix: offsets for CBaseEntity derived classes
Also adds test commands to test plugin for future validation
2023-12-14 15:14:34 +10:00
Michael Wilson
44e3f2240c chore: license updates (#199) 2023-12-14 11:03:18 +10:00
BuSheeZy
8af219e7a8 CHANGE: visual adjustments (#198) 2023-12-14 10:44:48 +10:00
38 changed files with 9706 additions and 4886 deletions

View File

@@ -22,6 +22,22 @@ saul/demofile-net, https://github.com/saul/demofile-net/blob/main/LICENSE:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
neverlosecc/source2gen, https://github.com/neverlosecc/source2gen
source2gen - Source2 games SDK generator
Copyright 2023 neverlosecc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Source2ZE/CS2Fixes:
alliedmodders/sourcemod:
Source-Python-Dev-Team/Source.Python:

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

@@ -28,8 +28,8 @@
},
"CCSPlayerController_Respawn": {
"offsets": {
"windows": 241,
"linux": 243
"windows": 242,
"linux": 244
}
},
"CCSPlayerPawn_Respawn": {
@@ -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",
@@ -122,14 +129,21 @@
},
"CBasePlayerPawn_CommitSuicide": {
"offsets": {
"windows": 356,
"linux": 356
"windows": 357,
"linux": 357
}
},
"CBasePlayerPawn_RemovePlayerItem": {
"signatures": {
"library": "server",
"linux": "\\x55\\x48\\x89\\x2A\\x41\\x2A\\x49\\x89\\x2A\\x41\\x2A\\x49\\x89\\x2A\\xE8\\x2A\\x2A\\x2A\\x2A\\x49\\x39",
"windows": "\\x48\\x85\\xD2\\x0F\\x84\\x2A\\x2A\\x2A\\x2A\\x48\\x89\\x5C\\x24\\x08\\x57\\x48\\x83\\xEC\\x30\\x48\\x8B\\xDA"
}
},
"CBaseEntity_Teleport": {
"offsets": {
"windows": 148,
"linux": 147
"windows": 149,
"linux": 148
}
},
"CBaseEntity_TakeDamageOld": {
@@ -153,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,7 +14,7 @@ description: Write Counter-Strike 2 server plugins in C#.
<span>CounterStrikeSharp is a simpler way to write CS2 server plugins.</span>
<div>
<a href="docs/guides/getting-started.md" class="btn btn-primary btn-lg fw-bold my-5">Get Started <i class="bi bi-arrow-right"></a>
<a href="https://github.com/roflmuffin/CounterStrikeSharp/releases/latest" class="btn btn-primary btn-lg fw-bold my-5">Download <i class="bi bi-arrow-right"></a>
<a href="https://github.com/roflmuffin/CounterStrikeSharp/releases/latest" class="btn btn-secondary btn-lg fw-bold my-5">Download <i class="bi bi-download"></a>
</div>
</div>

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

@@ -104,7 +104,7 @@ namespace CounterStrikeSharp.API.Core
info.ReplyToCommand(
" CounterStrikeSharp was created and is maintained by Michael \"roflmuffin\" Wilson.\n" +
" Counter-Strike Sharp uses code borrowed from SourceMod, Source.Python, FiveM, Saul Rennison and CS2Fixes.\n" +
" Counter-Strike Sharp uses code borrowed from SourceMod, Source.Python, FiveM, Saul Rennison, source2gen and CS2Fixes.\n" +
" See ACKNOWLEDGEMENTS.md for more information.\n" +
" Current API Version: " + currentVersion, true);
return;

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

@@ -93,6 +93,13 @@ namespace CounterStrikeSharp.API.Core
[ListenerName("OnClientDisconnectPost")]
public delegate void OnClientDisconnectPost(int playerSlot);
/// <summary>
/// Called when a client transmits voice data
/// </summary>
/// <param name="playerSlot">The player slot of the client.</param>
[ListenerName("OnClientVoice")]
public delegate void OnClientVoice(int playerSlot);
/// <summary>
/// Called when a client has been authorized by Steam.
/// </summary>

View File

@@ -14,4 +14,13 @@ public partial class CBasePlayerPawn
{
VirtualFunction.CreateVoid<IntPtr, bool, bool>(Handle, GameData.GetOffset("CBasePlayerPawn_CommitSuicide"))(Handle, explode, force);
}
/// <summary>
/// Remove Player Item
/// </summary>
/// <param name="weapon"></param>
public void RemovePlayerItem(CBasePlayerWeapon weapon)
{
VirtualFunctions.RemovePlayerItemVirtual(Handle, weapon.Handle);
}
}

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

@@ -76,4 +76,19 @@ public static class VirtualFunctions
public static MemoryFunctionVoid<CBaseTrigger, CBaseEntity> CBaseTrigger_EndTouchFunc = new (GameData.GetSignature("CBaseTrigger_EndTouch"));
public static Action<CBaseTrigger, CBaseEntity> CBaseTrigger_EndTouch = CBaseTrigger_EndTouchFunc.Invoke;
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

@@ -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
{
@@ -75,12 +77,47 @@ namespace CounterStrikeSharp.API
return new Target(pattern).GetTarget(player);
}
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;
foreach(var weapon in player.PlayerPawn.Value.WeaponServices.MyWeapons)
{
if (weapon is not { IsValid: true, Value.IsValid: true })
continue;
if (weapon.Value.DesignerName != designerName)
continue;
item = weapon;
}
if (item != null && item.Value != null)
{
player.PlayerPawn.Value.RemovePlayerItem(item.Value);
if (shouldRemoveEntity)
{
item.Value.Remove();
}
return true;
}
return false;
}
public static IEnumerable<T> FindAllEntitiesByDesignerName<T>(string designerName) where T : CEntityInstance
{
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;
}
}
@@ -101,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;
@@ -161,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;
});
@@ -465,6 +465,67 @@ namespace TestPlugin
player.PlayerPawn.Value.CommitSuicide(true, true);
}
[CommandHelper(minArgs: 1, usage: "[weaponName]")]
[ConsoleCommand("css_strip", "Removes weapon by name")]
public void OnStripActiveWeapon(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
if (!player.PlayerPawn.IsValid) return;
player.RemoveItemByDesignerName(command.GetArg(1));
}
[ConsoleCommand("css_stripweapons", "Removes player weapons")]
public void OnStripWeapons(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
if (!player.PlayerPawn.IsValid) return;
player.RemoveWeapons();
}
[ConsoleCommand("css_teleportup", "Teleports the player up")]
public void OnTeleport(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
if (!player.PlayerPawn.IsValid) return;
player.PlayerPawn.Value.Teleport(player.PlayerPawn.Value.AbsOrigin.With(z: player.PlayerPawn.Value.AbsOrigin.Z + 100), player.PlayerPawn.Value.AbsRotation, new Vector(IntPtr.Zero));
}
[ConsoleCommand("css_respawn", "Respawns the player")]
public void OnRespawn(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
if (!player.PlayerPawn.IsValid) return;
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

@@ -1,3 +1,12 @@
// Copyright (C) 2023 neverlosecc
// See end of file for extended copyright information.
/**
* =============================================================================
* Source2Gen
* Copyright (C) 2023 neverlose (https://github.com/neverlosecc/source2gen)
* =============================================================================
**/
#pragma once
#include <vector>
@@ -392,3 +401,18 @@ class CSchemaSystem
return CALL_VIRTUAL(CSchemaSystemTypeScope*, 13, this, m_module_name, nullptr);
}
};
// source2gen - Source2 games SDK generator
// Copyright 2023 neverlosecc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

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

@@ -46,8 +46,10 @@
SH_DECL_HOOK4_void(IServerGameClients, ClientActive, SH_NOATTRIB, 0, CPlayerSlot, bool, const char*,
uint64);
SH_DECL_HOOK5_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, CPlayerSlot, ENetworkDisconnectionReason,
const char*, uint64, const char*);
SH_DECL_HOOK5_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, CPlayerSlot,
ENetworkDisconnectionReason, const char*, uint64, const char*);
SH_DECL_HOOK1_void(IServerGameClients, ClientVoice, SH_NOATTRIB, 0, CPlayerSlot);
SH_DECL_HOOK4_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, CPlayerSlot, char const*,
int, uint64);
SH_DECL_HOOK1_void(IServerGameClients, ClientSettingsChanged, SH_NOATTRIB, 0, CPlayerSlot);
@@ -76,6 +78,8 @@ void PlayerManager::OnAllInitialized()
SH_MEMBER(this, &PlayerManager::OnClientDisconnect_Post), true);
SH_ADD_HOOK(IServerGameClients, ClientCommand, globals::serverGameClients,
SH_MEMBER(this, &PlayerManager::OnClientCommand), false);
SH_ADD_HOOK(IServerGameClients, ClientVoice, globals::serverGameClients,
SH_MEMBER(this, &PlayerManager::OnClientVoice), true);
m_on_client_connect_callback = globals::callbackManager.CreateCallback("OnClientConnect");
m_on_client_connected_callback = globals::callbackManager.CreateCallback("OnClientConnected");
@@ -84,6 +88,7 @@ void PlayerManager::OnAllInitialized()
m_on_client_disconnect_callback = globals::callbackManager.CreateCallback("OnClientDisconnect");
m_on_client_disconnect_post_callback =
globals::callbackManager.CreateCallback("OnClientDisconnectPost");
m_on_client_voice_callback = globals::callbackManager.CreateCallback("OnClientVoice");
m_on_client_authorized_callback = globals::callbackManager.CreateCallback("OnClientAuthorized");
}
@@ -101,6 +106,8 @@ void PlayerManager::OnShutdown()
SH_MEMBER(this, &PlayerManager::OnClientDisconnect_Post), true);
SH_REMOVE_HOOK(IServerGameClients, ClientCommand, globals::serverGameClients,
SH_MEMBER(this, &PlayerManager::OnClientCommand), false);
SH_REMOVE_HOOK(IServerGameClients, ClientVoice, globals::serverGameClients,
SH_MEMBER(this, &PlayerManager::OnClientVoice), true);
globals::callbackManager.ReleaseCallback(m_on_client_connect_callback);
globals::callbackManager.ReleaseCallback(m_on_client_connected_callback);
@@ -108,6 +115,7 @@ void PlayerManager::OnShutdown()
globals::callbackManager.ReleaseCallback(m_on_client_disconnect_callback);
globals::callbackManager.ReleaseCallback(m_on_client_disconnect_post_callback);
globals::callbackManager.ReleaseCallback(m_on_client_authorized_callback);
globals::callbackManager.ReleaseCallback(m_on_client_voice_callback);
}
bool PlayerManager::OnClientConnect(CPlayerSlot slot, const char* pszName, uint64 xuid,
@@ -121,8 +129,10 @@ bool PlayerManager::OnClientConnect(CPlayerSlot slot, const char* pszName, uint6
CPlayer* pPlayer = &m_players[client];
if (pPlayer->IsConnected()) {
OnClientDisconnect(slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, pszName, xuid, pszNetworkID);
OnClientDisconnect_Post(slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, pszName, xuid, pszNetworkID);
OnClientDisconnect(slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, pszName,
xuid, pszNetworkID);
OnClientDisconnect_Post(slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID,
pszName, xuid, pszNetworkID);
}
pPlayer->Initialize(pszName, pszNetworkID, slot);
@@ -223,8 +233,7 @@ void PlayerManager::OnClientPutInServer(CPlayerSlot slot, char const* pszName, i
m_on_client_put_in_server_callback->Execute();
}
void PlayerManager::OnClientDisconnect(CPlayerSlot slot,
ENetworkDisconnectionReason reason,
void PlayerManager::OnClientDisconnect(CPlayerSlot slot, ENetworkDisconnectionReason reason,
const char* pszName, uint64 xuid, const char* pszNetworkID)
{
CSSHARP_CORE_TRACE("[PlayerManager][OnClientDisconnect] - {}, {}, {}", slot.Get(), pszName,
@@ -247,8 +256,7 @@ void PlayerManager::OnClientDisconnect(CPlayerSlot slot,
// globals::entityListener.HandleEntityDeleted(pPlayer->GetBaseEntity(), client);
}
void PlayerManager::OnClientDisconnect_Post(CPlayerSlot slot,
ENetworkDisconnectionReason reason,
void PlayerManager::OnClientDisconnect_Post(CPlayerSlot slot, ENetworkDisconnectionReason reason,
const char* pszName, uint64 xuid,
const char* pszNetworkID) const
{
@@ -270,16 +278,27 @@ void PlayerManager::OnClientDisconnect_Post(CPlayerSlot slot,
m_on_client_disconnect_post_callback->Execute();
}
void PlayerManager::OnClientVoice(CPlayerSlot slot) const
{
CSSHARP_CORE_TRACE("[PlayerManager][OnClientVoice] - {}", slot.Get());
m_on_client_voice_callback->ScriptContext().Reset();
m_on_client_voice_callback->ScriptContext().Push(slot.Get());
m_on_client_voice_callback->Execute();
}
void PlayerManager::OnLevelEnd()
{
CSSHARP_CORE_TRACE("[PlayerManager][OnLevelEnd]");
for (int i = 0; i <= MaxClients(); i++) {
if (m_players[i].IsConnected()) {
OnClientDisconnect(m_players[i].m_slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, m_players[i].GetName(), 0,
m_players[i].GetIpAddress());
OnClientDisconnect_Post(m_players[i].m_slot, ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID, m_players[i].GetName(), 0,
m_players[i].GetIpAddress());
OnClientDisconnect(m_players[i].m_slot,
ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID,
m_players[i].GetName(), 0, m_players[i].GetIpAddress());
OnClientDisconnect_Post(m_players[i].m_slot,
ENetworkDisconnectionReason::NETWORK_DISCONNECT_INVALID,
m_players[i].GetName(), 0, m_players[i].GetIpAddress());
}
}
m_player_count = 0;
@@ -287,7 +306,8 @@ void PlayerManager::OnLevelEnd()
void PlayerManager::OnClientCommand(CPlayerSlot slot, const CCommand& args) const
{
CSSHARP_CORE_TRACE("[PlayerManager][OnClientCommand] - {}, {}, {}", slot.Get(), args.Arg(0), (void*)&args);
CSSHARP_CORE_TRACE("[PlayerManager][OnClientCommand] - {}, {}, {}", slot.Get(), args.Arg(0),
(void*)&args);
const char* cmd = args.Arg(0);
@@ -539,17 +559,11 @@ void CPlayer::SetListen(CPlayerSlot slot, ListenOverride listen)
m_listenMap[slot.Get()] = listen;
}
void CPlayer::SetVoiceFlags(VoiceFlag_t flags)
{
m_voiceFlag = flags;
}
void CPlayer::SetVoiceFlags(VoiceFlag_t flags) { m_voiceFlag = flags; }
VoiceFlag_t CPlayer::GetVoiceFlags() { return m_voiceFlag; }
ListenOverride CPlayer::GetListen(CPlayerSlot slot) const
{
return m_listenMap[slot.Get()];
}
ListenOverride CPlayer::GetListen(CPlayerSlot slot) const { return m_listenMap[slot.Get()]; }
void CPlayer::Connect()
{

View File

@@ -64,30 +64,31 @@ enum VoiceFlagValue
typedef uint8_t VoiceFlag_t;
class CPlayer {
class CPlayer
{
friend class PlayerManager;
public:
public:
CPlayer();
public:
void Initialize(const char *name, const char *ip, CPlayerSlot slot);
public:
void Initialize(const char* name, const char* ip, CPlayerSlot slot);
void Connect();
void Disconnect();
IPlayerInfo *GetPlayerInfo() const;
IPlayerInfo* GetPlayerInfo() const;
bool WasCountedAsInGame() const;
int GetUserId();
bool IsAuthStringValidated() const;
void Authorize();
public:
const char *GetName() const;
const CSteamID *GetSteamId();
void SetSteamId(const CSteamID *steam_id);
public:
const char* GetName() const;
const CSteamID* GetSteamId();
void SetSteamId(const CSteamID* steam_id);
bool IsConnected() const;
bool IsFakeClient() const;
bool IsAuthorized() const;
void PrintToConsole(const char *message) const;
void PrintToConsole(const char* message) const;
// void PrintToChat(const char *message);
// void PrintToHint(const char *message);
// void PrintToCenter(const char *message);
@@ -95,19 +96,19 @@ public:
Vector GetAbsOrigin() const;
bool IsAlive() const;
bool IsInGame() const;
void Kick(const char *kickReason);
const char *GetWeaponName() const;
void Kick(const char* kickReason);
const char* GetWeaponName() const;
void ChangeTeam(int team) const;
int GetTeam() const;
int GetArmor() const;
int GetFrags() const;
int GetDeaths() const;
const char *GetKeyValue(const char *key) const;
const char* GetKeyValue(const char* key) const;
Vector GetMaxSize() const;
Vector GetMinSize() const;
int GetMaxHealth() const;
const char *GetIpAddress() const;
const char *GetModelName() const;
const char* GetIpAddress() const;
const char* GetModelName() const;
int GetUserId() const;
float GetTimeConnected() const;
float GetLatency() const;
@@ -116,9 +117,9 @@ public:
VoiceFlag_t GetVoiceFlags();
ListenOverride GetListen(CPlayerSlot slot) const;
public:
public:
std::string m_name;
IPlayerInfo *m_info = nullptr;
IPlayerInfo* m_info = nullptr;
std::string m_auth_id;
bool m_is_connected = false;
bool m_is_fake_client = false;
@@ -131,72 +132,61 @@ public:
ListenOverride m_listenMap[66] = {};
VoiceFlag_t m_voiceFlag = 0;
CPlayerBitVec m_selfMutes[64] = {};
void SetName(const char *name);
INetChannelInfo *GetNetInfo() const;
void SetName(const char* name);
INetChannelInfo* GetNetInfo() const;
};
class PlayerManager : public GlobalClass {
class PlayerManager : public GlobalClass
{
friend class CPlayer;
public:
public:
PlayerManager();
void OnStartup() override;
void OnAllInitialized() override;
bool OnClientConnect(CPlayerSlot slot,
const char *pszName,
uint64 xuid,
const char *pszNetworkID,
bool unk1,
CBufferString *pRejectReason);
bool OnClientConnect_Post(CPlayerSlot slot,
const char *pszName,
uint64 xuid,
const char *pszNetworkID,
bool unk1,
CBufferString *pRejectReason);
void OnClientPutInServer(CPlayerSlot slot, char const *pszName, int type, uint64 xuid);
void OnClientDisconnect(CPlayerSlot slot,
ENetworkDisconnectionReason reason,
const char *pszName,
uint64 xuid,
const char *pszNetworkID);
void OnClientDisconnect_Post(CPlayerSlot slot,
ENetworkDisconnectionReason reason,
const char *pszName,
uint64 xuid,
const char *pszNetworkID) const;
bool OnClientConnect(CPlayerSlot slot, const char* pszName, uint64 xuid,
const char* pszNetworkID, bool unk1, CBufferString* pRejectReason);
bool OnClientConnect_Post(CPlayerSlot slot, const char* pszName, uint64 xuid,
const char* pszNetworkID, bool unk1, CBufferString* pRejectReason);
void OnClientPutInServer(CPlayerSlot slot, char const* pszName, int type, uint64 xuid);
void OnClientDisconnect(CPlayerSlot slot, ENetworkDisconnectionReason reason,
const char* pszName, uint64 xuid, const char* pszNetworkID);
void OnClientDisconnect_Post(CPlayerSlot slot, ENetworkDisconnectionReason reason,
const char* pszName, uint64 xuid, const char* pszNetworkID) const;
void OnClientVoice(CPlayerSlot slot) const;
void OnAuthorized(CPlayer* player) const;
void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax) const;
void OnServerActivate(edict_t* pEdictList, int edictCount, int clientMax) const;
void OnThink(bool last_tick) const;
void OnShutdown() override;
void OnLevelEnd() override;
void OnClientCommand(CPlayerSlot slot, const CCommand &args) const;
void OnClientCommand(CPlayerSlot slot, const CCommand& args) const;
int ListenClient() const;
void RunAuthChecks();
public:
public:
int NumPlayers() const;
int MaxClients() const;
CPlayer *GetPlayerBySlot(int client) const;
CPlayer *GetClientOfUserId(int user_id) const;
CPlayer* GetPlayerBySlot(int client) const;
CPlayer* GetClientOfUserId(int user_id) const;
private:
void InvalidatePlayer(CPlayer *pPlayer) const;
private:
void InvalidatePlayer(CPlayer* pPlayer) const;
CPlayer *m_players;
CPlayer* m_players;
int m_max_clients = 0;
int m_player_count = 0;
int *m_user_id_lookup;
int* m_user_id_lookup;
int m_listen_client;
bool m_is_listen_server;
float m_last_auth_check_time = 0;
ScriptCallback *m_on_client_connect_callback;
ScriptCallback *m_on_client_put_in_server_callback;
ScriptCallback *m_on_client_connected_callback;
ScriptCallback *m_on_client_disconnect_callback;
ScriptCallback *m_on_client_disconnect_post_callback;
ScriptCallback *m_on_client_authorized_callback;
ScriptCallback* m_on_client_connect_callback;
ScriptCallback* m_on_client_put_in_server_callback;
ScriptCallback* m_on_client_connected_callback;
ScriptCallback* m_on_client_disconnect_callback;
ScriptCallback* m_on_client_disconnect_post_callback;
ScriptCallback* m_on_client_voice_callback;
ScriptCallback* m_on_client_authorized_callback;
};
} // namespace counterstrikesharp
} // 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