Compare commits

...

10 Commits
v1.0.90 ... v99

Author SHA1 Message Date
Roflmuffin
0ab3cf429a feat: add NextWorldUpdate helper to run on next pre world update
also runs hot reload in next world update
2023-12-04 17:43:52 +10:00
chte
1f9630b92d Enriched SteamID (#163)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2023-12-03 19:50:53 +10:00
Roflmuffin
02bf2483d3 docs: add database (dapper) example plugin 2023-12-03 19:23:23 +10:00
Roflmuffin
cb181b6a49 docs: add database (dapper) example plugin 2023-12-03 19:23:02 +10:00
Nexd
cc21dca5a0 Exposing from ISource2Server and IVEngineServer2 (#159)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
Co-authored-by: Roflmuffin <shortguy014@gmail.com>
2023-12-03 15:17:04 +10:00
Roflmuffin
5721d060ea feat: add overload for PrintToCenterHtml that accepts duration
closes #127
2023-12-03 14:18:10 +10:00
Roflmuffin
220521d571 fix: free callback property on game event unhook 2023-12-03 14:02:42 +10:00
Roflmuffin
5698b511e9 fix: use authorized Steam ID for admin system
also fix reference equality for SteamID
2023-12-03 13:34:11 +10:00
Roflmuffin
48c9d195ff feat: add IpAddress to CCSPlayerController 2023-12-03 13:11:06 +10:00
Roflmuffin
603827d331 fix: remove reference equality for CEntityInstance 2023-12-03 13:02:33 +10:00
34 changed files with 656 additions and 64 deletions

View File

@@ -0,0 +1,2 @@
# With Database (Dapper)
Simple SQLite database example using Dapper library to track kills.

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.24" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.14" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,89 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Commands;
using Dapper;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
namespace WithDatabaseDapper;
[MinimumApiVersion(80)]
public class WithDatabaseDapperPlugin : BasePlugin
{
public override string ModuleName => "Example: With Database (Dapper)";
public override string ModuleVersion => "1.0.0";
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
public override string ModuleDescription => "A plugin that reads and writes from the database.";
private SqliteConnection _connection = null!;
public override void Load(bool hotReload)
{
Logger.LogInformation("Loading database from {Path}", Path.Join(ModuleDirectory, "database.db"));
_connection = new SqliteConnection($"Data Source={Path.Join(ModuleDirectory, "database.db")}");
_connection.Open();
// Create the table if it doesn't exist
// Run in a separate thread to avoid blocking the main thread
Task.Run(async () =>
{
await _connection.ExecuteAsync(@"
CREATE TABLE IF NOT EXISTS `players` (
`steamid` UNSIGNED BIG INT NOT NULL,
`kills` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`steamid`));");
});
}
[GameEventHandler]
public HookResult OnPlayerKilled(EventPlayerDeath @event, GameEventInfo info)
{
// Don't count suicides.
if (@event.Attacker == @event.Userid) return HookResult.Continue;
// Capture the steamid of the player as `@event` will not be available outside of this function.
var steamId = @event.Attacker.AuthorizedSteamID?.SteamId64;
if (steamId == null) return HookResult.Continue;
// Run in a separate thread to avoid blocking the main thread
Task.Run(async () =>
{
// insert or update the player's kills
await _connection.ExecuteAsync(@"
INSERT INTO `players` (`steamid`, `kills`) VALUES (@SteamId, 1)
ON CONFLICT(`steamid`) DO UPDATE SET `kills` = `kills` + 1;",
new
{
SteamId = steamId
});
});
return HookResult.Continue;
}
[ConsoleCommand("css_kills", "Get count of kills for a player")]
public void OnKillsCommand(CCSPlayerController? player, CommandInfo commandInfo)
{
if (player == null) return;
// Capture the SteamID of the player as `@event` will not be available outside of this function.
var steamId = player.AuthorizedSteamID.SteamId64;
// Run in a separate thread to avoid blocking the main thread
Task.Run(async () =>
{
var result = await _connection.QueryFirstOrDefaultAsync(@"SELECT `kills` FROM `players` WHERE `steamid` = @SteamId;",
new
{
SteamId = steamId
});
// Print the result to the player's chat. Note that this needs to be run on the game thread.
// So we use `Server.NextFrame` to run it on the next game tick.
Server.NextFrame(() => { player.PrintToChat($"Kills: {result?.kills ?? 0}"); });
});
}
}

View File

@@ -157,6 +157,30 @@ namespace CounterStrikeSharp.API.Core
}
}
public static string GetClientConvarValue(int clientindex, string convarname){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(clientindex);
ScriptContext.GlobalScriptContext.Push(convarname);
ScriptContext.GlobalScriptContext.SetIdentifier(0xAE4B1B79);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static void SetFakeClientConvarValue(int clientindex, string convarname, string convarvalue){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(clientindex);
ScriptContext.GlobalScriptContext.Push(convarname);
ScriptContext.GlobalScriptContext.Push(convarvalue);
ScriptContext.GlobalScriptContext.SetIdentifier(0x4C61E8BB);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static T DynamicHookGetReturn<T>(IntPtr hook, int datatype){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -455,6 +479,16 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void QueueTaskForNextWorldUpdate(IntPtr callback){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(callback);
ScriptContext.GlobalScriptContext.SetIdentifier(0xAD51A0C9);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static IntPtr GetValveInterface(int interfacetype, string interfacename){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -609,6 +643,17 @@ namespace CounterStrikeSharp.API.Core
}
}
public static string GetPlayerIpAddress(int slot){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(slot);
ScriptContext.GlobalScriptContext.SetIdentifier(0x46A45CB0);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static void HookEvent(string name, InputArgument callback, bool ispost){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
@@ -1042,6 +1087,16 @@ namespace CounterStrikeSharp.API.Core
}
}
public static bool IsServerPaused(){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.SetIdentifier(0xB216AAAC);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool));
}
}
public static IntPtr CreateTimer(float interval, InputArgument callback, int flags){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();

View File

@@ -126,5 +126,26 @@ namespace CounterStrikeSharp.API.Core
/// <param name="hostname">New hostname of the server</param>
[ListenerName("OnHostNameChanged")]
public delegate void OnHostNameChanged(string hostname);
/// <summary>
/// Called before the server enters fatal shutdown.
/// </summary>
[ListenerName("OnPreFatalShutdown")]
public delegate void OnServerPreFatalShutdown();
/// <summary>
/// Called when the server is in a loading stage.
/// </summary>
/// <param name="frameTime"></param>
[ListenerName("OnUpdateWhenNotInGame")]
public delegate void OnUpdateWhenNotInGame(float frameTime);
/// <summary>
/// Called before the world updates.
/// This seems to be called even when the server is hibernating.
/// </summary>
/// <param name="simulating"><see langword="true"/> if simulating, <see langword="false"/> otherwise</param>
[ListenerName("OnServerPreWorldUpdate")]
public delegate void OnServerPreWorldUpdate(bool simulating);
}
}

