diff --git a/configs/addons/counterstrikesharp/gamedata/gamedata.json b/configs/addons/counterstrikesharp/gamedata/gamedata.json index 2cd4fd0e7..0d634c5ff 100644 --- a/configs/addons/counterstrikesharp/gamedata/gamedata.json +++ b/configs/addons/counterstrikesharp/gamedata/gamedata.json @@ -253,5 +253,11 @@ "windows": 2, "linux": 0 } + }, + "CheckTransmitPlayerSlot": { + "offsets": { + "windows": 584, + "linux": 584 + } } } diff --git a/examples/WithCheckTransmit/README.md b/examples/WithCheckTransmit/README.md new file mode 100644 index 000000000..9030eb5c8 --- /dev/null +++ b/examples/WithCheckTransmit/README.md @@ -0,0 +1,2 @@ +# With CheckTransmit +This example shows how to work with the `CheckTransmit` listener. diff --git a/examples/WithCheckTransmit/WithCheckTransmit.csproj b/examples/WithCheckTransmit/WithCheckTransmit.csproj new file mode 100644 index 000000000..32c0ee55a --- /dev/null +++ b/examples/WithCheckTransmit/WithCheckTransmit.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/examples/WithCheckTransmit/WithCheckTransmitPlugin.cs b/examples/WithCheckTransmit/WithCheckTransmitPlugin.cs new file mode 100644 index 000000000..9a88b1389 --- /dev/null +++ b/examples/WithCheckTransmit/WithCheckTransmitPlugin.cs @@ -0,0 +1,115 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Core.Attributes; + +namespace WithCheckTransmit; + +[MinimumApiVersion(276)] +public class WithCheckTransmitPlugin : BasePlugin +{ + public override string ModuleName => "Example: With CheckTransmit"; + public override string ModuleVersion => "1.0.0"; + public override string ModuleAuthor => "CounterStrikeSharp & Contributors"; + public override string ModuleDescription => "A simple plugin that uses the CheckTransmit listener!"; + + private Dictionary ShouldSeeDoors = new Dictionary(); + + public override void Load(bool hotReload) + { + // This command is related to the following example. + AddCommand("nodoors", "Toggle door transmit", (player, info) => + { + if (player == null) + return; + + if (ShouldSeeDoors.ContainsKey(player.Slot)) + { + ShouldSeeDoors[player.Slot] = !ShouldSeeDoors[player.Slot]; + } else + { + ShouldSeeDoors.Add(player.Slot, false); + } + + info.ReplyToCommand($"You should {(ShouldSeeDoors[player.Slot] ? "see" : "not see")} doors"); + }); + + // In this example, we will hide every door for players that have enabled the option with the command 'nodoors' + RegisterListener((CCheckTransmitInfoList infoList) => + { + // Get the list of the currently available doors (prop_door_rotating) + IEnumerable doors = Utilities.FindAllEntitiesByDesignerName("prop_door_rotating"); + + // Do nothing if there is none. + if (!doors.Any()) + return; + + // Go through every received info + foreach ((CCheckTransmitInfo info, CCSPlayerController? player) in infoList) + { + // If no player is found, we can continue + if (player == null) + continue; + + // Otherwise, lets do the work: + + // Check if we should clear or not: + + // If we have no data saved for this player, then we should not continue + if (!ShouldSeeDoors.ContainsKey(player.Slot)) + continue; + + // If this value is true, then this player should see doors + if (ShouldSeeDoors[player.Slot]) + continue; + + // Otherwise, lets remove the door entity indexes from the info list so they won't be transmitted + foreach (CPropDoorRotating door in doors) + { + info.TransmitEntities.Remove(door); + } + + // NOTE: this is a barebone example, saving data and doing sanity checks is up to you. + } + }); + + // In this example, we will hide other players in the same team as the player. + // NOTE: 'Hiding' players requires extra work to do, killing non-transmitted players results in crash. + RegisterListener((CCheckTransmitInfoList infoList) => + { + // Get the list of the current players, we only work with this value later on + List players = Utilities.GetPlayers(); + + // Go through every received info + foreach ((CCheckTransmitInfo info, CCSPlayerController? player) in infoList) + { + // If no player is found, we can continue + if (player == null) + continue; + + // Otherwise, lets do the work: + + // as an example, lets hide everyone for this player who is in the same team. + IEnumerable targetPlayers = players.Where(p => + // is the player and its pawn valid + p.IsValid && p.Pawn.IsValid && + + // we shouldn't hide ourselves + p.Slot != player.Slot && + + // is the player is in the same team + p.Team == player.Team && + + // is alive + p.PlayerPawn.Value?.LifeState == (byte)LifeState_t.LIFE_ALIVE + ); + + foreach (CCSPlayerController targetPlayer in targetPlayers) + { + // Calling 'Remove' will clear the entity index of the target player pawn from the transmission list + // so it won't be transmitted for the 'player'. + info.TransmitEntities.Remove(targetPlayer.Pawn); + } + } + }); + } +} diff --git a/managed/CounterStrikeSharp.API/Core/Listeners.g.cs b/managed/CounterStrikeSharp.API/Core/Listeners.g.cs index 6e2d24e4a..b362834ab 100644 --- a/managed/CounterStrikeSharp.API/Core/Listeners.g.cs +++ b/managed/CounterStrikeSharp.API/Core/Listeners.g.cs @@ -163,5 +163,12 @@ public class Listeners { /// Resource Manifest [ListenerName("OnServerPrecacheResources")] public delegate void OnServerPrecacheResources(ResourceManifest manifest); + + /// + /// Called when checking transmit on entities. + /// + /// Transmit info list + [ListenerName("CheckTransmit")] + public delegate void CheckTransmit([CastFrom(typeof(nint))]CCheckTransmitInfoList infoList); } -} \ No newline at end of file +} diff --git a/managed/CounterStrikeSharp.API/Core/Model/CCheckTransmitInfo.cs b/managed/CounterStrikeSharp.API/Core/Model/CCheckTransmitInfo.cs new file mode 100644 index 000000000..04a9e80b6 --- /dev/null +++ b/managed/CounterStrikeSharp.API/Core/Model/CCheckTransmitInfo.cs @@ -0,0 +1,106 @@ +/* + * 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 . * + */ + +using System.Collections; +using System.Runtime.InteropServices; + +namespace CounterStrikeSharp.API.Core +{ + [StructLayout(LayoutKind.Explicit)] + public struct CCheckTransmitInfo + { + /// + /// Entity n is already marked for transmission + /// + [FieldOffset(0x0)] + public CFixedBitVecBase TransmitEntities; + + /// + /// Entity n is always send even if not in PVS (HLTV and Replay only) + /// + [FieldOffset(0x8)] + public CFixedBitVecBase TransmitAlways; + }; + + public sealed class CCheckTransmitInfoList : NativeObject, IReadOnlyList<(CCheckTransmitInfo, CCSPlayerController?)> + { + private int CheckTransmitPlayerSlotOffset = GameData.GetOffset("CheckTransmitPlayerSlot"); + + private unsafe nint* Inner => (nint*)base.Handle; + + public unsafe int Count { get => (int)(*(this.Inner + 1)); } + + public unsafe CCheckTransmitInfoList(IntPtr pointer) : base(pointer) + { } + + /// + /// Get transmit info for the given index. + /// + /// Index of the info you want to retrieve from the list, should be between 0 and '' - 1 + /// + public (CCheckTransmitInfo, CCSPlayerController?) this[int index] + { + get + { + var (transmit, slot) = this.Get(index); + CCSPlayerController? player = Utilities.GetPlayerFromSlot(slot); + return (transmit, player); + } + } + + /// + /// Get transmit info for the given index. + /// + /// Index of the info you want to retrieve from the list, should be between 0 and '' - 1 + /// + private unsafe (CCheckTransmitInfo, int) Get(int index) + { + if (index < 0 || index >= this.Count) + { + throw new ArgumentOutOfRangeException("index"); + } + + // 'base.Handle' holds the pointer for our 'CCheckTransmitInfoList' wrapper class + + // Get the pointer to the array of 'CCheckTransmitInfo' + nint* infoListPtr = *(nint**)this.Inner; // Dereference 'Inner' to get the pointer to the array + + // Access the specific 'CCheckTransmitInfo*' + nint infoPtr = *(infoListPtr + index); + + // Retrieve the 'CCheckTransmitInfo' from the pointer + CCheckTransmitInfo info = Marshal.PtrToStructure(infoPtr); + + // Get player slot from the 'infoPtr' using the 'CheckTransmitPlayerSlotOffset' offset + int playerSlot = *(int*)((byte*)infoPtr + CheckTransmitPlayerSlotOffset); + + return (info, playerSlot); + } + + public IEnumerator<(CCheckTransmitInfo, CCSPlayerController?)> GetEnumerator() + { + for (int i = 0; i < this.Count; i++) + { + yield return this[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} diff --git a/managed/CounterStrikeSharp.API/Core/Model/CFixedBitVecBase.cs b/managed/CounterStrikeSharp.API/Core/Model/CFixedBitVecBase.cs new file mode 100644 index 000000000..55ef6ec0e --- /dev/null +++ b/managed/CounterStrikeSharp.API/Core/Model/CFixedBitVecBase.cs @@ -0,0 +1,81 @@ +/* + * 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 . * + */ + +using System.Runtime.InteropServices; + +namespace CounterStrikeSharp.API.Core +{ + // credits: qstage + [StructLayout(LayoutKind.Sequential)] + public unsafe struct CFixedBitVecBase + { + private const int LOG2_BITS_PER_INT = 5; + private const int BITS_PER_INT = 32; + + private readonly uint* m_Ints; + + public void Add(CEntityInstance entityInstance) => Write(entityInstance.Index); + + public void Add(int bitNum) => Write(bitNum); + + public void Add(uint bitNum) => Write(bitNum); + + public void Remove(CEntityInstance entityInstance) => Clear(entityInstance.Index); + + public void Remove(int bitNum) => Clear(bitNum); + + public void Remove(uint bitNum) => Clear(bitNum); + + public bool Contains(CEntityInstance entityInstance) => Contains(entityInstance.Index); + + public bool Contains(uint bitNum) => Contains((int)bitNum); + + private void Clear(uint bitNum) => Clear((int)bitNum); + + private void Write(uint bitNum) => Write((int)bitNum); + + private void Clear(int bitNum) + { + if (!(bitNum >= 0 && bitNum < Utilities.MaxEdicts)) + return; + + uint* pInt = m_Ints + BitVec_Int(bitNum); + *pInt &= ~(uint)BitVec_Bit(bitNum); + } + + private void Write(int bitNum) + { + if (!(bitNum >= 0 && bitNum < Utilities.MaxEdicts)) + return; + + uint* pInt = m_Ints + BitVec_Int(bitNum); + *pInt |= (uint)BitVec_Bit(bitNum); + } + + public bool Contains(int bitNum) + { + if (!(bitNum >= 0 && bitNum < Utilities.MaxEdicts)) + return false; + + uint* pInt = m_Ints + BitVec_Int(bitNum); + return (*pInt & (uint)BitVec_Bit(bitNum)) != 0; + } + + private int BitVec_Int(int bitNum) => bitNum >> LOG2_BITS_PER_INT; + + private int BitVec_Bit(int bitNum) => 1 << (bitNum & (BITS_PER_INT - 1)); + } +} diff --git a/managed/CounterStrikeSharp.sln b/managed/CounterStrikeSharp.sln index e0d6f5040..82ccb2078 100644 --- a/managed/CounterStrikeSharp.sln +++ b/managed/CounterStrikeSharp.sln @@ -1,5 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35309.182 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{57E64289-5D69-4AA1-BEF0-D0D96A55EE8F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CounterStrikeSharp.API", "CounterStrikeSharp.API\CounterStrikeSharp.API.csproj", "{55B47E41-61AA-4D75-9069-CB14328107B7}" @@ -42,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithSharedTypesConsumer", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithUserMessages", "..\examples\WithUserMessages\WithUserMessages.csproj", "{A14029BA-CADE-4F25-ADC5-48CF14332F61}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithCheckTransmit", "..\examples\WithCheckTransmit\WithCheckTransmit.csproj", "{854B06B5-0E7B-438A-BA51-3F299557F884}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -108,14 +113,14 @@ Global {6FA3107D-42AF-42A0-BF51-2230D13268B5}.Debug|Any CPU.Build.0 = Debug|Any CPU {6FA3107D-42AF-42A0-BF51-2230D13268B5}.Release|Any CPU.ActiveCfg = Release|Any CPU {6FA3107D-42AF-42A0-BF51-2230D13268B5}.Release|Any CPU.Build.0 = Release|Any CPU - {4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.Build.0 = Release|Any CPU {1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {1309954E-FAF7-47A5-9FF9-C7263B33E4E3}.Release|Any CPU.Build.0 = Release|Any CPU + {4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E5289B5-E81D-421C-B340-B98B6FFE09D1}.Release|Any CPU.Build.0 = Release|Any CPU {A37676EA-CF2F-424D-85A1-C359D07A679D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A37676EA-CF2F-424D-85A1-C359D07A679D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A37676EA-CF2F-424D-85A1-C359D07A679D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -128,6 +133,13 @@ Global {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 + {854B06B5-0E7B-438A-BA51-3F299557F884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {854B06B5-0E7B-438A-BA51-3F299557F884}.Debug|Any CPU.Build.0 = Debug|Any CPU + {854B06B5-0E7B-438A-BA51-3F299557F884}.Release|Any CPU.ActiveCfg = Release|Any CPU + {854B06B5-0E7B-438A-BA51-3F299557F884}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {57E64289-5D69-4AA1-BEF0-D0D96A55EE8F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A} @@ -141,10 +153,14 @@ Global {31EABE0B-871F-497B-BF36-37FFC6FAD15F} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A} {BB44E08E-CCA8-4E22-A132-11B2F69D1890} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A} {6FA3107D-42AF-42A0-BF51-2230D13268B5} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A} - {4E5289B5-E81D-421C-B340-B98B6FFE09D1} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A} {1309954E-FAF7-47A5-9FF9-C7263B33E4E3} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A} + {4E5289B5-E81D-421C-B340-B98B6FFE09D1} = {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} + {854B06B5-0E7B-438A-BA51-3F299557F884} = {7DF99C35-881D-4FF2-B1C9-246BD3DECB9A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {069C4CD4-BACA-446A-A6B8-0194E4F75355} EndGlobalSection EndGlobal diff --git a/managed/TestPlugin/TestPlugin.cs b/managed/TestPlugin/TestPlugin.cs index a4640c586..66982f9aa 100644 --- a/managed/TestPlugin/TestPlugin.cs +++ b/managed/TestPlugin/TestPlugin.cs @@ -15,6 +15,7 @@ */ using System; +using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; @@ -301,6 +302,26 @@ private void SetupListeners() return; } }); + + // Hide every door (prop_door_rotating) for everyone as a test + RegisterListener((CCheckTransmitInfoList infoList) => + { + IEnumerable doors = Utilities.FindAllEntitiesByDesignerName("prop_door_rotating"); + + if (!doors.Any()) + return; + + foreach ((CCheckTransmitInfo info, CCSPlayerController? player) in infoList) + { + if (player == null) + continue; + + foreach (CPropDoorRotating door in doors) + { + info.TransmitEntities.Remove(door); + } + } + }); } private void SetupCommands() diff --git a/src/core/globals.cpp b/src/core/globals.cpp index bd0cecfff..941752c06 100644 --- a/src/core/globals.cpp +++ b/src/core/globals.cpp @@ -78,6 +78,7 @@ ISmmAPI* ismm = nullptr; CGameEntitySystem* entitySystem = nullptr; CCoreConfig* coreConfig = nullptr; CGameConfig* gameConfig = nullptr; +ISource2GameEntities* gameEntities = nullptr; // Custom Managers CallbackManager callbackManager; diff --git a/src/core/globals.h b/src/core/globals.h index 531a340da..b834d9770 100644 --- a/src/core/globals.h +++ b/src/core/globals.h @@ -94,6 +94,7 @@ extern ISource2Server* server; extern CGlobalEntityList* globalEntityList; extern EntityListener entityListener; extern CGameEntitySystem* entitySystem; +extern ISource2GameEntities* gameEntities; extern EventManager eventManager; extern UserMessageManager userMessageManager; diff --git a/src/core/managers/entity_manager.cpp b/src/core/managers/entity_manager.cpp index d27ada326..8f5d4252a 100644 --- a/src/core/managers/entity_manager.cpp +++ b/src/core/managers/entity_manager.cpp @@ -24,14 +24,21 @@ #include #include "scripting/callback_manager.h" +SH_DECL_HOOK7_void(ISource2GameEntities, CheckTransmit, SH_NOATTRIB, 0, CCheckTransmitInfo**, int, CBitVec<16384>&, const Entity2Networkable_t**, const uint16*, int, bool); + namespace counterstrikesharp { EntityManager::EntityManager() {} EntityManager::~EntityManager() {} +CCheckTransmitInfoList::CCheckTransmitInfoList(CCheckTransmitInfo** pInfoInfoList, int nInfoCount) : infoList(pInfoInfoList), infoCount(nInfoCount) {} + void EntityManager::OnAllInitialized() { + SH_ADD_HOOK_MEMFUNC(ISource2GameEntities, CheckTransmit, globals::gameEntities, this, &EntityManager::CheckTransmit, true); + + check_transmit = globals::callbackManager.CreateCallback("CheckTransmit"); on_entity_spawned_callback = globals::callbackManager.CreateCallback("OnEntitySpawned"); on_entity_created_callback = globals::callbackManager.CreateCallback("OnEntityCreated"); on_entity_deleted_callback = globals::callbackManager.CreateCallback("OnEntityDeleted"); @@ -75,7 +82,10 @@ void EntityManager::OnShutdown() globals::callbackManager.ReleaseCallback(on_entity_created_callback); globals::callbackManager.ReleaseCallback(on_entity_deleted_callback); globals::callbackManager.ReleaseCallback(on_entity_parent_changed_callback); + globals::callbackManager.ReleaseCallback(check_transmit); globals::entitySystem->RemoveListenerEntity(&entityListener); + + SH_REMOVE_HOOK_MEMFUNC(ISource2GameEntities, CheckTransmit, globals::gameEntities, this, &EntityManager::CheckTransmit, true); } void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity) @@ -156,6 +166,21 @@ void EntityManager::UnhookEntityOutput(const char* szClassname, const char* szOu } } +void EntityManager::CheckTransmit(CCheckTransmitInfo** pInfoInfoList, int nInfoCount, CBitVec<16384>& unionTransmitEdicts, const Entity2Networkable_t** pNetworkables, const uint16* pEntityIndicies, int nEntityIndices, bool bEnablePVSBits) +{ + auto callback = globals::entityManager.check_transmit; + + if (callback && callback->GetFunctionCount()) { + CCheckTransmitInfoList* infoList = new CCheckTransmitInfoList(pInfoInfoList, nInfoCount); + + callback->ScriptContext().Reset(); + callback->ScriptContext().Push(infoList); + callback->Execute(); + + delete infoList; + } +} + void DetourFireOutputInternal(CEntityIOOutput* const pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* const value, float flDelay) { diff --git a/src/core/managers/entity_manager.h b/src/core/managers/entity_manager.h index 4aaeebc20..793ac895a 100644 --- a/src/core/managers/entity_manager.h +++ b/src/core/managers/entity_manager.h @@ -39,6 +39,14 @@ class CEntityListener : public IEntityListener { void OnEntityParentChanged(CEntityInstance *pEntity, CEntityInstance *pNewParent) override; }; +class CCheckTransmitInfoList { +public: + CCheckTransmitInfoList(CCheckTransmitInfo** pInfoInfoList, int nInfoCount); +private: + CCheckTransmitInfo** infoList; + int infoCount; +}; + class EntityManager : public GlobalClass { friend CEntityListener; public: @@ -51,10 +59,13 @@ class EntityManager : public GlobalClass { CEntityListener entityListener; std::map m_pHookMap; private: + void CheckTransmit(CCheckTransmitInfo** pInfoInfoList, int nInfoCount, CBitVec<16384>& unionTransmitEdicts, const Entity2Networkable_t** pNetworkables, const uint16* pEntityIndicies, int nEntityIndices, bool bEnablePVSBits); + ScriptCallback *on_entity_spawned_callback; ScriptCallback *on_entity_created_callback; ScriptCallback *on_entity_deleted_callback; ScriptCallback *on_entity_parent_changed_callback; + ScriptCallback *check_transmit; }; diff --git a/src/mm_plugin.cpp b/src/mm_plugin.cpp index f7ae54ea0..ca8bbdb44 100644 --- a/src/mm_plugin.cpp +++ b/src/mm_plugin.cpp @@ -102,6 +102,7 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s 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); + GET_V_IFACE_ANY(GetServerFactory, globals::gameEntities, ISource2GameEntities, SOURCE2GAMEENTITIES_INTERFACE_VERSION); auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core"); globals::coreConfig = new CCoreConfig(coreconfig_path); diff --git a/src/scripting/listeners/entities.yaml b/src/scripting/listeners/entities.yaml index e31122698..94907b103 100644 --- a/src/scripting/listeners/entities.yaml +++ b/src/scripting/listeners/entities.yaml @@ -1,4 +1,5 @@ OnEntitySpawned: entity:pointer OnEntityCreated: entity:pointer OnEntityDeleted: entity:pointer -OnEntityParentChanged: entity:pointer, newParent:pointer \ No newline at end of file +OnEntityParentChanged: entity:pointer, newParent:pointer +CheckTransmit: infoList:pointer