Compare commits

...

9 Commits

Author SHA1 Message Date
Michael Wilson
bb38b1cb1a Cleanup Protobuf Generation (#562) 2024-08-29 11:23:50 +10:00
Michael Wilson
10f472ec85 Implement Usermessages (#435) 2024-08-26 17:18:40 +10:00
dependabot[bot]
28ce183cdc chore(deps): bump libraries/hl2sdk-cs2 from c57d5ab to 0b862d7 (#550)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 04:48:07 +00:00
Michael Wilson
dddf24d0f3 feat: add localizer extension methods for player language 2024-08-19 12:10:12 +10:00
Yarukon
fce68cb160 Update SetStateChanged parameters (#541)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2024-08-19 01:58:25 +00:00
Ian Lucas
a82faeb5c8 Fix CBasePlayerController_SetPawn signature (#547) 2024-08-19 11:52:30 +10:00
dependabot[bot]
ce3ff449b9 chore(deps): bump libraries/hl2sdk-cs2 from a5d9f80 to c57d5ab (#542)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2024-08-10 06:23:36 +00:00
Michael Wilson
aa40d81a86 fix: update gamedata json for update 2024-08-10 16:18:42 +10:00
Michael Wilson
5644921873 Improve plugin unloading consistency (#532) 2024-07-26 22:37:54 +10:00
38 changed files with 3346 additions and 62 deletions

2
.gitignore vendored
View File

@@ -3,6 +3,7 @@
cmake-build-*/
.kdev4/
generated/
!**/protobuf/generated/
# configure_file auto generated.
configs/addons/metamod/counterstrikesharp.vdf
@@ -11,6 +12,7 @@ libraries/mono/
CMakeSettings.json
build-*/
build/
build_test/

View File

@@ -15,6 +15,8 @@ add_subdirectory(libraries/DynoHook)
set_property(TARGET dynohook PROPERTY DYNO_ARCH_X86 64)
set_property(TARGET funchook-static PROPERTY POSITION_INDEPENDENT_CODE ON)
include("makefiles/protobuf.cmake")
set(SOURCE_FILES
libraries/hl2sdk-cs2/public/tier0/memoverride.cpp
src/mm_plugin.cpp
@@ -88,35 +90,20 @@ set(SOURCE_FILES
src/core/managers/server_manager.cpp
src/core/managers/server_manager.h
src/scripting/natives/natives_server.cpp
src/scripting/natives/natives_usermessages.cpp
libraries/nlohmann/json.hpp
src/core/managers/voice_manager.cpp
src/core/managers/voice_manager.h
src/core/managers/usermessage_manager.cpp
src/core/managers/usermessage_manager.h
src/scripting/natives/natives_dynamichooks.cpp
src/core/game_system.h
src/core/game_system.cpp
src/core/UserMessage.h
src/core/UserMessage.cpp
src/core/recipientfilters.h
)
set(PROTO_DIRS -I${CMAKE_CURRENT_SOURCE_DIR}/libraries/Protobufs/csgo)
file(GLOB PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/Protobufs/csgo/*.proto")
## Generate protobuf source & headers
if(LINUX)
set(PROTOC_EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2/devtools/bin/linux/protoc)
elseif(WIN32)
set(PROTOC_EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2/devtools/bin/protoc.exe)
endif()
add_custom_command(
OUTPUT protobuf_output_stamp
COMMAND ${PROTOC_EXECUTABLE} --proto_path=thirdparty/protobuf-3.21.8/src --proto_path=common --cpp_out=common common/network_connection.proto
COMMENT "Generating protobuf file"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2
VERBATIM
)
set(SOURCE_FILES ${SOURCE_FILES} protobuf_output_stamp)
# Sources
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${NATIVES_SOURCES} ${CONVERSIONS_SOURCES} ${CONVERSIONS_HEADERS})
target_include_directories(
@@ -141,7 +128,7 @@ elseif(WIN32)
endif()
# Libraries
target_link_libraries(${PROJECT_NAME} ${COUNTER_STRIKE_SHARP_LINK_LIBRARIES})
target_link_libraries(${PROJECT_NAME} ${COUNTER_STRIKE_SHARP_LINK_LIBRARIES} Protobufs)
add_custom_command(
TARGET ${PROJECT_NAME} PRE_BUILD

View File

@@ -9,7 +9,7 @@
"ClientPrint": {
"signatures": {
"library": "server",
"windows": "48 85 C9 0F 84 ? ? ? ? 48 8B C4 48 89 58 18",
"windows": "48 85 C9 0F 84 ? ? ? ? 48 89 5C 24 ? 55",
"linux": "55 48 89 E5 41 57 49 89 CF 41 56 49 89 D6 41 55 41 89 F5 41 54 4C 8D A5 A0 FE FF FF"
}
},
@@ -35,8 +35,8 @@
"CBasePlayerController_SetPawn": {
"signatures": {
"library": "server",
"windows": "44 88 4C 24 ? 55 56 57 41 54 41 56 48 8D 6C 24 ?",
"linux": "55 48 89 E5 41 57 41 56 41 89 D6 41 55 49 89 FD 41 54 45 89 C4"
"windows": "48 89 74 24 ? 55 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 4C 8B F9",
"linux": "55 48 89 E5 41 57 41 56 41 55 49 89 FD 41 54 45 89 C4"
}
},
"CCSPlayerPawnBase_PostThink": {
@@ -84,8 +84,8 @@
"CCSPlayer_WeaponServices_CanUse": {
"signatures": {
"library": "server",
"windows": "48 89 5C 24 10 48 89 6C 24 18 56 57 41 56 48 83 EC 30 80 B9 A0 00 00 00 00",
"linux": "48 85 F6 0F 84 ? ? ? ? 55 31 C9 48 89 E5 41 55 49 89 FD"
"windows": "48 89 5C 24 10 48 89 6C 24 18 56 57 41 56 48 83 EC 30 48 8B 01",
"linux": "55 48 8D 15 ? ? ? ? 48 89 E5 41 55 49 89 FD 41 54 49 89 F4"
}
},
"CCSPlayer_ItemServices_GiveNamedItem": {
@@ -197,7 +197,7 @@
"StateChanged": {
"signatures": {
"library": "server",
"windows": "40 55 53 56 41 55 41 57 48 8D 6C 24 B0",
"windows": "40 ? 53 56 41 ? 41 ? 48 ? ? ? ? 48 ? ? ? ? ? ? 48 ? ? 45",
"linux": "55 48 89 E5 41 57 41 56 41 55 41 54 53 89 D3"
}
},

View File

@@ -0,0 +1,5 @@
[!INCLUDE [WithUserMessages](../../examples/WithUserMessages/README.md)]
<a href="https://github.com/roflmuffin/CounterStrikeSharp/tree/main/examples/WithUserMessages" class="btn btn-secondary">View project on Github <i class="bi bi-github"></i></a>
[!code-csharp[](../../examples/WithUserMessages/WithUserMessagesPlugin.cs)]

View File

@@ -19,6 +19,8 @@ items:
href: WithSharedTypes.md
- name: Translations
href: WithTranslations.md
- name: User Messages
href: WithUserMessages.md
- name: Voice Overrides
href: WithVoiceOverrides.md
- name: Warcraft Plugin

View File

@@ -1,4 +1,4 @@
using System.Globalization;
using System.Globalization;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
@@ -22,18 +22,29 @@ public class WithTranslationsPlugin : BasePlugin
// A global `Localizer` is provided on the plugin instance.
// You can also use dependency injection to inject `IStringLocalizer` in your own services.
Logger.LogInformation("This message is in the server language: {Message}", Localizer["test.translation"]);
// IStringLocalizer can accept standard string format arguments.
// "This number has 2 decimal places {0:n2}" -> "This number has 2 decimal places 123.55"
Logger.LogInformation(Localizer["test.format", 123.551]);
// This message has colour codes
Server.PrintToChatAll(Localizer["test.colors"]);
// This message has colour codes and formatted values
Server.PrintToChatAll(Localizer["test.colors.withformat", 123.551]);
// This prints the message to all players in their respective language
PrintToAllPlayersLocalized("test.format", 123.456);
}
void PrintToAllPlayersLocalized(string key, params object[] args)
{
foreach (var player in Utilities.GetPlayers().Where(x => x.IsValid))
{
player.PrintToChat(Localizer.ForPlayer(player, key, args));
}
}
[ConsoleCommand("css_replylanguage", "Test Translations")]
public void OnCommandReplyLanguage(CCSPlayerController? player, CommandInfo command)
{

View File

@@ -0,0 +1,2 @@
# With User Messages
This is example shows how you can hook, interrupt and send User Messages.

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\managed\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,84 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.UserMessages;
using Microsoft.Extensions.Logging;
namespace WithUserMessages;
[MinimumApiVersion(80)]
public class WithUserMessagesPlugin : BasePlugin
{
public override string ModuleName => "Example: With User Messages";
public override string ModuleVersion => "1.0.0";
public override string ModuleAuthor => "CounterStrikeSharp & Contributors";
public override string ModuleDescription => "A simple plugin that hooks and sends User Messages";
public override void Load(bool hotReload)
{
// Hooks can be added using the user message ID. In this case it's the ID for `CMsgTEFireBullets`.
HookUserMessage(452, um =>
{
// Sets all weapon sounds to the sound of a silenced usp.
um.SetUInt("weapon_id", 0);
um.SetInt("sound_type", 9);
um.SetUInt("item_def_index", 61);
return HookResult.Continue;
}, HookMode.Pre);
HookUserMessage(118, um =>
{
var author = um.ReadString("param1");
var message = um.ReadString("param2");
Logger.LogInformation("Chat message from {Author}: {Message}", author, message);
for (var i = 0; i < um.Recipients.Count; i++)
{
Logger.LogInformation("Recipient {Index}: {Name}", i, um.Recipients[i].PlayerName);
}
if (message.Contains("stop"))
{
return HookResult.Stop;
}
if (message.Contains("skip"))
{
um.Recipients.Clear();
}
return HookResult.Continue;
});
}
[ConsoleCommand("css_shake")]
public void OnCommandShake(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
// UserMessage.FromPartialName is a helper method that creates a UserMessage object from a partial network name.
// In this case, it will resolve to `CUserMessageShake`.
var message = UserMessage.FromPartialName("Shake");
message.SetFloat("duration", 2);
message.SetFloat("amplitude", 5);
message.SetFloat("frequency", 10f);
message.SetInt("command", 0);
if (command.GetArg(1) == "all")
{
message.Recipients.AddAllPlayers();
}
else
{
message.Recipients.Add(player);
}
message.Send();
// You can also use an overload of `Send` to send the message to a specific player without manually creating a recipient filter.
// message.Send(player);
}
}

View File

@@ -1,5 +1,4 @@
#set(_ITERATOR_DEBUG_LEVEL 2)
add_definitions(-D_LINUX -DPOSIX -DLINUX -DGNUC -DCOMPILER_GCC -DPLATFORM_64BITS -D_FILE_OFFSET_BITS=64)
add_definitions(-D_LINUX -DPOSIX -DLINUX -DGNUC -DCOMPILER_GCC -DPLATFORM_64BITS -D_FILE_OFFSET_BITS=64 -D_GLIBCXX_USE_CXX11_ABI=0)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dstrnicmp=strncasecmp -D_snprintf=snprintf")
@@ -16,6 +15,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse -fno-strict-aliasing"
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics -v -fvisibility=default")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc -static-libstdc++")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs=libprotobuf.a")
set(
COUNTER_STRIKE_SHARP_LINK_LIBRARIES

59
makefiles/protobuf.cmake Normal file
View File

@@ -0,0 +1,59 @@
# Credit for this protobuf generation cmake file goes to Poggicek.
# Based on their work at https://github.com/Poggicek/StickerInspect
set(PROTO_TARGETS
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/network_connection.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/networkbasetypes.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/cs_gameevents.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/engine_gcmessages.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/gcsdk_gcmessages.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/cstrike15_gcmessages.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/cstrike15_usermessages.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/netmessages.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/steammessages.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/usermessages.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/gameevents.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/clientmessages.proto
${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo/te.proto
)
if(UNIX)
set(PROTOC_EXECUTABLE ${PROJECT_SOURCE_DIR}/libraries/hl2sdk-cs2/devtools/bin/linux/protoc)
elseif(WIN32)
set(PROTOC_EXECUTABLE ${PROJECT_SOURCE_DIR}/libraries/hl2sdk-cs2/devtools/bin/protoc.exe)
endif()
foreach(PROTO_TARGET ${PROTO_TARGETS})
get_filename_component(PROTO_FILENAME ${PROTO_TARGET} NAME_WLE)
list(APPEND PROTO_OUTPUT ${PROTO_FILENAME}.pb.cc ${PROTO_FILENAME}.pb.h)
list(APPEND PROTO_INPUT ${PROTO_FILENAME}.proto)
get_filename_component(PROTO_PATH ${PROTO_TARGET} DIRECTORY)
list(APPEND PROTO_PATHS "--proto_path=${PROTO_PATH}")
endforeach()
list(REMOVE_DUPLICATES PROTO_PATHS)
list(TRANSFORM PROTO_OUTPUT PREPEND ${CMAKE_CURRENT_BINARY_DIR}/protobufcompiler/)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/protobufcompiler)
add_custom_command(
OUTPUT ${PROTO_OUTPUT}
COMMAND "${PROTOC_EXECUTABLE}" -I ${PROJECT_SOURCE_DIR}/libraries/hl2sdk-cs2/thirdparty/protobuf-3.21.8/src --proto_path=${PROJECT_SOURCE_DIR}/libraries/Protobufs/csgo ${PROTO_PATHS} --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/protobufcompiler ${PROTO_INPUT}
COMMENT "Generating protobuf file"
)
add_library(Protobufs STATIC
${PROTO_OUTPUT}
)
target_include_directories(Protobufs
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/protobufcompiler
PUBLIC ${PROJECT_SOURCE_DIR}/libraries/hl2sdk-cs2/thirdparty/protobuf-3.21.8/src
)
if(WIN32)
target_link_libraries(Protobufs PUBLIC ${PROJECT_SOURCE_DIR}/libraries/hl2sdk-cs2/lib/public/win64/2015/libprotobuf.lib)
elseif(UNIX)
target_link_libraries(Protobufs PUBLIC ${PROJECT_SOURCE_DIR}/libraries/hl2sdk-cs2/lib/linux64/release/libprotobuf.a)
endif()
set_target_properties(Protobufs PROPERTIES LINKER_LANGUAGE CXX)
set_target_properties(Protobufs PROPERTIES FOLDER SDK)

View File

@@ -20,13 +20,14 @@ endif()
set(CMAKE_STATIC_LIBRARY_PREFIX "")
set(LIBRARIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries)
set(SOURCESDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/hl2sdk-cs2)
set(METAMOD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libraries/metamod-source)
set(SOURCESDK ${SOURCESDK_DIR}/${BRANCH})
set(SOURCESDK_LIB ${SOURCESDK}/lib)
add_definitions(-DMETA_IS_SOURCE2)
add_definitions(-DMETA_IS_SOURCE2 -D_ITERATOR_DEBUG_LEVEL=0)
if(DEFINED ENV{GITHUB_SHA_SHORT})
add_definitions(-DGITHUB_SHA="$ENV{GITHUB_SHA_SHORT}")
@@ -40,6 +41,10 @@ else()
add_definitions(-DBUILD_NUMBER="0")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)
endif()
include_directories(
${SOURCESDK}
${SOURCESDK}/thirdparty/protobuf-3.21.8/src
@@ -68,3 +73,5 @@ include_directories(
)
include(${CMAKE_CURRENT_LIST_DIR}/metamod/configure_metamod.cmake)

View File

@@ -1,7 +1,9 @@
using System;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.UserMessages;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Core
{
@@ -1243,6 +1245,375 @@ namespace CounterStrikeSharp.API.Core
}
}
public static void HookUsermessage(int messageid, InputArgument callback, HookMode mode){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(messageid);
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
ScriptContext.GlobalScriptContext.Push(mode);
ScriptContext.GlobalScriptContext.SetIdentifier(0x76C63A83);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void UnhookUsermessage(int messageid, InputArgument callback, HookMode mode){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(messageid);
ScriptContext.GlobalScriptContext.Push((InputArgument)callback);
ScriptContext.GlobalScriptContext.Push(mode);
ScriptContext.GlobalScriptContext.SetIdentifier(0x63B0AC38);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static bool PbHasfield(UserMessage message, string name){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.SetIdentifier(0xC971FB70);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool));
}
}
public static int PbReadint(UserMessage message, string name, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0x5FA8BDC9);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int));
}
}
public static long PbReadint64(UserMessage message, string name, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0xECCF528B);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (long)ScriptContext.GlobalScriptContext.GetResult(typeof(long));
}
}
public static float PbReadfloat(UserMessage message, string name, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0xED208CEA);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (float)ScriptContext.GlobalScriptContext.GetResult(typeof(float));
}
}
public static bool PbReadbool(UserMessage message, string name, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0x54C0D7F4);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (bool)ScriptContext.GlobalScriptContext.GetResult(typeof(bool));
}
}
public static string PbReadstring(UserMessage message, string name, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0x66CACEEF);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static int PbGetrepeatedfieldcount(UserMessage message, string name){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.SetIdentifier(0xDE4E1549);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int));
}
}
public static void PbSetint(UserMessage message, string name, int value, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0x99BBC059);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbSetint64(UserMessage message, string name, long value, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0xF7AD351B);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbSetfloat(UserMessage message, string name, float value, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0xF7FDEB7A);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbSetbool(UserMessage message, string name, bool value, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0xD1342864);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbSetstring(UserMessage message, string name, string value, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0x15C78B7F);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbAddint(UserMessage message, string name, int value){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.SetIdentifier(0x66CD6A1A);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbAddint64(UserMessage message, string name, object value){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.SetIdentifier(0x4FD05AD8);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbAddfloat(UserMessage message, string name, float value){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.SetIdentifier(0x5117B239);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbAddbool(UserMessage message, string name, bool value){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.SetIdentifier(0x40827C47);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbAddstring(UserMessage message, string name, string value){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(value);
ScriptContext.GlobalScriptContext.SetIdentifier(0x8DFD739C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void PbRemoverepeatedfieldvalue(UserMessage message, string name, int index){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.Push(index);
ScriptContext.GlobalScriptContext.SetIdentifier(0x1721FCB1);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static string PbGetdebugstring(UserMessage message){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.SetIdentifier(0x913FB7BA);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static ulong UsermessageGetrecipients(UserMessage message){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.SetIdentifier(0x70CDDEBE);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (ulong)ScriptContext.GlobalScriptContext.GetResult(typeof(ulong));
}
}
public static void UsermessageSetrecipients(UserMessage message, ulong recipients){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.Push(recipients);
ScriptContext.GlobalScriptContext.SetIdentifier(0xB4ED43AA);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static int UsermessageFindmessageidbyname(string name){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.SetIdentifier(0x22CD6C9F);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int));
}
}
public static IntPtr UsermessageCreate(string name){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(name);
ScriptContext.GlobalScriptContext.SetIdentifier(0xE8E83344);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr));
}
}
public static IntPtr UsermessageCreatebyid(int id){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(id);
ScriptContext.GlobalScriptContext.SetIdentifier(0xBC758632);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr));
}
}
public static void UsermessageSend(UserMessage message){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.SetIdentifier(0x24EB6B3C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static void UsermessageDelete(UserMessage message){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.SetIdentifier(0xE10465D9);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
}
}
public static int UsermessageGetid(UserMessage message){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.SetIdentifier(0xC17BA71B);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (int)ScriptContext.GlobalScriptContext.GetResult(typeof(int));
}
}
public static string UsermessageGetname(UserMessage message){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.SetIdentifier(0xEFE0FD1);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static string UsermessageGettype(UserMessage message){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(message);
ScriptContext.GlobalScriptContext.SetIdentifier(0xEF4842E);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (string)ScriptContext.GlobalScriptContext.GetResult(typeof(string));
}
}
public static IntPtr VectorNew(){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();

View File

@@ -156,17 +156,10 @@ namespace CounterStrikeSharp.API.Core
break;
}
// If our arugment doesn't end in ".dll" - try and construct a path similar to PluginName/PluginName.dll.
// If our argument doesn't end in ".dll" - try and construct a path similar to PluginName/PluginName.dll.
// We'll assume we have a full path if we have ".dll".
var path = info.GetArg(2);
if (!path.EndsWith(".dll"))
{
path = Path.Combine(_scriptHostConfiguration.RootPath, $"plugins/{path}/{path}.dll");
}
else
{
path = Path.Combine(_scriptHostConfiguration.RootPath, path);
}
path = Path.Combine(_scriptHostConfiguration.RootPath, !path.EndsWith(".dll") ? $"plugins/{path}/{path}.dll" : path);
var plugin = _pluginContextQueryHandler.FindPluginByModulePath(path);
@@ -204,7 +197,13 @@ namespace CounterStrikeSharp.API.Core
}
var pluginIdentifier = info.GetArg(2);
IPluginContext? plugin = _pluginContextQueryHandler.FindPluginByIdOrName(pluginIdentifier);
string path;
path = Path.Combine(_scriptHostConfiguration.RootPath,
!pluginIdentifier.EndsWith(".dll") ? $"plugins/{pluginIdentifier}/{pluginIdentifier}.dll" : pluginIdentifier);
var plugin = _pluginContextQueryHandler.FindPluginByIdOrName(pluginIdentifier)
?? _pluginContextQueryHandler.FindPluginByModulePath(path);
if (plugin == null)
{
info.ReplyToCommand($"Could not unload plugin \"{pluginIdentifier}\"");
@@ -249,7 +248,7 @@ namespace CounterStrikeSharp.API.Core
break;
}
}
private void OnLangCommand(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
@@ -305,4 +304,4 @@ namespace CounterStrikeSharp.API.Core
});
}
}
}
}

View File

@@ -30,6 +30,7 @@ using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.Config;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.UserMessages;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
@@ -528,6 +529,24 @@ namespace CounterStrikeSharp.API.Core
NativeAPI.HookEntityOutput(classname, outputName, subscriber.GetInputArgument(), mode);
EntityOutputHooks[handler] = subscriber;
}
public void HookUserMessage(int messageId, UserMessage.UserMessageHandler handler, HookMode mode = HookMode.Pre)
{
var subscriber = new CallbackSubscriber(handler, handler,
() => UnhookUserMessage(messageId, handler));
NativeAPI.HookUsermessage(messageId, subscriber.GetInputArgument(), mode);
Handlers[handler] = subscriber;
}
public void UnhookUserMessage(int messageId, UserMessage.UserMessageHandler handler, HookMode mode = HookMode.Pre)
{
if (!Handlers.TryGetValue(handler, out var subscriber)) return;
NativeAPI.UnhookUsermessage(messageId, subscriber.GetInputArgument(), mode);
FunctionReference.Remove(subscriber.GetReferenceIdentifier());
Handlers.Remove(handler);
}
/// <summary>
/// Unhooks an entity output.

View File

@@ -28,6 +28,7 @@ using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Core
{
@@ -205,6 +206,15 @@ namespace CounterStrikeSharp.API.Core
return;
}
else if (arg is IMarshalToNative marshalToNative)
{
foreach (var value in marshalToNative.GetNativeObject())
{
Push(context ,value);
}
return;
}
else if (arg is NativeObject nativeObject)
{
Push(context, (InputArgument)nativeObject);
@@ -497,4 +507,4 @@ namespace CounterStrikeSharp.API.Core
return $"ScriptContext{{numArgs={m_extContext.numArguments}}}";
}
}
}
}

View File

@@ -0,0 +1,25 @@
using Microsoft.Extensions.Localization;
namespace CounterStrikeSharp.API.Core.Translations;
public static class LocalizerExtensions
{
/// <summary>
/// Returns a localized string using the locale of the specified player.
/// <remarks>Defaults to the server language if the player is invalid.</remarks>
/// </summary>
public static string ForPlayer(this IStringLocalizer localizer, CCSPlayerController? player, string key)
{
using WithTemporaryCulture temporaryCulture = new WithTemporaryCulture(player.GetLanguage());
return localizer[key];
}
/// <summary>
/// <inheritdoc cref="ForPlayer(Microsoft.Extensions.Localization.IStringLocalizer,CounterStrikeSharp.API.Core.CCSPlayerController?,string)"/>
/// </summary>
public static string ForPlayer(this IStringLocalizer localizer, CCSPlayerController? player, string key, params object[] args)
{
using WithTemporaryCulture temporaryCulture = new WithTemporaryCulture(player.GetLanguage());
return localizer[key, args];
}
}

View File

@@ -32,4 +32,10 @@ public class ColorMarshaler : ICustomMarshal<Color>
{
Marshal.WriteInt32(pointer, (managedObj.A << 24) | (managedObj.B << 16) | (managedObj.G << 8) | managedObj.R);
}
}
}
internal interface IMarshalToNative
{
// Returns the object format that will be passed to the native API when marshalled by the script context.
IEnumerable<object> GetNativeObject();
}

View File

@@ -82,9 +82,9 @@ public static class VirtualFunctions
new(GameData.GetSignature("CBasePlayerPawn_RemovePlayerItem"));
public static Action<IntPtr, IntPtr> RemovePlayerItemVirtual = RemovePlayerItemFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, IntPtr, int, short, short> StateChangedFunc =
public static MemoryFunctionVoid<IntPtr, IntPtr, int, short, int> StateChangedFunc =
new(GameData.GetSignature("StateChanged"));
public static Action<IntPtr, IntPtr, int, short, short> StateChanged = StateChangedFunc.Invoke;
public static Action<IntPtr, IntPtr, int, short, int> StateChanged = StateChangedFunc.Invoke;
public static MemoryFunctionVoid<IntPtr, int, long> NetworkStateChangedFunc = new(GameData.GetSignature("NetworkStateChanged"));
public static Action<IntPtr, int, long> NetworkStateChanged = NetworkStateChangedFunc.Invoke;

View File

@@ -0,0 +1,171 @@
using System.Collections.ObjectModel;
using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Modules.UserMessages;
public class UserMessage : NativeObject, IDisposable
{
private RecipientFilter _recipients;
public delegate HookResult UserMessageHandler(UserMessage native);
public UserMessage(IntPtr pointer) : base(pointer)
{
Recipients = new RecipientFilter(NativeAPI.UsermessageGetrecipients(this));
Recipients.CollectionChanged = () => NativeAPI.UsermessageSetrecipients(this, Recipients.GetRecipientMask());
}
/// <summary>
/// Creates a new user message with a given network message name partial match.
/// </summary>
/// <param name="name"></param>
/// <throws>if the name is not a valid network message name</throws>
public static UserMessage FromPartialName(string name) => new(NativeAPI.UsermessageCreate(name));
/// <summary>
/// Creates a new user message with a given network message ID.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static UserMessage FromId(int id) => new(NativeAPI.UsermessageCreatebyid(id));
/// <summary>
/// Finds a network message ID by name.
/// <remarks>
/// Network message system must be loaded before this function can be used.
/// Avoid calling this method from <see cref="IPlugin.Load"/>
/// </remarks>
/// </summary>
public static int FindIdByName(string name) => NativeAPI.UsermessageFindmessageidbyname(name);
public bool HasField(string fieldName) => NativeAPI.PbHasfield(this, fieldName);
public int ReadInt(string fieldName, int? index = null) => NativeAPI.PbReadint(this, fieldName, index ?? -1);
public uint ReadUInt(string fieldName, int? index = null) => (uint)NativeAPI.PbReadint(this, fieldName, index ?? -1);
public long ReadInt64(string fieldName, int? index = null) => NativeAPI.PbReadint64(this, fieldName, index ?? -1);
public ulong ReadUInt64(string fieldName, int? index = null) => (ulong)NativeAPI.PbReadint64(this, fieldName, index ?? -1);
public float ReadFloat(string fieldName, int? index = null) => NativeAPI.PbReadfloat(this, fieldName, index ?? -1);
public double ReadDouble(string fieldName, int? index = null) => NativeAPI.PbReadfloat(this, fieldName, index ?? -1);
public string ReadString(string fieldName, int? index = null) => NativeAPI.PbReadstring(this, fieldName, index ?? -1);
public bool ReadBool(string fieldName, int? index = null) => NativeAPI.PbReadbool(this, fieldName, index ?? -1);
public void SetInt(string fieldName, int value, int? index = null) => NativeAPI.PbSetint(this, fieldName, value, index ?? -1);
public void SetUInt(string fieldName, uint value, int? index = null) => NativeAPI.PbSetint(this, fieldName, (int)value, index ?? -1);
public void SetInt64(string fieldName, long value, int? index = null) => NativeAPI.PbSetint64(this, fieldName, value, index ?? -1);
public void SetUInt64(string fieldName, ulong value, int? index = null) =>
NativeAPI.PbSetint64(this, fieldName, (long)value, index ?? -1);
public void SetFloat(string fieldName, float value, int? index = null) => NativeAPI.PbSetfloat(this, fieldName, value, index ?? -1);
public void SetDouble(string fieldName, double value, int? index = null) =>
NativeAPI.PbSetfloat(this, fieldName, (float)value, index ?? -1);
public void SetString(string fieldName, string value, int? index = null) => NativeAPI.PbSetstring(this, fieldName, value, index ?? -1);
public void SetBool(string fieldName, bool value, int? index = null) => NativeAPI.PbSetbool(this, fieldName, value, index ?? -1);
public int GetRepeatedFieldCount(string fieldName) => NativeAPI.PbGetrepeatedfieldcount(this, fieldName);
public void RemoveRepeatedField(string fieldName, int index) => NativeAPI.PbRemoverepeatedfieldvalue(this, fieldName, index);
public void AddInt(string fieldName, int value) => NativeAPI.PbAddint(this, fieldName, value);
public void AddUInt(string fieldName, uint value) => NativeAPI.PbAddint(this, fieldName, (int)value);
public void AddInt64(string fieldName, long value) => NativeAPI.PbAddint64(this, fieldName, value);
public void AddUInt64(string fieldName, ulong value) => NativeAPI.PbAddint64(this, fieldName, (long)value);
public void AddFloat(string fieldName, float value) => NativeAPI.PbAddfloat(this, fieldName, value);
public void AddDouble(string fieldName, double value) => NativeAPI.PbAddfloat(this, fieldName, (float)value);
public void AddString(string fieldName, string value) => NativeAPI.PbAddstring(this, fieldName, value);
public void AddBool(string fieldName, bool value) => NativeAPI.PbAddbool(this, fieldName, value);
// public UserMessage ReadMessage(string fieldName) => NativeAPI.PbReadmessage(this, fieldName);
// public UserMessage ReadRepeatedMessage(string fieldName, int index ) => NativeAPI.PbReadrepeatedmessage(this, fieldName, index);
// public UserMessage AddMessage(string fieldName) => NativeAPI.PbAddmessage(this, fieldName);
public void Send() => NativeAPI.UsermessageSend(this);
public void Send(RecipientFilter recipientFilter)
{
Recipients = recipientFilter;
Send();
}
/// <summary>
/// Returns the network message name of this user message.
/// <example>CMsgTEFireBullets [452]</example>
/// </summary>
public string Name => NativeAPI.UsermessageGetname(this);
/// <summary>
/// Returns the network message ID of this user message.
/// <example>452</example>
/// </summary>
public int Id => NativeAPI.UsermessageGetid(this);
/// <summary>
/// Returns the protobuf message type of this user message.
/// <example>CMsgTEFireBullets</example>
/// </summary>
public string Type => NativeAPI.UsermessageGettype(this);
/// <summary>
/// Returns the debug string of this user message, as defined by protobuf.
/// <example>
/// <code>
/// CMsgTEFireBullets [452], 452, origin {
/// x: -1969.24951
/// y: 2046.12683
/// z: 60.9242516
/// }
/// angles {
/// x: 32.8350143
/// y: 19.0894909
/// z: 0
/// }
/// weapon_id: 2441476
/// mode: 1
/// seed: 991759080
/// player: 6390016
/// inaccuracy: 0.00490000239
/// recoil_index: 0
/// spread: 0.0015
/// sound_type: 9
/// item_def_index: 61
/// sound_dsp_effect: 2005810340
/// ent_origin {
/// x: -1969.24976
/// y: 2046.12671
/// z: -1.92980957
/// }
/// num_bullets_remaining: 12
/// attack_type: 0
/// </code>
/// </example>
/// </summary>
public string DebugString => NativeAPI.PbGetdebugstring(this);
public RecipientFilter Recipients
{
get => _recipients;
set
{
_recipients = value;
NativeAPI.UsermessageSetrecipients(this, _recipients.GetRecipientMask());
}
}
private void ReleaseUnmanagedResources()
{
Server.NextFrame(() => { NativeAPI.UsermessageDelete(this); });
}
public void Dispose()
{
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
~UserMessage()
{
ReleaseUnmanagedResources();
}
}

View File

@@ -0,0 +1,164 @@
/*
* 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 System.Collections;
namespace CounterStrikeSharp.API.Modules.Utils;
/// <summary>
/// A generic filter for determining whom to send message/sounds etc.
/// to and providing a bit of additional state information
/// </summary>
public class RecipientFilter : IList<CCSPlayerController>, IMarshalToNative
{
private readonly List<CCSPlayerController> _recipients = new();
internal Action? CollectionChanged;
public RecipientFilter()
{
}
public RecipientFilter(params CCSPlayerController[] slots)
{
foreach (var slot in slots)
{
Add(slot);
}
}
public RecipientFilter(params int[] slots)
{
foreach (var slot in slots)
{
Add(slot);
}
}
public RecipientFilter(ulong mask)
{
for (var i = 0; i < 64; i++)
{
if ((mask & ((ulong)1 << i)) != 0)
{
Add(i);
}
}
}
public IEnumerable<object> GetNativeObject()
{
yield return GetRecipientMask();
}
public ulong GetRecipientMask()
{
ulong recipientMask = 0;
foreach (var recipient in _recipients)
{
recipientMask |= (ulong)1 << recipient.Slot;
}
return recipientMask;
}
public int IndexOf(CCSPlayerController item)
{
return _recipients.IndexOf(item);
}
public void Insert(int index, CCSPlayerController item)
{
_recipients.Insert(index, item);
CollectionChanged?.Invoke();
}
public void RemoveAt(int index)
{
_recipients.RemoveAt(index);
CollectionChanged?.Invoke();
}
public CCSPlayerController this[int index]
{
get => _recipients[index];
set => throw new NotImplementedException();
}
public void Add(int slot)
{
var player = Utilities.GetPlayerFromSlot(slot);
if (player == null)
{
throw new ArgumentException($"Player with slot {slot} not found");
}
_recipients.Add(player);
CollectionChanged?.Invoke();
}
public void AddAllPlayers()
{
_recipients.AddRange(Utilities.GetPlayers());
CollectionChanged?.Invoke();
}
public IEnumerator<CCSPlayerController> GetEnumerator()
{
return _recipients.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(CCSPlayerController item)
{
_recipients.Add(item);
CollectionChanged?.Invoke();
}
public void Clear()
{
_recipients.Clear();
CollectionChanged?.Invoke();
}
public bool Contains(CCSPlayerController item)
{
return _recipients.Contains(item);
}
public void CopyTo(CCSPlayerController[] array, int arrayIndex)
{
_recipients.CopyTo(array, arrayIndex);
}
public bool Remove(CCSPlayerController item)
{
var success = _recipients.Remove(item);
CollectionChanged?.Invoke();
return success;
}
public int Count => _recipients.Count;
public bool IsReadOnly => false;
public static implicit operator RecipientFilter(CCSPlayerController player) => new(player);
}

View File

@@ -40,6 +40,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySharedTypes.Contracts", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithSharedTypesConsumer", "..\examples\WithSharedTypesConsumer\WithSharedTypesConsumer.csproj", "{76AD7BB0-A096-4336-83E2-B32CAE4E9933}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithUserMessages", "..\examples\WithUserMessages\WithUserMessages.csproj", "{A14029BA-CADE-4F25-ADC5-48CF14332F61}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -122,6 +124,10 @@ Global
{76AD7BB0-A096-4336-83E2-B32CAE4E9933}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76AD7BB0-A096-4336-83E2-B32CAE4E9933}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76AD7BB0-A096-4336-83E2-B32CAE4E9933}.Release|Any CPU.Build.0 = Release|Any CPU
{A14029BA-CADE-4F25-ADC5-48CF14332F61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A14029BA-CADE-4F25-ADC5-48CF14332F61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A14029BA-CADE-4F25-ADC5-48CF14332F61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A14029BA-CADE-4F25-ADC5-48CF14332F61}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{57E64289-5D69-4AA1-BEF0-D0D96A55EE8F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
@@ -139,5 +145,6 @@ Global
{1309954E-FAF7-47A5-9FF9-C7263B33E4E3} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
{A37676EA-CF2F-424D-85A1-C359D07A679D} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
{76AD7BB0-A096-4336-83E2-B32CAE4E9933} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
{A14029BA-CADE-4F25-ADC5-48CF14332F61} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A}
EndGlobalSection
EndGlobal

View File

@@ -30,6 +30,7 @@ using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Events;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.UserMessages;
using CounterStrikeSharp.API.Modules.Utils;
using Microsoft.Extensions.Logging;
@@ -160,6 +161,19 @@ namespace TestPlugin
info.DontBroadcast = true;
}
if (@event.Attacker != null)
{
var message = UserMessage.FromPartialName("Shake");
Logger.LogInformation("Created user message CCSUsrMsg_Shake {Message:x}", message.Handle);
message.SetFloat("duration", 2);
message.SetFloat("amplitude", 5);
message.SetFloat("frequency", 10f);
message.SetInt("command", 0);
message.Send(@event.Attacker);
}
return HookResult.Continue;
}, HookMode.Pre);
@@ -283,7 +297,7 @@ namespace TestPlugin
case "flashbang_projectile":
var flashbang = entity.As<CBaseCSGrenadeProjectile>();
Server.NextFrame(() => { flashbang.Remove(); });
// Server.NextFrame(() => { flashbang.Remove(); });
return;
}
});

41
src/core/UserMessage.cpp Normal file
View File

@@ -0,0 +1,41 @@
/*
* 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/>. *
*/
#include "UserMessage.h"
#include "networksystem/inetworkserializer.h"
using namespace google;
namespace counterstrikesharp {
int UserMessage::GetMessageID() { return msgSerializable->GetNetMessageInfo()->m_MessageId; }
std::string UserMessage::GetMessageName() { return msgSerializable->GetUnscopedName(); }
bool UserMessage::HasField(std::string fieldName)
{
const google::protobuf::Descriptor* descriptor = msg->GetDescriptor();
const google::protobuf::FieldDescriptor* field = descriptor->FindFieldByName(fieldName);
if (field == nullptr || (field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED))
{
return false;
}
return this->msg->GetReflection()->HasField(*this->msg, field);
}
const CNetMessagePB<google::protobuf::Message>* UserMessage::GetProtobufMessage() { return msg; }
} // namespace counterstrikesharp

1168
src/core/UserMessage.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@
#include "core/managers/entity_manager.h"
#include "core/managers/server_manager.h"
#include "core/managers/voice_manager.h"
#include "core/managers/usermessage_manager.h"
#include <public/game/server/iplayerinfo.h>
#include <public/entity2/entitysystem.h>
@@ -52,6 +53,7 @@ IUniformRandomStream* randomStream = nullptr;
IEngineTrace* engineTrace = nullptr;
IEngineSound* engineSound = nullptr;
IEngineServiceMgr* engineServiceManager = nullptr;
INetworkMessages* networkMessages = nullptr;
INetworkStringTableContainer* netStringTables = nullptr;
CGlobalVars* globalVars = nullptr;
IFileSystem* fileSystem = nullptr;
@@ -88,6 +90,7 @@ ChatManager chatManager;
ServerManager serverManager;
VoiceManager voiceManager;
TickScheduler tickScheduler;
UserMessageManager userMessageManager;
bool gameLoopInitialized = false;
GetLegacyGameEventListener_t* GetLegacyGameEventListener = nullptr;

View File

@@ -6,14 +6,14 @@
#undef protected
#undef private
#include <thread>
#include <sourcehook/sourcehook.h>
#include <memory>
#include <thread>
#include "ISmmAPI.h"
#include "eiface.h"
#include "iserver.h"
#include <sourcehook/sourcehook.h>
class IGameEventManager2;
class IPlayerInfoManager;
@@ -26,6 +26,7 @@ class IEngineServiceMgr;
class INetworkStringTableContainer;
class CGlobalVars;
class IFileSystem;
class INetworkMessages;
class IServerTools;
class IPhysics;
class IPhysicsCollision;
@@ -73,6 +74,7 @@ extern IUniformRandomStream* randomStream;
extern IEngineTrace* engineTrace;
extern IEngineSound* engineSound;
extern IEngineServiceMgr* engineServiceManager;
extern INetworkMessages* networkMessages;
extern INetworkStringTableContainer* netStringTables;
extern CGlobalVars* globalVars;
extern IFileSystem* fileSystem;

View File

@@ -0,0 +1,208 @@
/*
* 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/>. *
*/
#include "core/managers/usermessage_manager.h"
#include <entity2/entitysystem.h>
#include <public/eiface.h>
#include "core/UserMessage.h"
#include "core/log.h"
#include "core/managers/player_manager.h"
#include "igameeventsystem.h"
#include "scripting/callback_manager.h"
using namespace google;
SH_DECL_HOOK8_void(IGameEventSystem,
PostEventAbstract,
SH_NOATTRIB,
0,
CSplitScreenSlot,
bool,
int,
const uint64*,
INetworkMessageInternal*,
const CNetMessage*,
unsigned long,
NetChannelBufType_t)
namespace counterstrikesharp
{
UserMessageManager::UserMessageManager() {}
UserMessageManager::~UserMessageManager() {}
void UserMessageManager::OnAllInitialized()
{
SH_ADD_HOOK_MEMFUNC(IGameEventSystem, PostEventAbstract, globals::gameEventSystem, this, &UserMessageManager::Hook_PostEvent,
false);
}
void UserMessageManager::OnShutdown()
{
SH_REMOVE_HOOK_MEMFUNC(IGameEventSystem, PostEventAbstract, globals::gameEventSystem, this, &UserMessageManager::Hook_PostEvent,
false);
}
void UserMessageManager::HookUserMessage(int messageId, CallbackT fnCallback, HookMode mode)
{
UserMessageHook* pHook;
CSSHARP_CORE_TRACE("Hooking user message: {0} with callback pointer: {1}", messageId, (void*)fnCallback);
auto search = m_hooksMap.find(messageId);
// If hook struct is not found
if (search == m_hooksMap.end())
{
pHook = new UserMessageHook();
if (mode == HookMode::Post)
{
pHook->m_pPostHook = globals::callbackManager.CreateCallback("");
pHook->m_pPostHook->AddListener(fnCallback);
}
else
{
pHook->m_pPreHook = globals::callbackManager.CreateCallback("");
pHook->m_pPreHook->AddListener(fnCallback);
}
pHook->m_messageId = messageId;
m_hooksMap[messageId] = pHook;
return;
}
else
{
pHook = search->second;
}
if (mode == HookMode::Post)
{
if (!pHook->m_pPostHook)
{
pHook->m_pPostHook = globals::callbackManager.CreateCallback("");
}
pHook->m_pPostHook->AddListener(fnCallback);
}
else
{
if (!pHook->m_pPreHook)
{
pHook->m_pPreHook = globals::callbackManager.CreateCallback("");
}
pHook->m_pPreHook->AddListener(fnCallback);
}
}
void UserMessageManager::UnhookUserMessage(int messageId, CallbackT fnCallback, HookMode mode)
{
UserMessageHook* pHook;
ScriptCallback* pCallback;
auto search = m_hooksMap.find(messageId);
if (search == m_hooksMap.end())
{
return;
}
pHook = search->second;
if (mode == HookMode::Post)
{
pCallback = pHook->m_pPostHook;
}
else
{
pCallback = pHook->m_pPreHook;
}
pCallback->RemoveListener(fnCallback);
if (pCallback->GetFunctionCount() == 0)
{
globals::callbackManager.ReleaseCallback(pCallback);
if (mode == HookMode::Post)
{
pHook->m_pPostHook = nullptr;
}
else
{
pHook->m_pPreHook = nullptr;
}
}
CSSHARP_CORE_TRACE("Unhooking user message: {0} with callback pointer: {1}", messageId, (void*)fnCallback);
return;
}
void UserMessageManager::Hook_PostEvent(CSplitScreenSlot nSlot, bool bLocalOnly, int nClientCount, const uint64* clients,
INetworkMessageInternal* pEvent, const CNetMessage* pData, unsigned long nSize,
NetChannelBufType_t bufType)
{
auto message = UserMessage(pEvent, pData, nClientCount, const_cast<uint64*>(clients));
auto iMessageID = message.GetMessageID();
auto I = m_hooksMap.find(iMessageID);
HookResult result = HookResult::Continue;
if (I != m_hooksMap.end())
{
auto pEventHook = I->second;
auto* pCallback = pEventHook->m_pPreHook;
if (pCallback)
{
CSSHARP_CORE_TRACE("Pushing user message `{}` pointer: {}, post: {}", iMessageID, (void*)pEvent, false);
pCallback->Reset();
pCallback->ScriptContext().Push(&message);
for (auto fnMethodToCall : pCallback->GetFunctions())
{
if (!fnMethodToCall) continue;
fnMethodToCall(&pCallback->ScriptContextStruct());
auto hookResult = pCallback->ScriptContext().GetResult<HookResult>();
if (hookResult >= HookResult::Stop)
{
RETURN_META(MRES_SUPERCEDE);
}
if (hookResult >= HookResult::Handled)
{
result = hookResult;
}
}
}
}
if (result >= HookResult::Handled)
{
RETURN_META(MRES_SUPERCEDE);
}
RETURN_META(MRES_IGNORED);
}
} // namespace counterstrikesharp

View File

@@ -0,0 +1,64 @@
/*
* 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/>. *
*/
#pragma once
#include "core/global_listener.h"
#include "core/globals.h"
#include "inetchannel.h"
#include "networksystem/inetworkserializer.h"
#include "scripting/script_engine.h"
namespace counterstrikesharp {
class ScriptCallback;
struct UserMessageHook
{
UserMessageHook()
{
m_pPreHook = nullptr;
m_pPostHook = nullptr;
}
counterstrikesharp::ScriptCallback* m_pPreHook;
counterstrikesharp::ScriptCallback* m_pPostHook;
int m_messageId;
};
class UserMessageManager : public GlobalClass
{
public:
UserMessageManager();
~UserMessageManager();
void OnAllInitialized() override;
void OnShutdown() override;
void Hook_PostEvent(CSplitScreenSlot nSlot,
bool bLocalOnly,
int nClientCount,
const uint64* clients,
INetworkMessageInternal* pEvent,
const CNetMessage* pData,
unsigned long nSize,
NetChannelBufType_t bufType);
void UnhookUserMessage(int messageId, CallbackT fnCallback, HookMode mode);
void HookUserMessage(int messageId, CallbackT fnCallback, HookMode mode);
private:
ScriptCallback* m_on_user_message_callback;
std::map<int, UserMessageHook*> m_hooksMap;
};
} // namespace counterstrikesharp

View File

@@ -0,0 +1,84 @@
/*
* 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/>. *
*/
#include "irecipientfilter.h"
class CSingleRecipientFilter : public IRecipientFilter
{
public:
CSingleRecipientFilter(CPlayerSlot iRecipient, NetChannelBufType_t nBufType = BUF_RELIABLE, bool bInitMessage = false)
: m_iRecipient(iRecipient), m_nBufType(nBufType), m_bInitMessage(bInitMessage)
{
}
~CSingleRecipientFilter() override {}
NetChannelBufType_t GetNetworkBufType(void) const override { return m_nBufType; }
bool IsInitMessage(void) const override { return m_bInitMessage; }
int GetRecipientCount(void) const override { return 1; }
CPlayerSlot GetRecipientIndex(int slot) const override { return m_iRecipient; }
private:
CPlayerSlot m_iRecipient;
NetChannelBufType_t m_nBufType;
bool m_bInitMessage;
};
class CRecipientFilter : public IRecipientFilter
{
public:
CRecipientFilter()
{
m_nBufType = BUF_RELIABLE;
m_bInitMessage = false;
}
~CRecipientFilter() override {}
NetChannelBufType_t GetNetworkBufType(void) const override { return m_nBufType; }
bool IsInitMessage(void) const override { return m_bInitMessage; }
int GetRecipientCount(void) const override { return m_Recipients.Count(); }
CPlayerSlot GetRecipientIndex(int slot) const override
{
if (slot < 0 || slot >= GetRecipientCount()) return CPlayerSlot(-1);
return m_Recipients[slot];
}
void AddRecipient(CPlayerSlot slot)
{
if (m_Recipients.Find(slot) != m_Recipients.InvalidIndex()) return;
m_Recipients.AddToTail(slot);
}
void AddRecipientsFromMask(uint64 mask)
{
for (int i = 0; i < 64; ++i)
{
if (mask & (uint64)1 << i)
{
AddRecipient(CPlayerSlot(i));
}
}
}
private:
NetChannelBufType_t m_nBufType;
bool m_bInitMessage;
CUtlVectorFixed<CPlayerSlot, 64> m_Recipients;
};

View File

@@ -101,6 +101,7 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
GET_V_IFACE_ANY(GetEngineFactory, globals::schemaSystem, CSchemaSystem, SCHEMASYSTEM_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetEngineFactory, globals::gameEventSystem, IGameEventSystem, GAMEEVENTSYSTEM_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetEngineFactory, globals::engineServiceManager, IEngineServiceMgr, ENGINESERVICEMGR_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetEngineFactory, globals::networkMessages, INetworkMessages, NETWORKMESSAGES_INTERFACE_VERSION);
auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core");
globals::coreConfig = new CCoreConfig(coreconfig_path);

View File

@@ -17,17 +17,17 @@
#ifndef _INCLUDE_METAMOD_SOURCE_STUB_PLUGIN_H_
#define _INCLUDE_METAMOD_SOURCE_STUB_PLUGIN_H_
// clang-format off
#include <ISmmPlugin.h>
#include <functional>
#include <iserver.h>
#include <igameevents.h>
#include <iplayerinfo.h>
#include <iserver.h>
#include <sh_vector.h>
#include <functional>
#include <vector>
#include "concurrentqueue.h"
#include "entitysystem.h"
#include "concurrentqueue.h"
// clang-format on
namespace counterstrikesharp {
class ScriptCallback;

View File

@@ -5,4 +5,5 @@ UNHOOK_FUNCTION: function:pointer, hook:callback, post:bool -> void
EXECUTE_VIRTUAL_FUNCTION: function:pointer,arguments:object[] -> any
FIND_SIGNATURE: modulePath:string, signature:string -> pointer
GET_NETWORK_VECTOR_SIZE: vec:pointer -> int
GET_NETWORK_VECTOR_ELEMENT_AT: vec:pointer, index:int -> pointer
GET_NETWORK_VECTOR_ELEMENT_AT: vec:pointer, index:int -> pointer
REMOVE_ALL_NETWORK_VECTOR_ELEMENTS: vec:pointer -> void

View File

@@ -0,0 +1,713 @@
/*
* 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/>. *
*/
// clang-format off
#include "core/UserMessage.h"
#include "core/globals.h"
#include "core/log.h"
#include "core/managers/usermessage_manager.h"
#include "igameeventsystem.h"
#include "scripting/autonative.h"
#include "core/recipientfilters.h"
// clang-format on
namespace counterstrikesharp {
#define GET_MESSAGE_OR_ERR() \
auto message = scriptContext.GetArgument<UserMessage*>(0); \
if (message == nullptr || message->GetMessageID() < 0) \
{ \
scriptContext.ThrowNativeError("Invalid message"); \
return; \
}
#define GET_FIELD_NAME_OR_ERR() const char* fieldName = scriptContext.GetArgument<const char*>(1);
std::vector<UserMessage*> managed_usermessages;
static void HookUserMessage(ScriptContext& script_context)
{
auto messageId = script_context.GetArgument<int>(0);
auto callback = script_context.GetArgument<CallbackT>(1);
auto mode = script_context.GetArgument<HookMode>(2);
globals::userMessageManager.HookUserMessage(messageId, callback, mode);
}
static void UnhookUserMessage(ScriptContext& script_context)
{
auto messageId = script_context.GetArgument<int>(0);
auto callback = script_context.GetArgument<CallbackT>(1);
auto mode = script_context.GetArgument<HookMode>(2);
globals::userMessageManager.UnhookUserMessage(messageId, callback, mode);
}
static void GetInt32OrUnsignedOrEnum(ScriptContext& script_context)
{
auto message = script_context.GetArgument<UserMessage*>(0);
auto fieldName = script_context.GetArgument<const char*>(1);
int returnValue;
if (!message->GetInt32OrUnsignedOrEnum(fieldName, &returnValue))
{
script_context.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
script_context.SetResult(returnValue);
}
static void SetInt32OrUnsignedOrEnum(ScriptContext& script_context)
{
auto message = script_context.GetArgument<UserMessage*>(0);
auto fieldName = script_context.GetArgument<const char*>(1);
auto value = script_context.GetArgument<int>(2);
if (!message->SetInt32OrUnsignedOrEnum(fieldName, value))
{
script_context.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
static void PbReadInt(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
int returnValue;
auto index = scriptContext.GetArgument<int>(2);
if (index < 0)
{
if (!message->GetInt32OrUnsignedOrEnum(fieldName, &returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
else
{
if (!message->GetRepeatedInt32OrUnsignedOrEnum(fieldName, index, &returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
scriptContext.SetResult(returnValue);
}
static void PbReadInt64(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
int64 returnValue;
auto index = scriptContext.GetArgument<int>(2);
if (index < 0)
{
if (!message->GetInt64OrUnsigned(fieldName, &returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
else
{
if (!message->GetRepeatedInt64OrUnsigned(fieldName, index, &returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
scriptContext.SetResult(returnValue);
}
static void PbReadFloat(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
float returnValue;
auto index = scriptContext.GetArgument<int>(2);
if (index < 0)
{
if (!message->GetFloatOrDouble(fieldName, &returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
else
{
if (!message->GetRepeatedFloatOrDouble(fieldName, index, &returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
scriptContext.SetResult(returnValue);
}
static void PbReadBool(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
bool returnValue;
auto index = scriptContext.GetArgument<int>(2);
if (index < 0)
{
if (!message->GetBool(fieldName, &returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
else
{
if (!message->GetRepeatedBool(fieldName, index, &returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
scriptContext.SetResult(returnValue);
}
static void PbReadString(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
std::string returnValue;
auto index = scriptContext.GetArgument<int>(2);
if (index < 0)
{
if (!message->GetString(fieldName, returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
else
{
if (!message->GetRepeatedString(fieldName, index, returnValue))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
return;
}
}
scriptContext.SetResult(returnValue.c_str());
}
static void PbGetRepeatedFieldCount(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto count = message->GetRepeatedFieldCount(fieldName);
if (count == -1)
{
return scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
scriptContext.SetResult(count);
}
static void PbHasField(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
scriptContext.SetResult(message->HasField(fieldName));
}
static void PbSetInt(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<int>(2);
auto index = scriptContext.GetArgument<int>(3);
if (index < 0)
{
if (!message->SetInt32OrUnsignedOrEnum(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
else
{
if (!message->SetRepeatedInt32OrUnsignedOrEnum(fieldName, index, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
}
static void PbSetInt64(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<int64>(2);
auto index = scriptContext.GetArgument<int>(3);
if (index < 0)
{
if (!message->SetInt64OrUnsigned(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
else
{
if (!message->SetRepeatedInt64OrUnsigned(fieldName, index, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
}
static void PbSetFloat(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<float>(2);
auto index = scriptContext.GetArgument<int>(3);
if (index < 0)
{
if (!message->SetFloatOrDouble(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
else
{
if (!message->SetRepeatedFloatOrDouble(fieldName, index, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
}
static void PbSetBool(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<bool>(2);
auto index = scriptContext.GetArgument<int>(3);
if (index < 0)
{
if (!message->SetBool(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
else
{
if (!message->SetRepeatedBool(fieldName, index, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
}
static void PbSetString(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = strdup(std::string(scriptContext.GetArgument<const char*>(2)).c_str());
auto index = scriptContext.GetArgument<int>(3);
if (index < 0)
{
if (!message->SetString(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
else
{
if (!message->SetRepeatedString(fieldName, index, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
}
static void PbAddInt(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<int>(2);
if (!message->AddInt32OrUnsignedOrEnum(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
static void PbAddInt64(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<int64>(2);
if (!message->AddInt64OrUnsigned(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
static void PbAddFloat(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<float>(2);
if (!message->AddFloatOrDouble(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
static void PbAddBool(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<bool>(2);
if (!message->AddBool(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
static void PbAddString(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto value = scriptContext.GetArgument<const char*>(2);
if (!message->AddString(fieldName, value))
{
scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
static void PbRemoveRepeatedFieldValue(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
GET_FIELD_NAME_OR_ERR();
auto index = scriptContext.GetArgument<int>(2);
if (!message->RemoveRepeatedFieldValue(fieldName, index))
{
scriptContext.ThrowNativeError("Invalid field \"%s\"[%d] for message \"%s\"", fieldName, index,
message->GetProtobufMessage()->GetTypeName().c_str());
}
}
// static void PbReadMessage(ScriptContext& scriptContext)
//{
// GET_MESSAGE_OR_ERR();
// GET_FIELD_NAME_OR_ERR();
//
// google::protobuf::Message* subMessage = nullptr;
//
// if (!message->GetMessage(fieldName, &subMessage))
// {
// scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
// message->GetProtobufMessage()->GetTypeName().c_str());
// return;
// }
//
// auto subUserMessage = new UserMessage(subMessage);
// managed_usermessages.push_back(subUserMessage);
// scriptContext.SetResult(subUserMessage);
// }
//
// static void PbReadRepeatedMessage(ScriptContext& scriptContext)
//{
// GET_MESSAGE_OR_ERR();
// GET_FIELD_NAME_OR_ERR();
//
// const google::protobuf::Message* subMessage = nullptr;
//
// auto index = scriptContext.GetArgument<int>(2);
//
// if (!message->GetRepeatedMessage(fieldName, index, &subMessage))
// {
// scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
// message->GetProtobufMessage()->GetTypeName().c_str());
// return;
// }
//
// auto subUserMessage = new UserMessage(const_cast<google::protobuf::Message*>(subMessage));
// managed_usermessages.push_back(subUserMessage);
// scriptContext.SetResult(subUserMessage);
// }
//
// static void PbAddMessage(ScriptContext& scriptContext)
//{
// GET_MESSAGE_OR_ERR();
// GET_FIELD_NAME_OR_ERR();
//
// google::protobuf::Message* subMessage;
//
// if (!message->AddMessage(fieldName, &subMessage))
// {
// scriptContext.ThrowNativeError("Invalid field \"%s\" for message \"%s\"", fieldName,
// message->GetProtobufMessage()->GetTypeName().c_str());
// return;
// }
//
// auto subUserMessage = new UserMessage(subMessage);
// managed_usermessages.push_back(subUserMessage);
// scriptContext.SetResult(subUserMessage);
// }
static void PbGetDebugString(ScriptContext& scriptContext)
{
GET_MESSAGE_OR_ERR();
scriptContext.SetResult(message->GetDebugString().c_str());
}
static void UserMessageFindMessageIdByName(ScriptContext& scriptContext)
{
auto messageName = scriptContext.GetArgument<const char*>(0);
auto message = globals::networkMessages->FindNetworkMessagePartial(messageName);
if (message == nullptr)
{
scriptContext.ThrowNativeError("Could not find user message: %s", messageName);
return;
}
scriptContext.SetResult(message->GetNetMessageInfo()->m_MessageId);
}
static void UserMessageCreate(ScriptContext& scriptContext)
{
auto messageName = scriptContext.GetArgument<const char*>(0);
auto message = new UserMessage(messageName);
if (message->GetSerializableMessage() == nullptr)
{
scriptContext.ThrowNativeError("Failed to create user message: %s", messageName);
return;
}
managed_usermessages.push_back(message);
scriptContext.SetResult(message);
}
static void UserMessageCreateById(ScriptContext& scriptContext)
{
auto messageId = scriptContext.GetArgument<int>(0);
auto message = new UserMessage(messageId);
if (message->GetSerializableMessage() == nullptr)
{
scriptContext.ThrowNativeError("Failed to create user message: %d", messageId);
return;
}
managed_usermessages.push_back(message);
scriptContext.SetResult(message);
}
static void UserMessageGetRecipients(ScriptContext& scriptContext)
{
auto message = scriptContext.GetArgument<UserMessage*>(0);
if (message == nullptr)
{
scriptContext.ThrowNativeError("Invalid message");
return;
}
scriptContext.SetResult(message->GetRecipientMask() ? *message->GetRecipientMask() : 0);
}
static void UserMessageSetRecipients(ScriptContext& scriptContext)
{
auto message = scriptContext.GetArgument<UserMessage*>(0);
auto recipientMask = scriptContext.GetArgument<uint64>(1);
*message->GetRecipientMask() = recipientMask;
}
static void UserMessageSend(ScriptContext& scriptContext)
{
auto message = scriptContext.GetArgument<UserMessage*>(0);
CRecipientFilter filter{};
filter.AddRecipientsFromMask(message->GetRecipientMask() ? *message->GetRecipientMask() : 0);
globals::gameEventSystem->PostEventAbstract(0, false, &filter, message->GetSerializableMessage(), message->GetProtobufMessage(), 0);
}
static void UserMessageDelete(ScriptContext& scriptContext)
{
auto message = scriptContext.GetArgument<UserMessage*>(0);
auto it = std::find(managed_usermessages.begin(), managed_usermessages.end(), message);
if (it != managed_usermessages.end())
{
managed_usermessages.erase(it);
delete message;
}
}
static void UserMessageGetMessageId(ScriptContext& scriptContext)
{
auto message = scriptContext.GetArgument<UserMessage*>(0);
if (message == nullptr || message->GetSerializableMessage() == nullptr)
{
scriptContext.ThrowNativeError("Invalid message");
return;
}
scriptContext.SetResult(message->GetMessageID());
}
static void UserMessageGetMessageName(ScriptContext& scriptContext)
{
auto message = scriptContext.GetArgument<UserMessage*>(0);
if (message == nullptr || message->GetSerializableMessage() == nullptr)
{
scriptContext.ThrowNativeError("Invalid message");
return;
}
scriptContext.SetResult(message->GetSerializableMessage()->GetUnscopedName());
}
static void UserMessageGetMessageTypeName(ScriptContext& scriptContext)
{
auto message = scriptContext.GetArgument<UserMessage*>(0);
if (message == nullptr || message->GetProtobufMessage() == nullptr)
{
scriptContext.ThrowNativeError("Invalid message");
return;
}
scriptContext.SetResult(message->GetProtobufMessage()->GetTypeName().c_str());
}
REGISTER_NATIVES(usermessages, {
ScriptEngine::RegisterNativeHandler("HOOK_USERMESSAGE", HookUserMessage);
ScriptEngine::RegisterNativeHandler("UNHOOK_USERMESSAGE", UnhookUserMessage);
ScriptEngine::RegisterNativeHandler("PB_HASFIELD", PbHasField);
ScriptEngine::RegisterNativeHandler("PB_READINT", PbReadInt);
ScriptEngine::RegisterNativeHandler("PB_READINT64", PbReadInt64);
ScriptEngine::RegisterNativeHandler("PB_READFLOAT", PbReadFloat);
ScriptEngine::RegisterNativeHandler("PB_READBOOL", PbReadBool);
ScriptEngine::RegisterNativeHandler("PB_READSTRING", PbReadString);
ScriptEngine::RegisterNativeHandler("PB_GETREPEATEDFIELDCOUNT", PbGetRepeatedFieldCount);
ScriptEngine::RegisterNativeHandler("PB_SETINT", PbSetInt);
ScriptEngine::RegisterNativeHandler("PB_SETINT64", PbSetInt64);
ScriptEngine::RegisterNativeHandler("PB_SETFLOAT", PbSetFloat);
ScriptEngine::RegisterNativeHandler("PB_SETBOOL", PbSetBool);
ScriptEngine::RegisterNativeHandler("PB_SETSTRING", PbSetString);
ScriptEngine::RegisterNativeHandler("PB_ADDINT", PbAddInt);
ScriptEngine::RegisterNativeHandler("PB_ADDINT64", PbAddInt64);
ScriptEngine::RegisterNativeHandler("PB_ADDFLOAT", PbAddFloat);
ScriptEngine::RegisterNativeHandler("PB_ADDBOOL", PbAddBool);
ScriptEngine::RegisterNativeHandler("PB_ADDSTRING", PbAddString);
ScriptEngine::RegisterNativeHandler("PB_REMOVEREPEATEDFIELDVALUE", PbRemoveRepeatedFieldValue);
// ScriptEngine::RegisterNativeHandler("PB_READMESSAGE", PbReadMessage);
// ScriptEngine::RegisterNativeHandler("PB_READREPEATEDMESSAGE", PbReadRepeatedMessage);
// ScriptEngine::RegisterNativeHandler("PB_ADDMESSAGE", PbAddMessage);
ScriptEngine::RegisterNativeHandler("PB_GETDEBUGSTRING", PbGetDebugString);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_FINDMESSAGEIDBYNAME", UserMessageFindMessageIdByName);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_CREATE", UserMessageCreate);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_CREATEBYID", UserMessageCreateById);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_GETRECIPIENTS", UserMessageGetRecipients);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_SETRECIPIENTS", UserMessageSetRecipients);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_SEND", UserMessageSend);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_DELETE", UserMessageDelete);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_GETID", UserMessageGetMessageId);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_GETNAME", UserMessageGetMessageName);
ScriptEngine::RegisterNativeHandler("USERMESSAGE_GETTYPE", UserMessageGetMessageTypeName);
})
} // namespace counterstrikesharp

View File

@@ -0,0 +1,34 @@
HOOK_USERMESSAGE: messageId:int, callback:func, mode:HookMode -> void
UNHOOK_USERMESSAGE: messageId:int, callback:func, mode:HookMode -> void
PB_HASFIELD: message:UserMessage, name:string -> bool
PB_READINT: message:UserMessage, name:string, index:int -> int
PB_READINT64: message:UserMessage, name:string, index:int -> long
PB_READFLOAT: message:UserMessage, name:string, index:int -> float
PB_READBOOL: message:UserMessage, name:string, index:int -> bool
PB_READSTRING: message:UserMessage, name:string, index:int -> string
PB_GETREPEATEDFIELDCOUNT: message:UserMessage, name:string -> int
PB_SETINT: message:UserMessage, name:string, value:int, index:int -> void
PB_SETINT64: message:UserMessage, name:string, value:long, index:int -> void
PB_SETFLOAT: message:UserMessage, name:string, value:float, index:int -> void
PB_SETBOOL: message:UserMessage, name:string, value:bool, index:int -> void
PB_SETSTRING: message:UserMessage, name:string, value:string, index:int -> void
PB_ADDINT: message:UserMessage, name:string, value:int -> void
PB_ADDINT64: message:UserMessage, name:string, value:int64 -> void
PB_ADDFLOAT: message:UserMessage, name:string, value:float -> void
PB_ADDBOOL: message:UserMessage, name:string, value:bool -> void
PB_ADDSTRING: message:UserMessage, name:string, value:string -> void
PB_REMOVEREPEATEDFIELDVALUE: message:UserMessage, name:string, index:int -> void
#PB_READMESSAGE: message:UserMessage, name:string -> UserMessage
#PB_READREPEATEDMESSAGE: message:UserMessage, name:string, index:int -> UserMessage
#PB_ADDMESSAGE: message:UserMessage, name:string -> UserMessage
PB_GETDEBUGSTRING: message:UserMessage -> string
USERMESSAGE_GETRECIPIENTS: message:UserMessage -> uint64
USERMESSAGE_SETRECIPIENTS: message:UserMessage, recipients:uint64 -> void
USERMESSAGE_FINDMESSAGEIDBYNAME: name:string -> int
USERMESSAGE_CREATE: name:string -> pointer
USERMESSAGE_CREATEBYID: id:int -> pointer
USERMESSAGE_SEND: message:UserMessage -> void
USERMESSAGE_DELETE: message:UserMessage -> void
USERMESSAGE_GETID: message:UserMessage -> int
USERMESSAGE_GETNAME: message:UserMessage -> string
USERMESSAGE_GETTYPE: message:UserMessage -> string

View File

@@ -54,6 +54,8 @@ public class Mapping
return "void";
case "uint64":
return "ulong";
case "long":
return "long";
case "func":
case "callback":
return "InputArgument";
@@ -69,6 +71,10 @@ public class Mapping
return "DataType";
case "CommandCallingContext":
return "CommandCallingContext";
case "UserMessage":
return "UserMessage";
case "RecipientFilter":
return "RecipientFilter";
case "any":
return "T";
}

View File

@@ -91,7 +91,9 @@ public partial class Generators
var result = $@"
using System;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.UserMessages;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Core
{{
@@ -106,4 +108,4 @@ namespace CounterStrikeSharp.API.Core
File.WriteAllText(Path.Join(Helpers.GetRootDirectory(), "managed/CounterStrikeSharp.API/Core/API.cs"),
result);
}
}
}