View File

@@ -1,4 +1,6 @@
using System;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Memory;
@@ -50,13 +52,15 @@ public partial class CCSPlayerController
{
VirtualFunctions.ClientPrint(this.Handle, HudDestination.Center, message, 0, 0, 0, 0);
}
public void PrintToCenterHtml(string message) => PrintToCenterHtml(message, 5);
public void PrintToCenterHtml(string message)
public void PrintToCenterHtml(string message, int duration)
{
var @event = new EventShowSurvivalRespawnStatus(true)
{
LocToken = message,
Duration = 5,
Duration = duration,
Userid = this
};
@event.FireEventToClient(this);
@@ -140,6 +144,57 @@ public partial class CCSPlayerController
team);
}
/// <summary>
/// Get a ConVar value for given player
/// </summary>
/// <param name="conVar">Name of the convar to retrieve</param>
/// <returns>ConVar string value</returns>
public string GetConVarValue(string conVar)
{
return NativeAPI.GetClientConvarValue(this.Slot, conVar);
}
public string GetConVarValue(ConVar? conVar)
{
if (conVar == null)
{
throw new Exception("Invalid convar passed to 'GetConVarValue'");
}
return GetConVarValue(conVar.Name);
}
/// <summary>
/// Sets a ConVar value on a fake client (bot).
/// </summary>
/// <param name="conVar">Console variable name</param>
/// <param name="value">String value to set</param>
/// <exception cref="InvalidOperationException">Player is not a bot</exception>
public void SetFakeClientConVar(string conVar, string value)
{
if (!IsBot)
{
throw new InvalidOperationException("'SetFakeClientConVar' can only be called for fake clients (bots)");
}
NativeAPI.SetFakeClientConvarValue(this.Slot, conVar, value);
}
/// <summary>
/// <inheritdoc cref="SetFakeClientConVar(string,string)"/>
/// </summary>
/// <exception cref="ArgumentException"><paramref name="conVar"/> is <see langword="null"/></exception>
/// <inheritdoc cref="SetFakeClientConVar(string,string)" select="exception"/>
public void SetFakeClientConVar(ConVar conVar, string value)
{
if (conVar == null)
{
throw new ArgumentException("Invalid convar passed to 'SetFakeClientConVar'");
}
SetFakeClientConVar(conVar.Name, value);
}
/// <summary>
/// Gets the active pawns button state. Will work even if the player is dead or observing.
/// </summary>
@@ -163,4 +218,20 @@ public partial class CCSPlayerController
return (SteamID)authorizedSteamId;
}
}
/// <summary>
/// Returns the IP address (and possibly port) of this player.
/// <remarks>Returns 127.0.0.1 if the player is a bot.</remarks>
/// </summary>
public string? IpAddress
{
get
{
if (!this.IsValid) return null;
var ipAddress = NativeAPI.GetPlayerIpAddress(this.Slot);
if (string.IsNullOrWhiteSpace(ipAddress)) return null;
return ipAddress;
}
}
}

View File

@@ -36,12 +36,12 @@ public partial class CEntityInstance : IEquatable<CEntityInstance>
public bool Equals(CEntityInstance? other)
{
return this.EntityHandle == other?.EntityHandle;
return this.EntityHandle.Equals(other?.EntityHandle);
}
public override bool Equals(object? obj)
{
return ReferenceEquals(this, obj) || obj is CEntityInstance other && Equals(other);
return obj is CEntityInstance other && Equals(other);
}
public override int GetHashCode()

View File

