diff --git a/addons/source-python/packages/source-python/entities/_base.py b/addons/source-python/packages/source-python/entities/_base.py
index e2bb09c92..8b27ad37d 100755
--- a/addons/source-python/packages/source-python/entities/_base.py
+++ b/addons/source-python/packages/source-python/entities/_base.py
@@ -68,6 +68,7 @@
# Source.Python Imports
# Entities
from _entities._entity import BaseEntity
+from _entities._transmit import transmit_manager
# =============================================================================
@@ -842,6 +843,71 @@ def set_parent(self, parent, attachment=INVALID_ATTACHMENT_INDEX):
return [parent, attachment]
+ # =========================================================================
+ # >> ENTITY TRANSMIT FUNCTIONALITY
+ # =========================================================================
+ def hide(self):
+ """Hide the entity from all players."""
+ transmit_manager.hide(self.index)
+
+ def hide_from(self, player_index):
+ """Hide the entity from player.
+
+ :param int player_index:
+ The target player index to hide this entity.
+ """
+ transmit_manager.hide_from(self.index, player_index)
+
+ def show(self):
+ """Show the entity to all players."""
+ transmit_manager.show(self.index)
+
+ def show_from(self, player_index):
+ """Show the entity to player.
+
+ :param int player_index:
+ The target player index to show this entity.
+ """
+ transmit_manager.show_from(self.index, player_index)
+
+ def reset(self):
+ """Reset the entity's hidden/shown state."""
+ transmit_manager.reset(self.index)
+
+ def reset_from(self, player_index):
+ """Reset the player's entity hidden/shown state.
+
+ :param int player_index:
+ The target player index to reset the player's hidden/shown state.
+ """
+ transmit_manager.reset_from(self.index, player_index)
+
+ def is_hidden(self):
+ """Return True if the entity is hidden from any player.
+
+ :rtype: bool
+ """
+ return transmit_manager.is_hidden(self.index)
+
+ def is_hidden_from(self, player_index):
+ """Return True if the entity is hidden from the player.
+
+ :param int player_index:
+ The target player index to check if the entity is hidden.
+ :rtype: bool
+ """
+ return transmit_manager.is_hidden_from(self.index, player_index)
+
+ def get_hidden_player(self):
+ """Get the players where the entity is hidden.
+
+ :return:
+ A tuple containing :class:`players.entity.Player` instances
+ in which the entity is hidden.
+ :rtype: tuple
+ """
+ return transmit_manager.get_hidden_player(self.index)
+
# =============================================================================
# >> LISTENERS
@@ -873,3 +939,6 @@ def _on_networked_entity_deleted(index):
# Invalidate the internal entity caches for this entity
for cls in _entity_classes:
cls.cache.pop(index, None)
+
+ # Reset the entity's hidden state.
+ transmit_manager.reset(index)
diff --git a/addons/source-python/packages/source-python/entities/transmit.py b/addons/source-python/packages/source-python/entities/transmit.py
new file mode 100755
index 000000000..07e6e989f
--- /dev/null
+++ b/addons/source-python/packages/source-python/entities/transmit.py
@@ -0,0 +1,29 @@
+# ../entities/transmit.py
+
+"""Provides entity transmission filtering."""
+
+
+# ============================================================================
+# >> FORWARD IMPORTS
+# ============================================================================
+# Source.Python
+# Entities
+from _entities._transmit import transmit_manager
+
+
+# ============================================================================
+# >> ALL DECLARATION
+# ============================================================================
+__all__ = (
+ 'transmit_manager',
+ 'reset_hidden_state',
+)
+
+
+# =============================================================================
+# >> FUNCTIONS
+# =============================================================================
+def reset_hidden_state():
+ """Reset all entities' hidden/shown state."""
+ transmit_manager.reset_all()
+
diff --git a/addons/source-python/packages/source-python/players/_base.py b/addons/source-python/packages/source-python/players/_base.py
index bc20a0b0f..65010f23c 100755
--- a/addons/source-python/packages/source-python/players/_base.py
+++ b/addons/source-python/packages/source-python/players/_base.py
@@ -77,6 +77,14 @@
from auth.manager import auth_manager
+# =============================================================================
+# >> FORWARD IMPORTS
+# =============================================================================
+# Source.Python Imports
+# Entities
+from _entities._transmit import transmit_manager
+
+
# =============================================================================
# >> CLASSES
# =============================================================================
@@ -1004,6 +1012,56 @@ def drop_weapon(self, weapon, target=None, velocity=None):
"""
return [weapon, target, velocity]
+ # =========================================================================
+ # >> PLAYER TRANSMIT FUNCTIONALITY
+ # =========================================================================
+ def hide_entity(self, entity_index):
+ """Hide the entity from player.
+
+ :param int entity_index:
+ The target entity index to hide from the player.
+ """
+ transmit_manager.hide_from(entity_index, self.index)
+
+ def show_entity(self, entity_index):
+ """Show the entity to player.
+
+ :param int entity_index:
+ The target entity index to show the entity to player.
+ """
+ transmit_manager.show_from(entity_index, self.index)
+
+ def reset_entity(self, entity_index):
+ """Reset the player's entity hidden/shown state.
+
+ :param int entity_index:
+ The target entity_index to reset the player's hidden/shown state.
+ """
+ transmit_manager.reset_from(entity_index, self.index)
+
+ def reset_all_entity(self):
+ """Reset the player's hidden/shown state on all entities."""
+ transmit_manager.reset_player(self.index)
+
+ def is_entity_hidden(self, entity_index):
+ """Return True if the entity is hidden from the player.
+
+ :param int entity_index:
+ The target entity index to check if the entity is hidden.
+ :rtype: bool
+ """
+ return transmit_manager.is_hidden_from(entity_index, self.index)
+
+ def get_hidden_entity(self):
+ """Get the entities that are hidden from the player.
+
+ :return:
+ A tuple containing :class:`entities.entity.Entity` instances
+ that are hidden from the player.
+ :rtype: tuple
+ """
+ return transmit_manager.get_hidden_entity(self.index)
+
# =============================================================================
# >> HELPER FUNCTIONS
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9bb21297a..12894d8ac 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -225,6 +225,7 @@ Set(SOURCEPYTHON_ENTITIES_MODULE_HEADERS
core/modules/entities/${SOURCE_ENGINE}/entities_props_wrap.h
core/modules/entities/${SOURCE_ENGINE}/entities_constants_wrap.h
core/modules/entities/entities_entity.h
+ core/modules/entities/entities_transmit.h
)
Set(SOURCEPYTHON_ENTITIES_MODULE_SOURCES
@@ -241,6 +242,8 @@ Set(SOURCEPYTHON_ENTITIES_MODULE_SOURCES
core/modules/entities/entities_props_wrap.cpp
core/modules/entities/entities_entity.cpp
core/modules/entities/entities_entity_wrap.cpp
+ core/modules/entities/entities_transmit.cpp
+ core/modules/entities/entities_transmit_wrap.cpp
)
# ------------------------------------------------------------------
diff --git a/src/core/modules/entities/entities_transmit.cpp b/src/core/modules/entities/entities_transmit.cpp
new file mode 100755
index 000000000..dceb6a8e8
--- /dev/null
+++ b/src/core/modules/entities/entities_transmit.cpp
@@ -0,0 +1,438 @@
+/**
+* =============================================================================
+* Source Python
+* Copyright (C) 2012-2020 Source Python Development Team. All rights reserved.
+* =============================================================================
+*
+* This program is free software; you can redistribute it and/or modify it under
+* the terms of the GNU General Public License, version 3.0, as published by the
+* Free Software Foundation.
+*
+* This program 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
+* this program. If not, see .
+*
+* As a special exception, the Source Python Team gives you permission
+* to link the code of this program (as well as its derivative works) to
+* "Half-Life 2," the "Source Engine," and any Game MODs that run on software
+* by the Valve Corporation. You must obey the GNU General Public License in
+* all respects for all other code used. Additionally, the Source.Python
+* Development Team grants this exception to all derivative works.
+*/
+
+//-----------------------------------------------------------------------------
+// Includes.
+//-----------------------------------------------------------------------------
+#include "eiface.h"
+#include "strtools.h"
+
+#include "utilities/conversions.h"
+#include "modules/entities/entities_transmit.h"
+#include "modules/memory/memory_pointer.h"
+#include "modules/memory/memory_function_info.h"
+
+
+//-----------------------------------------------------------------------------
+// Eternal variables.
+//-----------------------------------------------------------------------------
+extern IServerGameEnts *gameents;
+extern CGlobalVars *gpGlobals;
+extern IPlayerInfoManager *playerinfomanager;
+
+
+//-----------------------------------------------------------------------------
+// Static variables.
+//-----------------------------------------------------------------------------
+CTransmitManager* CTransmitManager::instance = nullptr;
+
+
+//-----------------------------------------------------------------------------
+// CTransmitManager class.
+//-----------------------------------------------------------------------------
+CTransmitManager::CTransmitManager()
+ :m_arrayFilterIndexes()
+{
+ CFunctionInfo *pFunctionInfo = GetFunctionInfo(&IServerGameEnts::CheckTransmit);
+ if (!pFunctionInfo)
+ BOOST_RAISE_EXCEPTION(
+ PyExc_ValueError,
+ "Failed to retrieve CheckTransmit's info."
+ )
+
+ CFunction *pFunction = CPointer((unsigned long)((void *)gameents)).MakeVirtualFunction(*pFunctionInfo);
+ delete pFunctionInfo;
+
+ if (!pFunction || !pFunction->IsHookable())
+ BOOST_RAISE_EXCEPTION(
+ PyExc_ValueError,
+ "CheckTransmit is invalid or not hookable."
+ )
+
+ void *pAddr = (void *)pFunction->m_ulAddr;
+ CHook *pHook = GetHookManager()->FindHook(pAddr);
+ if (!pHook)
+ {
+ pHook = GetHookManager()->HookFunction(pAddr, pFunction->m_pCallingConvention);
+ if (!pHook)
+ BOOST_RAISE_EXCEPTION(
+ PyExc_ValueError,
+ "Failed to hook CheckTransmit."
+ )
+ }
+
+ //m_pCheckTransmit = (void *)pFunction->m_ulAddr;
+ delete pFunction;
+
+ pHook->AddCallback(
+ HOOKTYPE_POST,
+ (HookHandlerFn *)&CTransmitManager::_post_check_transmit
+ );
+
+ current_clients = 0;
+
+ IPlayerInfo* pPlayerInfo = nullptr;
+ ConVar* sv_replaybots = cvar->FindVar("sv_replaybots");
+ ConVar* sv_stressbots = cvar->FindVar("sv_stressbots");
+
+ for (unsigned int i=1; i <= (unsigned int) gpGlobals->maxClients; ++i)
+ {
+ if (!PlayerInfoFromIndex(i, pPlayerInfo))
+ continue;
+
+ if ((pPlayerInfo->IsFakeClient() || V_strstr(pPlayerInfo->GetNetworkIDString(), "BOT")) && (
+ (!sv_replaybots || !sv_replaybots->GetBool()) ||
+ (!sv_stressbots || !sv_stressbots->GetBool())))
+ continue;
+
+ ++current_clients;
+ m_arrayFilterIndexes[i-1] = current_clients;
+ }
+
+ unsigned int filter_size = bit_ceil(current_clients);
+ if (filter_size < 4)
+ filter_size = 4;
+
+ m_vecFilters.resize(filter_size);
+
+ for(auto it = m_vecFilters.begin(); it != m_vecFilters.begin()+current_clients; ++it)
+ {
+ it->hide.SetAll();
+ }
+}
+
+
+CTransmitManager::~CTransmitManager()
+{
+/*
+ CHook *pHook = GetHookManager()->FindHook(m_pCheckTransmit);
+ if (pHook)
+ pHook->RemoveCallback(HOOKTYPE_POST, (HookHandlerFn *)&CTransmitManager::_post_check_transmit);
+*/
+}
+
+
+CTransmitManager* CTransmitManager::get_instance()
+{
+ if (!instance)
+ create();
+
+ return instance;
+}
+
+
+void CTransmitManager::create()
+{
+ instance = new CTransmitManager;
+}
+
+
+/*
+void CTransmitManager::destroy()
+{
+ delete instance;
+ instance = nullptr;
+}
+*/
+
+
+void CTransmitManager::add_player(edict_t* pEdict, unsigned int player_index)
+{
+ static ConVar* sv_replaybots = cvar->FindVar("sv_replaybots");
+ static ConVar* sv_stressbots = cvar->FindVar("sv_stressbots");
+ IPlayerInfo* pPlayerInfo = playerinfomanager->GetPlayerInfo(pEdict);
+
+ if ((pPlayerInfo &&
+ (pPlayerInfo->IsFakeClient() || V_strstr(pPlayerInfo->GetNetworkIDString(), "BOT"))) && (
+ (!sv_replaybots || !sv_replaybots->GetBool()) ||
+ (!sv_stressbots || !sv_stressbots->GetBool())))
+ return;
+
+ unsigned int filter_size = m_vecFilters.size();
+ if (current_clients >= filter_size)
+ m_vecFilters.resize(filter_size*2);
+
+ unsigned char filter_index;
+ if (find_filter_index(player_index, filter_index))
+ {
+ ++current_clients;
+ m_arrayFilterIndexes[player_index-1] = filter_index;
+
+ Filter& filter = m_vecFilters[filter_index-1];
+
+ filter.hide.SetAll();
+ filter.show.ClearAll();
+ }
+}
+
+
+void CTransmitManager::remove_player(edict_t* pEdict, unsigned int player_index)
+{
+ unsigned char filter_index;
+ if (get_filter_index(player_index, filter_index))
+ {
+ --current_clients;
+ m_arrayFilterIndexes[player_index-1] = 0;
+ }
+}
+
+
+void CTransmitManager::hide(unsigned int entity_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return;
+
+ for(auto it = m_vecFilters.begin(); it != m_vecFilters.begin()+current_clients; ++it)
+ {
+ it->hide.Set((int) entity_index, false);
+ it->show.Set((int) entity_index, false);
+ }
+
+ reset_from(entity_index, entity_index);
+}
+
+
+void CTransmitManager::hide_from(unsigned int entity_index, unsigned int player_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return;
+
+ if (player_index < 1 || player_index > (unsigned int) gpGlobals->maxClients)
+ return;
+
+ if (entity_index == player_index)
+ return;
+
+ unsigned char filter_index;
+ if (get_filter_index(player_index, filter_index))
+ {
+ Filter& filter = m_vecFilters[filter_index-1];
+
+ filter.hide.Set((int) entity_index, false);
+ filter.show.Set((int) entity_index, false);
+ }
+}
+
+
+void CTransmitManager::show(unsigned int entity_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return;
+
+ for( auto it = m_vecFilters.begin(); it != m_vecFilters.begin()+current_clients; ++it)
+ {
+ it->hide.Set((int) entity_index, true);
+ it->show.Set((int) entity_index, true);
+ }
+}
+
+
+void CTransmitManager::show_from(unsigned int entity_index, unsigned int player_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return;
+
+ if (player_index < 1 || player_index > (unsigned int) gpGlobals->maxClients)
+ return;
+
+ unsigned char filter_index;
+ if (get_filter_index(player_index, filter_index))
+ {
+ Filter& filter = m_vecFilters[filter_index-1];
+
+ filter.hide.Set((int) entity_index, true);
+ filter.show.Set((int) entity_index, true);
+ }
+}
+
+
+void CTransmitManager::reset(unsigned int entity_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return;
+
+ for( auto it = m_vecFilters.begin(); it != m_vecFilters.begin()+current_clients; ++it)
+ {
+ it->hide.Set((int) entity_index, true);
+ it->show.Set((int) entity_index, false);
+ }
+}
+
+
+void CTransmitManager::reset_from(unsigned int entity_index, unsigned int player_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return;
+
+ if (player_index < 1 || player_index > (unsigned int) gpGlobals->maxClients)
+ return;
+
+ unsigned char filter_index;
+ if (get_filter_index(player_index, filter_index))
+ {
+ Filter& filter = m_vecFilters[filter_index-1];
+
+ filter.hide.Set((int) entity_index, true);
+ filter.show.Set((int) entity_index, false);
+ }
+}
+
+
+void CTransmitManager::reset_player(unsigned int player_index)
+{
+ if (player_index < 1 || player_index > (unsigned int) gpGlobals->maxClients)
+ return;
+
+ unsigned char filter_index;
+ if (get_filter_index(player_index, filter_index))
+ {
+ Filter& filter = m_vecFilters[filter_index-1];
+
+ filter.hide.SetAll();
+ filter.show.ClearAll();
+ }
+}
+
+
+void CTransmitManager::reset_all()
+{
+ for( auto it = m_vecFilters.begin(); it != m_vecFilters.begin()+current_clients; ++it)
+ {
+ it->hide.SetAll();
+ it->show.ClearAll();
+ }
+}
+
+
+bool CTransmitManager::is_hidden(unsigned int entity_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return false;
+
+ bool hidden_state = false;
+
+ for( auto it = m_vecFilters.begin(); it != m_vecFilters.begin()+current_clients; ++it)
+ {
+ hidden_state |= (!it->hide.IsBitSet((int) entity_index));
+ }
+
+ return hidden_state;
+}
+
+
+bool CTransmitManager::is_hidden_from(unsigned int entity_index, unsigned int player_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return false;
+
+ if (player_index < 1 || player_index > (unsigned int) gpGlobals->maxClients)
+ return false;
+
+ unsigned char filter_index;
+ if (get_filter_index(player_index, filter_index))
+ {
+ Filter& filter = m_vecFilters[filter_index-1];
+
+ return (!filter.hide.IsBitSet((int) entity_index));
+ }
+
+ return false;
+}
+
+
+boost::python::tuple CTransmitManager::get_hidden_player(unsigned int entity_index)
+{
+ if (entity_index < 1 || entity_index >= MAX_EDICTS)
+ return boost::python::tuple();
+
+ static boost::python::object Player = boost::python::import("players.entity").attr("Player");
+
+ boost::python::list player_list;
+
+ for (unsigned int i=1; i <= (unsigned int) gpGlobals->maxClients; ++i)
+ {
+ if (is_hidden_from(entity_index, i))
+ player_list.append(Player(i));
+ }
+
+ return boost::python::tuple(player_list);
+}
+
+
+boost::python::tuple CTransmitManager::get_hidden_entity(unsigned int player_index)
+{
+ if (player_index < 1 || player_index > (unsigned int) gpGlobals->maxClients)
+ return boost::python::tuple();
+
+ static boost::python::object Entity = boost::python::import("entities.entity").attr("Entity");
+
+ boost::python::list entity_list;
+
+ for (unsigned int i=1; i < MAX_EDICTS; ++i)
+ {
+ if (is_hidden_from(i, player_index))
+ entity_list.append(Entity(i));
+ }
+
+ return boost::python::tuple(entity_list);
+}
+
+
+void CTransmitManager::handle_filters(TransmitStates_t* pTransmitEdict, unsigned int player_index)
+{
+ unsigned char filter_index;
+ if (get_filter_index(player_index, filter_index))
+ {
+ Filter& filter = m_vecFilters[filter_index-1];
+
+ uint32* base = pTransmitEdict->Base();
+ const uint32* hide = filter.hide.Base();
+ const uint32* show = filter.show.Base();
+ for (int i = 0; i < CalcNumIntsForBits(MAX_EDICTS); ++i)
+ {
+ base[i] = (base[i] & hide[i]) | show[i];
+ }
+ }
+}
+
+
+bool CTransmitManager::_post_check_transmit(HookType_t eHookType, CHook* pHook)
+{
+ int nEdicts = pHook->GetArgument(3);
+ if (!nEdicts)
+ return false;
+
+ CCheckTransmitInfo* pTransmitInfo = pHook->GetArgument(1);
+
+ unsigned int player_index;
+ if (!IndexFromEdict(pTransmitInfo->m_pClientEnt, player_index))
+ return false;
+
+ instance->handle_filters(pTransmitInfo->m_pTransmitEdict, player_index);
+
+ return false;
+}
+
diff --git a/src/core/modules/entities/entities_transmit.h b/src/core/modules/entities/entities_transmit.h
new file mode 100755
index 000000000..db6e74a89
--- /dev/null
+++ b/src/core/modules/entities/entities_transmit.h
@@ -0,0 +1,155 @@
+/**
+* =============================================================================
+* Source Python
+* Copyright (C) 2012-2020 Source Python Development Team. All rights reserved.
+* =============================================================================
+*
+* This program is free software; you can redistribute it and/or modify it under
+* the terms of the GNU General Public License, version 3.0, as published by the
+* Free Software Foundation.
+*
+* This program 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
+* this program. If not, see .
+*
+* As a special exception, the Source Python Team gives you permission
+* to link the code of this program (as well as its derivative works) to
+* "Half-Life 2," the "Source Engine," and any Game MODs that run on software
+* by the Valve Corporation. You must obey the GNU General Public License in
+* all respects for all other code used. Additionally, the Source.Python
+* Development Team grants this exception to all derivative works.
+*/
+
+#ifndef _ENTITIES_TRANSMIT_H
+#define _ENTITIES_TRANSMIT_H
+
+//-----------------------------------------------------------------------------
+// Includes.
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+
+#ifdef _WIN32
+ #include
+#endif
+
+// Source SDK
+#include "public/bitvec.h"
+
+// Source.Python
+#include "utilities/baseentity.h"
+#include "modules/memory/memory_function.h"
+
+// Boost.Python
+#include "boost/python.hpp"
+
+
+//-----------------------------------------------------------------------------
+// TransmitStates_t definition.
+//-----------------------------------------------------------------------------
+typedef CBitVec TransmitStates_t;
+
+
+//-----------------------------------------------------------------------------
+// Filter struct.
+//-----------------------------------------------------------------------------
+struct Filter{
+ TransmitStates_t hide;
+ TransmitStates_t show;
+};
+
+
+//-----------------------------------------------------------------------------
+// CTransmitManager class.
+//-----------------------------------------------------------------------------
+class CTransmitManager
+{
+private:
+ CTransmitManager();
+ ~CTransmitManager();
+
+ void handle_filters(TransmitStates_t* pTransmitEdict, unsigned int player_index);
+ static bool _post_check_transmit(HookType_t eHookType, CHook *pHook);
+
+public:
+ static CTransmitManager* get_instance();
+
+ static void create();
+ //static void destroy();
+
+ void add_player(edict_t* pEdict, unsigned int player_index);
+ void remove_player(edict_t* pEdict, unsigned int player_index);
+
+ void hide(unsigned int entity_index);
+ void hide_from(unsigned int entity_index, unsigned int player_index);
+
+ void show(unsigned int entity_index);
+ void show_from(unsigned int entity_index, unsigned int player_index);
+
+ void reset(unsigned int entity_index);
+ void reset_from(unsigned int entity_index, unsigned int player_index);
+ void reset_player(unsigned int player_index);
+ void reset_all();
+
+ bool is_hidden(unsigned int entity_index);
+ bool is_hidden_from(unsigned int entity_index, unsigned int player_index);
+
+ boost::python::tuple get_hidden_player(unsigned int entity_index);
+ boost::python::tuple get_hidden_entity(unsigned int player_index);
+
+ inline bool find_filter_index(unsigned int player_index, unsigned char& filter_index)
+ {
+ for (unsigned int i = 1; i <= m_arrayFilterIndexes.size(); ++i)
+ {
+ auto it = std::find(m_arrayFilterIndexes.begin(), m_arrayFilterIndexes.end(), i);
+ if (it == m_arrayFilterIndexes.end())
+ {
+ filter_index = i;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ inline bool get_filter_index(unsigned int player_index, unsigned char& filter_index)
+ {
+ filter_index = m_arrayFilterIndexes[player_index-1];
+ if (filter_index)
+ return true;
+ else
+ return false;
+ }
+
+private:
+ static CTransmitManager* instance;
+
+ //void* m_pCheckTransmit;
+
+ unsigned int current_clients;
+ std::vector m_vecFilters;
+ std::array m_arrayFilterIndexes;
+};
+
+
+//-----------------------------------------------------------------------------
+// Functions.
+//-----------------------------------------------------------------------------
+inline unsigned int bit_ceil(unsigned int x)
+{
+#ifdef _WIN32
+ unsigned long ret;
+ _BitScanReverse(&ret, (x-1));
+ return 1 << (32 - (31 ^ ret));//(1 << (32 - __lzcnt(x))))
+#else
+ return 1 << (32 - __builtin_clz(x-1));
+#endif
+}
+
+
+#endif // _ENTITIES_TRANSMIT_H
diff --git a/src/core/modules/entities/entities_transmit_wrap.cpp b/src/core/modules/entities/entities_transmit_wrap.cpp
new file mode 100755
index 000000000..e1a405048
--- /dev/null
+++ b/src/core/modules/entities/entities_transmit_wrap.cpp
@@ -0,0 +1,142 @@
+/**
+* =============================================================================
+* Source Python
+* Copyright (C) 2012-2020 Source Python Development Team. All rights reserved.
+* =============================================================================
+*
+* This program is free software; you can redistribute it and/or modify it under
+* the terms of the GNU General Public License, version 3.0, as published by the
+* Free Software Foundation.
+*
+* This program 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
+* this program. If not, see .
+*
+* As a special exception, the Source Python Team gives you permission
+* to link the code of this program (as well as its derivative works) to
+* "Half-Life 2," the "Source Engine," and any Game MODs that run on software
+* by the Valve Corporation. You must obey the GNU General Public License in
+* all respects for all other code used. Additionally, the Source.Python
+* Development Team grants this exception to all derivative works.
+*/
+
+//-----------------------------------------------------------------------------
+// Includes.
+//-----------------------------------------------------------------------------
+#include "export_main.h"
+#include "utilities/wrap_macros.h"
+#include "modules/entities/entities_transmit.h"
+#include "modules/memory/memory_tools.h"
+
+
+//-----------------------------------------------------------------------------
+// Forward declarations.
+//-----------------------------------------------------------------------------
+static void export_transmit_manager(scope);
+
+
+//-----------------------------------------------------------------------------
+// Declare the _entities._transmit module.
+//-----------------------------------------------------------------------------
+DECLARE_SP_SUBMODULE(_entities, _transmit)
+{
+ export_transmit_manager(_transmit);
+}
+
+
+//-----------------------------------------------------------------------------
+// Exports CTransmitManager.
+//-----------------------------------------------------------------------------
+void export_transmit_manager(scope _transmit)
+{
+ class_("TransmitManager", no_init)
+
+ // Class methods
+ .def("hide",
+ &CTransmitManager::hide,
+ "Hide the entity from all players.",
+ ("entity_index")
+ )
+
+ .def("hide_from",
+ &CTransmitManager::hide_from,
+ "Hide the entity from player.",
+ ("entity_index", "player_index")
+ )
+
+
+ .def("show",
+ &CTransmitManager::show,
+ "Show the entity to all players.",
+ ("entity_index")
+ )
+
+ .def("show_from",
+ &CTransmitManager::show_from,
+ "Show the entity to player.",
+ ("entity_index", "player_index")
+ )
+
+
+ .def("reset",
+ &CTransmitManager::reset,
+ "Reset the entity's hidden/shown state.",
+ ("entity_index")
+ )
+
+ .def("reset_from",
+ &CTransmitManager::reset_from,
+ "Reset the player's entity hidden/shown state.",
+ ("entity_index", "player_index")
+ )
+
+ .def("reset_player",
+ &CTransmitManager::reset_player,
+ "Reset the player's hidden/shown state on all entities.",
+ ("player_index")
+ )
+
+ .def("reset_all",
+ &CTransmitManager::reset_all,
+ "Reset all entities' hidden/shown state."
+ )
+
+
+ .def("is_hidden",
+ &CTransmitManager::is_hidden,
+ "Return True if the entity is hidden from any player.",
+ ("entity_index")
+ )
+
+ .def("is_hidden_from",
+ &CTransmitManager::is_hidden_from,
+ "Return True if the entity is hidden from the player.",
+ ("entity_index", "player_index")
+ )
+
+
+ .def("get_hidden_player",
+ &CTransmitManager::get_hidden_player,
+ "Get the players where the entity is hidden.",
+ ("entity_index")
+ )
+
+ .def("get_hidden_entity",
+ &CTransmitManager::get_hidden_entity,
+ "Get the entities that are hidden from the player.",
+ ("player_index")
+ )
+
+
+ // Add memory tools...
+ ADD_MEM_TOOLS(CTransmitManager)
+ ;
+
+ // Singleton...
+ _transmit.attr("transmit_manager") = object(ptr(CTransmitManager::get_instance()));
+}
+
diff --git a/src/core/sp_main.cpp b/src/core/sp_main.cpp
index 6f1be1dcb..e68c599ce 100755
--- a/src/core/sp_main.cpp
+++ b/src/core/sp_main.cpp
@@ -61,6 +61,7 @@
#include "utilities/conversions.h"
#include "modules/entities/entities_entity.h"
#include "modules/core/core.h"
+#include "modules/entities/entities_transmit.h"
#ifdef _WIN32
#include "Windows.h"
@@ -92,6 +93,7 @@ IEngineSound* enginesound = NULL;
CGlobalVars* gpGlobals = NULL;
IFileSystem* filesystem = NULL;
IServerGameDLL* servergamedll = NULL;
+IServerGameEnts* gameents = NULL; // Interface to get at server entities
IServerGameClients* servergameclients = NULL;
IServerTools* servertools = NULL;
IPhysics* physics = NULL;
@@ -154,6 +156,7 @@ InterfaceHelper_t gGameInterfaces[] = {
{INTERFACEVERSION_PLAYERINFOMANAGER, (void **)&playerinfomanager},
{INTERFACEVERSION_PLAYERBOTMANAGER, (void **)&botmanager},
{INTERFACEVERSION_SERVERGAMEDLL, (void **)&servergamedll},
+ {INTERFACEVERSION_SERVERGAMEENTS, (void **)&gameents},
{INTERFACEVERSION_SERVERGAMECLIENTS, (void **)&servergameclients},
{VSERVERTOOLS_INTERFACE_VERSION, (void **)&servertools},
{NULL, NULL}
@@ -500,6 +503,9 @@ void CSourcePython::ClientActive( edict_t *pEntity )
if (!IndexFromEdict(pEntity, iEntityIndex))
return;
+ static CTransmitManager* transmit_manager = CTransmitManager::get_instance();
+ transmit_manager->add_player(pEntity, iEntityIndex);
+
CALL_LISTENERS(OnClientActive, iEntityIndex);
}
@@ -513,6 +519,9 @@ void CSourcePython::ClientDisconnect( edict_t *pEntity )
return;
CALL_LISTENERS(OnClientDisconnect, iEntityIndex);
+
+ static CTransmitManager* transmit_manager = CTransmitManager::get_instance();
+ transmit_manager->remove_player(pEntity, iEntityIndex);
}
//-----------------------------------------------------------------------------