mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-05 23:58:24 -08:00
268 lines
9.6 KiB
C#
268 lines
9.6 KiB
C#
/*
|
|
* This file is part of CounterStrikeSharp.
|
|
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
|
*/
|
|
|
|
using CounterStrikeSharp.API.Modules.Memory;
|
|
using CounterStrikeSharp.API.Modules.Utils;
|
|
using System.Collections.Generic;
|
|
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
|
|
{
|
|
public static class Utilities
|
|
{
|
|
// https://github.com/dotabuff/manta/blob/master/entity.go#L186-L190
|
|
public const int MaxEdictBits = 15;
|
|
public const int MaxEdicts = 1 << MaxEdictBits;
|
|
public const int NumEHandleSerialNumberBits = 17;
|
|
public const uint InvalidEHandleIndex = 0xFFFFFFFF;
|
|
|
|
public static IEnumerable<T> FlagsToList<T>(this T flags) where T : Enum
|
|
{
|
|
return Enum.GetValues(typeof(T)).Cast<T>()
|
|
.Where(x => flags.HasFlag(x)).AsEnumerable();
|
|
}
|
|
|
|
public static T? GetEntityFromIndex<T>(int index) where T : CEntityInstance
|
|
{
|
|
var entityPtr = EntitySystem.GetEntityByIndex((uint)index);
|
|
if (entityPtr is null || entityPtr == IntPtr.Zero)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return (T)Activator.CreateInstance(typeof(T), entityPtr)!;
|
|
}
|
|
|
|
public static T? CreateEntityByName<T>(string name) where T : CBaseEntity
|
|
{
|
|
return (T?)Activator.CreateInstance(typeof(T), VirtualFunctions.UTIL_CreateEntityByName(name, -1));
|
|
}
|
|
|
|
public static CCSPlayerController? GetPlayerFromIndex(int index)
|
|
{
|
|
var player = GetEntityFromIndex<CCSPlayerController>(index);
|
|
if (player == null || player.DesignerName != "cs_player_controller")
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return player;
|
|
}
|
|
|
|
public static CCSPlayerController? GetPlayerFromSlot(int slot)
|
|
{
|
|
return GetPlayerFromIndex(slot + 1);
|
|
}
|
|
|
|
public static CCSPlayerController? GetPlayerFromUserid(int userid)
|
|
{
|
|
return GetPlayerFromIndex((userid & 0xFF) + 1);
|
|
}
|
|
|
|
public static CCSPlayerController? GetPlayerFromSteamId(ulong steamId)
|
|
{
|
|
return Utilities.GetPlayers().FirstOrDefault(player => player.AuthorizedSteamID == (SteamID)steamId);
|
|
}
|
|
|
|
public static TargetResult ProcessTargetString(string pattern, CCSPlayerController player)
|
|
{
|
|
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 == null || !pEntity.DesignerName.Contains(designerName)) continue;
|
|
yield return new PointerTo<T>(pEntity.Handle).Value;
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<CEntityInstance> GetAllEntities()
|
|
{
|
|
var pEntity = new CEntityIdentity(EntitySystem.FirstActiveEntity);
|
|
for (; pEntity != null && pEntity.Handle != IntPtr.Zero; pEntity = pEntity.Next)
|
|
{
|
|
yield return new PointerTo<CEntityInstance>(pEntity.Handle).Value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a list of <see cref="CCSPlayerController"/> that are valid and have a valid <see cref="CCSPlayerController.UserId"/> >= 0
|
|
/// </summary>
|
|
public static List<CCSPlayerController> GetPlayers()
|
|
{
|
|
List<CCSPlayerController> players = new();
|
|
|
|
for (int i = 0; i < Server.MaxPlayers; i++)
|
|
{
|
|
var controller = GetPlayerFromSlot(i);
|
|
|
|
if (controller == null || !controller.IsValid || controller.Connected != PlayerConnectedState.PlayerConnected)
|
|
continue;
|
|
|
|
players.Add(controller);
|
|
}
|
|
|
|
return players;
|
|
}
|
|
|
|
[Obsolete]
|
|
public static void ReplyToCommand(CCSPlayerController? player, string msg, bool console = false)
|
|
{
|
|
if (player != null)
|
|
{
|
|
if (console) player.PrintToConsole(msg);
|
|
else player.PrintToChat(msg);
|
|
}
|
|
else
|
|
{
|
|
Server.PrintToConsole(msg);
|
|
}
|
|
}
|
|
|
|
public static unsafe string ReadStringUtf8(IntPtr ptr)
|
|
{
|
|
unsafe
|
|
{
|
|
var nativeUtf8 = Unsafe.Read<IntPtr>((void*)ptr);
|
|
|
|
if (nativeUtf8 == IntPtr.Zero)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var len = 0;
|
|
while (Marshal.ReadByte(nativeUtf8, len) != 0)
|
|
{
|
|
++len;
|
|
}
|
|
|
|
var buffer = new byte[len];
|
|
Marshal.Copy(nativeUtf8, buffer, 0, buffer.Length);
|
|
return Encoding.UTF8.GetString(buffer);
|
|
}
|
|
}
|
|
|
|
public static T? GetPointer<T>(IntPtr pointer) where T : NativeObject
|
|
{
|
|
var pointerTo = Marshal.ReadIntPtr(pointer);
|
|
if (pointerTo == IntPtr.Zero)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
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>
|
|
/// <exception cref="InvalidOperationException">Entity is not valid</exception>
|
|
public static void SetStateChanged(CBaseEntity entity, string className, string fieldName, int extraOffset = 0)
|
|
{
|
|
Guard.IsValidEntity(entity);
|
|
|
|
if (!Schema.IsSchemaFieldNetworked(className, fieldName))
|
|
{
|
|
Application.Instance.Logger.LogWarning(
|
|
"Field {ClassName}:{FieldName} is not networked, but SetStateChanged was called on it.", className, fieldName);
|
|
return;
|
|
}
|
|
|
|
int offset = Schema.GetSchemaOffset(className, fieldName);
|
|
int chainOffset = FindSchemaChain(className);
|
|
|
|
if (chainOffset != 0)
|
|
{
|
|
NativeAPI.SchemaNetworkStateChanged(entity.Handle + chainOffset, (uint)(offset + extraOffset), 0xFFFFFFFF, 0xFFFFFFFF);
|
|
return;
|
|
}
|
|
|
|
NativeAPI.SchemaSetStateChanged(entity.Handle, (uint)(offset + extraOffset), 0xFFFFFFFF, 0xFFFFFFFF);
|
|
|
|
entity.LastNetworkChange = Server.CurrentTime;
|
|
entity.IsSteadyState.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// metamod method 'MetaFactory' to get the pointer of api interface exposed by metamod plugins.
|
|
/// Returns null when the interface cannot be found.
|
|
/// </summary>
|
|
/// <param name="interfaceName">The interface name of metamod api, can be found in their api header file</param>
|
|
public static IntPtr? MetaFactory(string interfaceName)
|
|
{
|
|
IntPtr ptr = NativeAPI.MetaFactory(interfaceName);
|
|
if (ptr == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
}
|
|
}
|