@@ -88,11 +88,14 @@ namespace CounterStrikeSharp.API.Core.Plugin
private Task OnReloadedAsync(object sender, PluginReloadedEventArgs eventargs)
{
_logger.LogInformation("Reloading plugin {Name}", Plugin.ModuleName);
Loader = eventargs.Loader;
Unload(hotReload: true);
Load(hotReload: true);
Server.NextWorldUpdate(() =>
{
_logger.LogInformation("Reloading plugin {Name}", Plugin.ModuleName);
Loader = eventargs.Loader;
Unload(hotReload: true);
Load(hotReload: true);
});
return Task.CompletedTask;
}

View File

@@ -94,7 +94,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
// The server console should have access to all commands, regardless of groups.
if (player == null) return true;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
return playerData?.Groups.IsSupersetOf(groups) ?? false;
}
@@ -119,7 +119,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
{
if (player == null) return;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
AddPlayerToGroup((SteamID)player.SteamID, groups);
AddPlayerToGroup((SteamID)player.AuthorizedSteamID, groups);
}
/// <summary>
@@ -161,7 +161,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
{
if (player == null) return;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
RemovePlayerFromGroup((SteamID)player.SteamID, true, groups);
RemovePlayerFromGroup((SteamID)player.AuthorizedSteamID, true, groups);
}
/// <summary>

View File

@@ -102,7 +102,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
// The server console should have access to all commands, regardless of permissions.
if (player == null) return true;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
var playerData = GetPlayerAdminData(player.AuthorizedSteamID);
return playerData?.Flags.IsSupersetOf(flags) ?? false;
}
@@ -136,7 +136,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
// The server console should have access to all commands, regardless of permissions.
if (player == null) return true;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
return playerData?.CommandOverrides.ContainsKey(command) ?? false;
}
@@ -165,7 +165,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
// The server console should have access to all commands, regardless of permissions.
if (player == null) return true;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return false; }
var playerData = GetPlayerAdminData((SteamID)player.SteamID);
var playerData = GetPlayerAdminData((SteamID)player.AuthorizedSteamID);
return playerData?.CommandOverrides.GetValueOrDefault(command) ?? false;
}
@@ -193,7 +193,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
// The server console should have access to all commands, regardless of permissions.
if (player == null) return;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) { return; }
SetPlayerCommandOverride((SteamID)player.SteamID, command, state);
SetPlayerCommandOverride((SteamID)player.AuthorizedSteamID, command, state);
}
/// <summary>
@@ -235,7 +235,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
{
if (player == null) return;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
AddPlayerPermissions((SteamID)player.SteamID, flags);
AddPlayerPermissions((SteamID)player.AuthorizedSteamID, flags);
}
/// <summary>
@@ -278,7 +278,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
if (player == null) return;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
RemovePlayerPermissions((SteamID)player.SteamID, flags);
RemovePlayerPermissions((SteamID)player.AuthorizedSteamID, flags);
}
/// <summary>
@@ -306,7 +306,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
if (player == null) return;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
ClearPlayerPermissions((SteamID)player.SteamID);
ClearPlayerPermissions((SteamID)player.AuthorizedSteamID);
}
/// <summary>
@@ -335,7 +335,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
if (player == null) return;
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected || player.IsBot) return;
SetPlayerImmunity((SteamID)player.SteamID, value);
SetPlayerImmunity((SteamID)player.AuthorizedSteamID, value);
}
/// <summary>
@@ -366,10 +366,10 @@ namespace CounterStrikeSharp.API.Modules.Admin
if (target == null) return false;
if (!target.IsValid || target.Connected != PlayerConnectedState.PlayerConnected) return false;
var callerData = GetPlayerAdminData((SteamID)caller.SteamID);
var callerData = GetPlayerAdminData((SteamID)caller.AuthorizedSteamID);
if (callerData == null) return false;
var targetData = GetPlayerAdminData((SteamID)target.SteamID);
var targetData = GetPlayerAdminData((SteamID)target.AuthorizedSteamID);
if (targetData == null) return true;
return callerData.Immunity >= targetData.Immunity;

View File

@@ -31,7 +31,8 @@ namespace CounterStrikeSharp.API.Modules.Admin
{
// If we have a command in the "command_overrides" section in "configs/admins.json",
// we skip the checks below and just return the defined value.
var adminData = AdminManager.GetPlayerAdminData((SteamID)caller.SteamID);
if (caller?.AuthorizedSteamID == null) return false;
var adminData = AdminManager.GetPlayerAdminData(caller.AuthorizedSteamID);
if (adminData == null) return false;
if (adminData.CommandOverrides.ContainsKey(Command))
{

View File

@@ -23,7 +23,7 @@ namespace CounterStrikeSharp.API.Modules.Admin
var groupPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.GroupPermissionChar));
var userPermissions = Permissions.Where(perm => perm.StartsWith(PermissionCharacters.UserPermissionChar));
var adminData = AdminManager.GetPlayerAdminData((SteamID)caller.SteamID);
var adminData = AdminManager.GetPlayerAdminData(caller.AuthorizedSteamID);
if (adminData == null) return false;
return (groupPermissions.Intersect(adminData.Groups).Count() + userPermissions.Intersect(adminData.Flags).Count()) > 0;
}

View File

@@ -0,0 +1,32 @@
namespace CounterStrikeSharp.API.Modules.Entities.Constants;
/// <summary>
/// Represents the instance types for a Steam account.
/// </summary>
public enum SteamAccountInstance
{
/// <summary>
/// Invalid instance.
/// </summary>
Invalid = -1,
/// <summary>
/// All instances.
/// </summary>
All = 0,
/// <summary>
/// Desktop instance.
/// </summary>
Desktop = 1,
/// <summary>
/// Console instance.
/// </summary>
Console = 2,
/// <summary>
/// Web instance.
/// </summary>
Web = 4
}

View File

@@ -0,0 +1,70 @@
using System.Runtime.Serialization;
namespace CounterStrikeSharp.API.Modules.Entities.Constants;
/// <summary>
/// Represents the types of Steam accounts.
/// </summary>
public enum SteamAccountType
{
/// <summary>
/// Invalid account type.
/// </summary>
[EnumMember(Value = "I")]
Invalid = 0,
/// <summary>
/// Individual account type.
/// </summary>
[EnumMember(Value = "U")]
Individual,
/// <summary>
/// MultiSeat account type.
/// </summary>
[EnumMember(Value = "M")]
MultiSeat,
/// <summary>
/// Game Server account type.
/// </summary>
[EnumMember(Value = "G")]
GameServer,
/// <summary>
/// Anonymous Game Server account type.
/// </summary>
[EnumMember(Value = "A")]
AnonGameServer,
/// <summary>
/// Pending account type.
/// </summary>
[EnumMember(Value = "P")]
Pending,
/// <summary>
/// Content Server account type.
/// </summary>
[EnumMember(Value = "C")]
ContentServer,
/// <summary>
/// Clan account type.
/// </summary>
[EnumMember(Value = "g")]
Clan,
/// <summary>
/// Chat account type.
/// </summary>
[EnumMember(Value = "T")]
Chat,
/// <summary>
/// Console user account type.
/// </summary>
[EnumMember(Value = "c")]
ConsoleUser,
/// <summary>
/// P2P Super Seeder account type.
/// </summary>
[EnumMember(Value = " ")]
P2PSuperSeeder,
/// <summary>
/// Anonymous user account type.
/// </summary>
[EnumMember(Value = "a")]
AnonUser,
}

View File

@@ -0,0 +1,32 @@
namespace CounterStrikeSharp.API.Modules.Entities.Constants;
/// <summary>
/// Represents the universe of a Steam account.
/// </summary>
public enum SteamAccountUniverse
{
/// <summary>
/// Individual / unspecified universe.
/// </summary>
Unspecified = 0,
/// <summary>
/// Public universe.
/// </summary>
Public = 1,
/// <summary>
/// Beta universe.
/// </summary>
Beta = 2,
/// <summary>
/// Internal universe.
/// </summary>
Internal = 3,
/// <summary>
/// Development universe.
/// </summary>
Dev = 4,
}

View File

@@ -1,4 +1,5 @@
using System;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Modules.Entities
{
@@ -12,6 +13,7 @@ namespace CounterStrikeSharp.API.Modules.Entities
public static explicit operator SteamID(ulong u) => new(u);
public static explicit operator SteamID(string s) => new(s);
ulong ParseId(string id)
{
var parts = id.Split(':');
@@ -34,7 +36,7 @@ namespace CounterStrikeSharp.API.Modules.Entities
public string SteamId3
{
get => $"[U:1:{SteamId64 - Base}]";
get => $"[{EnumUtils.GetEnumMemberAttributeValue(AccountType)}:{(int)AccountUniverse}:{SteamId64 - Base}]";
set => SteamId64 = ParseId3(value);
}
@@ -44,20 +46,54 @@ namespace CounterStrikeSharp.API.Modules.Entities
set => SteamId64 = (ulong)value + Base;
}
public int AccountId => (int)((SteamId64 >> 0) & 0xFFFFFFFF);
public SteamAccountInstance AccountInstance =>
(SteamAccountInstance)((SteamId64 >> 32) & 0xFFFFF);
public SteamAccountType AccountType =>
(SteamAccountType)((SteamId64 >> 52) & 0xF);
public SteamAccountUniverse AccountUniverse =>
(SteamAccountUniverse)((SteamId64 >> 56) & 0xF);
public bool IsValid()
{
if (AccountUniverse == SteamAccountUniverse.Unspecified
|| AccountType == SteamAccountType.Invalid
|| AccountInstance == SteamAccountInstance.Invalid)
return false;
if (AccountType == SteamAccountType.Individual
&& (AccountId == 0 || AccountInstance != SteamAccountInstance.Desktop))
return false;
if (AccountType == SteamAccountType.Clan
&& (AccountId == 0 || AccountInstance != SteamAccountInstance.All))
return false;
if (AccountType == SteamAccountType.GameServer && AccountId == 0)
return false;
return true;
}
public override string ToString() => $"[SteamID {SteamId64}, {SteamId2}, {SteamId3}]";
public Uri ToCommunityUrl()
{
string url = string.Empty;
if (AccountType == SteamAccountType.Individual)
url = "https://steamcommunity.com/profiles/" + SteamId64;
if (AccountType == SteamAccountType.Clan)
url = "https://steamcommunity.com/gid/" + SteamId64;
return new Uri(url);
}
public bool Equals(SteamID? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return SteamId64 == other.SteamId64;
return other != null && SteamId64 == other.SteamId64;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
if (obj?.GetType() != this.GetType()) return false;
return Equals((SteamID)obj);
}

View File

@@ -94,7 +94,7 @@ public partial class VirtualFunctionVoid<TArg1, TArg2, TArg3>
{
this.Function = VirtualFunction.CreateVoid<TArg1, TArg2, TArg3>(signature);
}
public VirtualFunctionVoid(string signature, string binarypath)
{
this.Function = VirtualFunction.CreateVoid<TArg1, TArg2, TArg3>(signature, binarypath);

View File

@@ -99,7 +99,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TResult>
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TResult>(signature);
}
public VirtualFunctionWithReturn(string signature, string binarypath)
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TResult>(signature, binarypath);
@@ -124,7 +124,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TResu
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TResult>(signature);
}
public VirtualFunctionWithReturn(string signature, string binarypath)
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TResult>(signature, binarypath);
@@ -149,7 +149,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TResult>(signature);
}
public VirtualFunctionWithReturn(string signature, string binarypath)
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TResult>(signature, binarypath);
@@ -174,7 +174,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>(signature);
}
public VirtualFunctionWithReturn(string signature, string binarypath)
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>(signature, binarypath);
@@ -199,7 +199,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>(signature);
}
public VirtualFunctionWithReturn(string signature, string binarypath)
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>(signature, binarypath);
@@ -224,7 +224,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult>(signature);
}
public VirtualFunctionWithReturn(string signature, string binarypath)
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult>(signature, binarypath);
@@ -249,7 +249,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult>(signature);
}
public VirtualFunctionWithReturn(string signature, string binarypath)
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult>(signature, binarypath);
@@ -274,7 +274,7 @@ public partial class VirtualFunctionWithReturn<TArg1, TArg2, TArg3, TArg4, TArg5
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult>(signature);
}
public VirtualFunctionWithReturn(string signature, string binarypath)
{
this.Function = VirtualFunction.Create<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult>(signature, binarypath);

View File

@@ -62,17 +62,12 @@ public class CHandle<T> : IEquatable<CHandle<T>> where T : NativeEntity
public bool Equals(CHandle<T>? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Raw == other.Raw;
return other != null && Raw == other.Raw;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((CHandle<T>)obj);
return Equals(obj as CHandle<T>);
}
public override int GetHashCode()

View File

@@ -45,12 +45,28 @@ namespace CounterStrikeSharp.API
// Currently only used to keep the delegate from being garbage collected
private static List<Action> nextFrameTasks = new List<Action>();
/// <summary>
/// Queue a task to be executed on the next game frame.
/// <remarks>Does not execute if the server is hibernating.</remarks>
/// </summary>
public static void NextFrame(Action task)
{
nextFrameTasks.Add(task);
var ptr = Marshal.GetFunctionPointerForDelegate(task);
NativeAPI.QueueTaskForNextFrame(ptr);
}
/// <summary>
/// Queue a task to be executed on the next pre world update.
/// <remarks>Executes if the server is hibernating.</remarks>
/// </summary>
/// <param name="task"></param>
public static void NextWorldUpdate(Action task)
{
nextFrameTasks.Add(task);
var ptr = Marshal.GetFunctionPointerForDelegate(task);
NativeAPI.QueueTaskForNextWorldUpdate(ptr);
}
public static void PrintToChatAll(string message)
{

View File

@@ -67,7 +67,7 @@ namespace CounterStrikeSharp.API
public static CCSPlayerController? GetPlayerFromSteamId(ulong steamId)
{
return Utilities.GetPlayers().FirstOrDefault(player => player.SteamID == steamId);
return Utilities.GetPlayers().FirstOrDefault(player => player.AuthorizedSteamID == (SteamID)steamId);
}
public static TargetResult ProcessTargetString(string pattern, CCSPlayerController player)

View File

@@ -22,6 +22,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithCommands", "..\examples
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithGameEventHandlers", "..\examples\WithGameEventHandlers\WithGameEventHandlers.csproj", "{3032F3FA-E20A-4581-9A08-2FB5FF1524F4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithDatabaseDapper", "..\examples\WithDatabaseDapper\WithDatabaseDapper.csproj", "{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -68,6 +70,10 @@ Global
{3032F3FA-E20A-4581-9A08-2FB5FF1524F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3032F3FA-E20A-4581-9A08-2FB5FF1524F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3032F3FA-E20A-4581-9A08-2FB5FF1524F4}.Release|Any CPU.Build.0 = Release|Any CPU
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{57E64289-5D69-4AA1-BEF0-D0D96A55EE8F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
@@ -77,5 +83,6 @@ Global
{E497E40C-A7B4-41A7-A1C6-2EC6698FF3BF} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
{EA2F596E-2236-4999-B476-B1FDA287674A} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
{3032F3FA-E20A-4581-9A08-2FB5FF1524F4} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
{A641D8D7-35F1-48AB-AABA-EDFB6B7FC49B} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
EndGlobalSection
EndGlobal

View File

@@ -103,7 +103,6 @@ bool EventManager::HookEvent(const char* szName, CallbackT fnCallback, bool bPos
} else {
if (!pHook->m_pPreHook) {
pHook->m_pPreHook = globals::callbackManager.CreateCallback("");
;
}
pHook->m_pPreHook->AddListener(fnCallback);
@@ -130,18 +129,17 @@ bool EventManager::UnhookEvent(const char* szName, CallbackT fnCallback, bool bP
pCallback = pHook->m_pPreHook;
}
// Remove from function list
if (pCallback == nullptr) {
return false;
}
pCallback->RemoveListener(fnCallback);
if (bPost) {
pHook->m_pPostHook = nullptr;
} else {
pHook->m_pPreHook = nullptr;
}
if (pCallback->GetFunctionCount() == 0) {
globals::callbackManager.ReleaseCallback(pCallback);
// TODO: Clean up callback if theres noone left attached.
if (bPost) {
pHook->m_pPostHook = nullptr;
} else {
pHook->m_pPreHook = nullptr;
}
}
CSSHARP_CORE_TRACE("Unhooking event: {0} with callback pointer: {1}", szName, (void*)fnCallback);

View File

@@ -23,6 +23,9 @@ SH_DECL_HOOK1_void(ISource2Server, ServerHibernationUpdate, SH_NOATTRIB, 0, bool
SH_DECL_HOOK0_void(ISource2Server, GameServerSteamAPIActivated, SH_NOATTRIB, 0);
SH_DECL_HOOK0_void(ISource2Server, GameServerSteamAPIDeactivated, SH_NOATTRIB, 0);
SH_DECL_HOOK1_void(ISource2Server, OnHostNameChanged, SH_NOATTRIB, 0, const char*);
SH_DECL_HOOK0_void(ISource2Server, PreFatalShutdown, const, 0);
SH_DECL_HOOK1_void(ISource2Server, UpdateWhenNotInGame, SH_NOATTRIB, 0, float);
SH_DECL_HOOK1_void(ISource2Server, PreWorldUpdate, SH_NOATTRIB, 0, bool);
namespace counterstrikesharp {
@@ -39,11 +42,20 @@ void ServerManager::OnAllInitialized() {
SH_MEMBER(this, &ServerManager::GameServerSteamAPIDeactivated), true);
SH_ADD_HOOK(ISource2Server, OnHostNameChanged, globals::server,
SH_MEMBER(this, &ServerManager::OnHostNameChanged), true);
SH_ADD_HOOK(ISource2Server, PreFatalShutdown, globals::server,
SH_MEMBER(this, &ServerManager::PreFatalShutdown), true);
SH_ADD_HOOK(ISource2Server, UpdateWhenNotInGame, globals::server,
SH_MEMBER(this, &ServerManager::UpdateWhenNotInGame), true);
SH_ADD_HOOK(ISource2Server, PreWorldUpdate, globals::server,
SH_MEMBER(this, &ServerManager::PreWorldUpdate), true);
on_server_hibernation_update_callback = globals::callbackManager.CreateCallback("OnServerHibernationUpdate");
on_server_steam_api_activated_callback = globals::callbackManager.CreateCallback("OnGameServerSteamAPIActivated");
on_server_steam_api_deactivated_callback = globals::callbackManager.CreateCallback("OnGameServerSteamAPIDeactivated");
on_server_hostname_changed_callback = globals::callbackManager.CreateCallback("OnHostNameChanged");
on_server_pre_fatal_shutdown = globals::callbackManager.CreateCallback("OnPreFatalShutdown");
on_server_update_when_not_in_game = globals::callbackManager.CreateCallback("OnUpdateWhenNotInGame");
on_server_pre_world_update = globals::callbackManager.CreateCallback("OnServerPreWorldUpdate");
}
void ServerManager::OnShutdown() {
@@ -55,11 +67,20 @@ void ServerManager::OnShutdown() {
SH_MEMBER(this, &ServerManager::GameServerSteamAPIDeactivated), true);
SH_REMOVE_HOOK(ISource2Server, OnHostNameChanged, globals::server,
SH_MEMBER(this, &ServerManager::OnHostNameChanged), true);
SH_REMOVE_HOOK(ISource2Server, PreFatalShutdown, globals::server,
SH_MEMBER(this, &ServerManager::PreFatalShutdown), true);
SH_REMOVE_HOOK(ISource2Server, UpdateWhenNotInGame, globals::server,
SH_MEMBER(this, &ServerManager::UpdateWhenNotInGame), true);
SH_REMOVE_HOOK(ISource2Server, PreWorldUpdate, globals::server,
SH_MEMBER(this, &ServerManager::PreWorldUpdate), true);
globals::callbackManager.ReleaseCallback(on_server_hibernation_update_callback);
globals::callbackManager.ReleaseCallback(on_server_steam_api_activated_callback);
globals::callbackManager.ReleaseCallback(on_server_steam_api_deactivated_callback);
globals::callbackManager.ReleaseCallback(on_server_hostname_changed_callback);
globals::callbackManager.ReleaseCallback(on_server_pre_fatal_shutdown);
globals::callbackManager.ReleaseCallback(on_server_update_when_not_in_game);
globals::callbackManager.ReleaseCallback(on_server_pre_world_update);
}
void* ServerManager::GetEconItemSystem()
@@ -67,6 +88,11 @@ void* ServerManager::GetEconItemSystem()
return globals::server->GetEconItemSystem();
}
bool ServerManager::IsPaused()
{
return globals::server->IsPaused();
}
void ServerManager::ServerHibernationUpdate(bool bHibernating)
{
CSSHARP_CORE_TRACE("Server hibernation update {0}", bHibernating);
@@ -117,4 +143,59 @@ void ServerManager::OnHostNameChanged(const char *pHostname)
}
}
void ServerManager::PreFatalShutdown()
{
CSSHARP_CORE_TRACE("Pre fatal shutdown");
auto callback = globals::serverManager.on_server_pre_fatal_shutdown;
if (callback && callback->GetFunctionCount()) {
callback->ScriptContext().Reset();
callback->Execute();
}
}
void ServerManager::UpdateWhenNotInGame(float flFrameTime)
{
CSSHARP_CORE_TRACE("Update when not in game {}", flFrameTime);
auto callback = globals::serverManager.on_server_update_when_not_in_game;
if (callback && callback->GetFunctionCount()) {
callback->ScriptContext().Reset();
callback->ScriptContext().Push(flFrameTime);
callback->Execute();
}
}
void ServerManager::PreWorldUpdate(bool bSimulating)
{
std::lock_guard<std::mutex> lock(m_nextWorldUpdateTasksLock);
if (m_nextWorldUpdateTasks.empty())
return;
CSSHARP_CORE_TRACE("Executing queued tasks of size: {0} at time {1}", m_nextWorldUpdateTasks.size(),
globals::getGlobalVars()->curtime);
for (size_t i = 0; i < m_nextWorldUpdateTasks.size(); i++) {
m_nextWorldUpdateTasks[i]();
}
m_nextWorldUpdateTasks.clear();
auto callback = globals::serverManager.on_server_pre_world_update;
if (callback && callback->GetFunctionCount()) {
callback->ScriptContext().Reset();
callback->ScriptContext().Push(bSimulating);
callback->Execute();
}
}
void ServerManager::AddTaskForNextWorldUpdate(std::function<void()>&& task)
{
std::lock_guard<std::mutex> lock(m_nextWorldUpdateTasksLock);
m_nextWorldUpdateTasks.push_back(std::forward<decltype(task)>(task));
}
} // namespace counterstrikesharp

View File

@@ -30,17 +30,29 @@ public:
void OnAllInitialized() override;
void OnShutdown() override;
void* GetEconItemSystem();
bool IsPaused();
void AddTaskForNextWorldUpdate(std::function<void()> &&task);
private:
void ServerHibernationUpdate(bool bHibernating);
void GameServerSteamAPIActivated();
void GameServerSteamAPIDeactivated();
void OnHostNameChanged(const char *pHostname);
void PreFatalShutdown();
void UpdateWhenNotInGame(float flFrameTime);
void PreWorldUpdate(bool bSimulating);
ScriptCallback *on_server_hibernation_update_callback;
ScriptCallback *on_server_steam_api_activated_callback;
ScriptCallback *on_server_steam_api_deactivated_callback;
ScriptCallback *on_server_hostname_changed_callback;
ScriptCallback *on_server_pre_fatal_shutdown;
ScriptCallback *on_server_update_when_not_in_game;
ScriptCallback *on_server_pre_world_update;
std::vector<std::function<void()>> m_nextWorldUpdateTasks;
std::mutex m_nextWorldUpdateTasksLock;
};
} // namespace counterstrikesharp

View File

@@ -1,4 +1,7 @@
OnServerHibernationUpdate: isHibernating:bool
OnGameServerSteamAPIActivated:
OnGameServerSteamAPIDeactivated:
OnHostNameChanged: hostname:string
OnHostNameChanged: hostname:string
PreFatalShutdown:
UpdateWhenNotInGame: frameTime:float
PreWorldUpdate: simulating:bool

View File

@@ -124,6 +124,23 @@ static void IssueClientCommand(ScriptContext& script_context)
globals::engine->ClientCommand(CPlayerSlot(entity_index), command);
}
static const char* GetClientConVarValue(ScriptContext& script_context)
{
auto playerSlot = script_context.GetArgument<int>(0);
auto convarName = script_context.GetArgument<const char*>(1);
return globals::engine->GetClientConVarValue(CPlayerSlot(playerSlot), convarName);
}
static void SetFakeClientConVarValue(ScriptContext& script_context)
{
auto playerSlot = script_context.GetArgument<int>(0);
auto convarName = script_context.GetArgument<const char*>(1);
auto convarValue = script_context.GetArgument<const char*>(2);
globals::engine->SetFakeClientConVarValue(CPlayerSlot(playerSlot), convarName, convarValue);
}
ConVar* FindConVar(ScriptContext& script_context)
{
auto name = script_context.GetArgument<const char*>(0);
@@ -163,5 +180,7 @@ REGISTER_NATIVES(commands, {
ScriptEngine::RegisterNativeHandler("SET_CONVAR_STRING_VALUE", SetConVarStringValue);
ScriptEngine::RegisterNativeHandler("ISSUE_CLIENT_COMMAND", IssueClientCommand);
ScriptEngine::RegisterNativeHandler("GET_CLIENT_CONVAR_VALUE", GetClientConVarValue);
ScriptEngine::RegisterNativeHandler("SET_FAKE_CLIENT_CONVAR_VALUE", SetFakeClientConVarValue);
})
} // namespace counterstrikesharp

View File

@@ -8,4 +8,6 @@ COMMAND_GET_COMMAND_STRING: command:pointer -> string
COMMAND_GET_ARG_BY_INDEX: command:pointer,index:int -> string
ISSUE_CLIENT_COMMAND: clientIndex:int,command:string -> void
FIND_CONVAR: name:string -> pointer
SET_CONVAR_STRING_VALUE: convar:pointer,value:string -> void
SET_CONVAR_STRING_VALUE: convar:pointer,value:string -> void
GET_CLIENT_CONVAR_VALUE: clientIndex:int,convarName:string -> string
SET_FAKE_CLIENT_CONVAR_VALUE: clientIndex:int,convarName:string,convarValue:string -> void

View File

@@ -30,6 +30,7 @@
#include "core/memory.h"
#include "core/log.h"
#include "core/function.h"
#include "core/managers/server_manager.h"
// clang-format on
#if _WIN32
@@ -217,6 +218,14 @@ void QueueTaskForNextFrame(ScriptContext& script_context)
globals::mmPlugin->AddTaskForNextFrame([func]() { reinterpret_cast<voidfunc*>(func)(); });
}
void QueueTaskForNextWorldUpdate(ScriptContext& script_context)
{
auto func = script_context.GetArgument<void*>(0);
typedef void(voidfunc)(void);
globals::serverManager.AddTaskForNextWorldUpdate([func]() { reinterpret_cast<voidfunc*>(func)(); });
}
enum InterfaceType
{
Engine,
@@ -308,6 +317,7 @@ REGISTER_NATIVES(engine, {
ScriptEngine::RegisterNativeHandler("TRACE_RAY", TraceRay);
ScriptEngine::RegisterNativeHandler("GET_TICKED_TIME", GetTickedTime);
ScriptEngine::RegisterNativeHandler("QUEUE_TASK_FOR_NEXT_FRAME", QueueTaskForNextFrame);
ScriptEngine::RegisterNativeHandler("QUEUE_TASK_FOR_NEXT_WORLD_UPDATE", QueueTaskForNextWorldUpdate);
ScriptEngine::RegisterNativeHandler("GET_VALVE_INTERFACE", GetValveInterface);
ScriptEngine::RegisterNativeHandler("GET_COMMAND_PARAM_VALUE", GetCommandParamValue);
ScriptEngine::RegisterNativeHandler("PRINT_TO_SERVER_CONSOLE", PrintToServerConsole);

View File

@@ -21,6 +21,7 @@ TRACE_FILTER_PROXY_SET_SHOULD_HIT_ENTITY_CALLBACK: trace_filter:pointer, callbac
NEW_TRACE_RESULT: -> pointer
GET_TICKED_TIME: -> double
QUEUE_TASK_FOR_NEXT_FRAME: callback:pointer -> void
QUEUE_TASK_FOR_NEXT_WORLD_UPDATE: callback:pointer -> void
GET_VALVE_INTERFACE: interfaceType:int, interfaceName:string -> pointer
GET_COMMAND_PARAM_VALUE: param:string, dataType:DataType_t, defaultValue:any -> any
PRINT_TO_SERVER_CONSOLE: msg:string -> void

View File

@@ -132,6 +132,17 @@ unsigned long GetPlayerAuthorizedSteamID(ScriptContext& script_context) {
return pSteamId->ConvertToUint64();
}
const char* GetPlayerIpAddress(ScriptContext& script_context) {
auto iSlot = script_context.GetArgument<int>(0);
auto pPlayer = globals::playerManager.GetPlayerBySlot(iSlot);
if (pPlayer == nullptr) {
return nullptr;
}
return pPlayer->GetIpAddress();
}
REGISTER_NATIVES(entities, {
ScriptEngine::RegisterNativeHandler("GET_ENTITY_FROM_INDEX", GetEntityFromIndex);
ScriptEngine::RegisterNativeHandler("GET_USERID_FROM_INDEX", GetUserIdFromIndex);
@@ -145,5 +156,6 @@ REGISTER_NATIVES(entities, {
ScriptEngine::RegisterNativeHandler("PRINT_TO_CONSOLE", PrintToConsole);
ScriptEngine::RegisterNativeHandler("GET_FIRST_ACTIVE_ENTITY", GetFirstActiveEntity);
ScriptEngine::RegisterNativeHandler("GET_PLAYER_AUTHORIZED_STEAMID", GetPlayerAuthorizedSteamID);
ScriptEngine::RegisterNativeHandler("GET_PLAYER_IP_ADDRESS", GetPlayerIpAddress);
})
} // namespace counterstrikesharp

View File

@@ -8,4 +8,5 @@ GET_CONCRETE_ENTITY_LIST_POINTER: -> pointer
IS_REF_VALID_ENTITY: entityRef:uint -> bool
PRINT_TO_CONSOLE: index:int, message:string -> void
GET_FIRST_ACTIVE_ENTITY: -> pointer
GET_PLAYER_AUTHORIZED_STEAMID: slot:int -> uint64
GET_PLAYER_AUTHORIZED_STEAMID: slot:int -> uint64
GET_PLAYER_IP_ADDRESS: slot:int -> string

View File

@@ -24,8 +24,14 @@ static void *GetEconItemSystem(ScriptContext& scriptContext) {
return globals::serverManager.GetEconItemSystem();
}
static bool IsServerPaused(ScriptContext& scriptContext)
{
return globals::serverManager.IsPaused();
}
REGISTER_NATIVES(server, {
ScriptEngine::RegisterNativeHandler("GET_ECON_ITEM_SYSTEM", GetEconItemSystem);
ScriptEngine::RegisterNativeHandler("IS_SERVER_PAUSED", IsServerPaused);
})
}

View File

@@ -1 +1,2 @@
GET_ECON_ITEM_SYSTEM: -> pointer
GET_ECON_ITEM_SYSTEM: -> pointer
IS_SERVER_PAUSED: -> bool