From a8b43076742767cfe37c1abcd51311a2ebb85ffc Mon Sep 17 00:00:00 2001
From: Ayuto22 <ayuto@web.de>
Date: Wed, 28 Feb 2018 22:30:44 +0100
Subject: [PATCH 1/8] Initial structure for core hooks

---
 src/CMakeLists.txt    |   2 +
 src/core/sp_hooks.cpp | 167 ++++++++++++++++++++++++++++++++++++++++++
 src/core/sp_hooks.h   |  95 ++++++++++++++++++++++++
 src/core/sp_main.cpp  |  11 +++
 4 files changed, 275 insertions(+)
 create mode 100644 src/core/sp_hooks.cpp
 create mode 100644 src/core/sp_hooks.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d9c3bb875..1c1625f8d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -81,12 +81,14 @@ Set(SOURCEPYTHON_CORE_HEADERS
     core/sp_python.h
     core/sp_main.h
     core/export_main.h
+    core/sp_hooks.h
 )
 
 Set(SOURCEPYTHON_CORE_SOURCES
     core/sp_python.cpp
     core/sp_main.cpp
     core/export_main.cpp
+    core/sp_hooks.cpp
 )
 
 Set(SOURCEPYTHON_CORE_FILES
diff --git a/src/core/sp_hooks.cpp b/src/core/sp_hooks.cpp
new file mode 100644
index 000000000..415da71fa
--- /dev/null
+++ b/src/core/sp_hooks.cpp
@@ -0,0 +1,167 @@
+/**
+* =============================================================================
+* Source Python
+* Copyright (C) 2012-2018 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 <http://www.gnu.org/licenses/>.
+*
+* 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
+//---------------------------------------------------------------------------------
+// Boost.Python
+#include "boost/python.hpp"
+using namespace boost::python;
+
+// Source.Python
+#include "sp_hooks.h"
+#include "utilities/conversions.h"
+#include "utilities/call_python.h"
+#include "modules/entities/entities_entity.h"
+
+
+//---------------------------------------------------------------------------------
+// GLOBAL VARIABLES
+//---------------------------------------------------------------------------------
+std::vector<IEntityHook*> g_EntityHooks;
+
+
+//---------------------------------------------------------------------------------
+// ISimpleEntityHook
+//---------------------------------------------------------------------------------
+ISimpleEntityHook::ISimpleEntityHook(const char* func_name, HookHandlerFn* hook_handler, bool pre, bool post)
+{
+	this->func_name = func_name;
+	this->hook_handler = hook_handler;
+	this->pre = pre;
+	this->post = post;
+}
+
+bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
+{
+	if (!Test(pEntity))
+	{
+		return false;
+	}
+
+	unsigned int index;
+	if (!IndexFromBaseEntity(pEntity, index))
+	{
+		return false;
+	}
+	
+	PythonLog(3, "Initializing core hook (%s)...", this->func_name);
+
+	static object Entity = import("entities.entity").attr("Entity");
+
+	object entity = Entity(index);
+	CFunction* func = extract<CFunction*>(entity.attr(this->func_name));
+
+	CHook* pHook = GetHookManager()->FindHook((void*) func->m_ulAddr);
+	if (!pHook)
+	{
+		pHook = GetHookManager()->HookFunction(
+			(void*) func->m_ulAddr,
+			func->m_pCallingConvention);
+	}
+
+	if (!pHook)
+	{
+		PythonLog(0, "Could not find or create a hook.");
+		return false;
+	}
+
+	if (this->pre)
+	{
+		PythonLog(4, "Adding a pre hook.");
+		pHook->AddCallback(HOOKTYPE_PRE, this->hook_handler);
+	}
+
+	if (this->post)
+	{
+		PythonLog(4, "Adding a post hook.");
+		pHook->AddCallback(HOOKTYPE_POST, this->hook_handler);
+	}
+	
+	PythonLog(3, "Core hook has been initialized.");
+	return true;
+}
+
+
+//---------------------------------------------------------------------------------
+// PlayerHook
+//---------------------------------------------------------------------------------
+PlayerHook::PlayerHook(const char* func_name, HookHandlerFn* hook_handler, bool pre, bool post)
+	:ISimpleEntityHook(func_name, hook_handler, pre, post)
+{
+}
+
+bool PlayerHook::Test(CBaseEntity* pEntity)
+{
+	CBaseEntityWrapper* pWrapper = (CBaseEntityWrapper*) pEntity;
+	return pWrapper->IsPlayer();
+}
+
+
+//---------------------------------------------------------------------------------
+// FUNCTIONS
+//---------------------------------------------------------------------------------
+void InitHooks()
+{
+	CBaseEntity* pEntity = (CBaseEntity *) servertools->FirstEntity();
+	while (pEntity)
+	{
+		InitHooks(pEntity);
+		pEntity = (CBaseEntity *) servertools->NextEntity(pEntity);
+	}
+}
+
+void InitHooks(CBaseEntity* pEntity)
+{
+	if (!pEntity)
+		return;
+
+	std::vector<IEntityHook*>::iterator it = g_EntityHooks.begin();
+	while (it != g_EntityHooks.end())
+	{
+		IEntityHook* pHook = *it;
+
+		if (pHook->Initialize(pEntity))
+		{
+			it = g_EntityHooks.erase(it);
+			delete pHook;
+		}
+		else
+		{
+			++it;
+		}
+	}
+}
+
+//---------------------------------------------------------------------------------
+// HOOKS
+//---------------------------------------------------------------------------------
+bool PrePlayerRunCommand(HookType_t hook_type, CHook* pHook)
+{
+	// Test
+	puts("PrePlayerRunCommand");
+	return false;
+}
diff --git a/src/core/sp_hooks.h b/src/core/sp_hooks.h
new file mode 100644
index 000000000..d254982a5
--- /dev/null
+++ b/src/core/sp_hooks.h
@@ -0,0 +1,95 @@
+/**
+* =============================================================================
+* Source Python
+* Copyright (C) 2012-2018 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 <http://www.gnu.org/licenses/>.
+*
+* 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 _SP_HOOKS_H
+#define _SP_HOOKS_H
+
+//---------------------------------------------------------------------------------
+// Includes
+//---------------------------------------------------------------------------------
+// C++
+#include <vector>
+
+// DynamicHooks
+#include "hook.h"
+
+
+//---------------------------------------------------------------------------------
+// IEntityHook
+//---------------------------------------------------------------------------------
+class CBaseEntity;
+
+class IEntityHook
+{
+public:
+	virtual bool Initialize(CBaseEntity* pEntity) = NULL;
+};
+
+extern std::vector<IEntityHook*> g_EntityHooks;
+
+//---------------------------------------------------------------------------------
+// ISimpleEntityHook
+//---------------------------------------------------------------------------------
+class ISimpleEntityHook: public IEntityHook
+{
+public:
+	ISimpleEntityHook(const char* func_name, HookHandlerFn* hook_handler, bool pre, bool post);
+	
+	virtual bool Initialize(CBaseEntity* pEntity);
+	virtual bool Test(CBaseEntity* pEntity) = NULL;
+
+public:
+	const char* func_name;
+	HookHandlerFn* hook_handler;
+	bool pre;
+	bool post;
+};
+
+//---------------------------------------------------------------------------------
+// PlayerHook
+//---------------------------------------------------------------------------------
+class PlayerHook: public ISimpleEntityHook
+{
+public:
+	PlayerHook(const char* func_name, HookHandlerFn* hook_handler, bool pre, bool post);
+
+	virtual bool Test(CBaseEntity* pEntity);
+};
+
+
+//---------------------------------------------------------------------------------
+// FUNCTIONS
+//---------------------------------------------------------------------------------
+void InitHooks();
+void InitHooks(CBaseEntity* pEntity);
+
+
+//---------------------------------------------------------------------------------
+// HOOKS
+//---------------------------------------------------------------------------------
+bool PrePlayerRunCommand(HookType_t hook_type, CHook* pHook);
+
+
+#endif // _SP_HOOKS_H
diff --git a/src/core/sp_main.cpp b/src/core/sp_main.cpp
index b82d2eac5..92df38832 100644
--- a/src/core/sp_main.cpp
+++ b/src/core/sp_main.cpp
@@ -65,6 +65,7 @@
 	#include "Windows.h"
 #endif
 
+#include "sp_hooks.h"
 
 //-----------------------------------------------------------------------------
 // Disable warnings.
@@ -353,6 +354,13 @@ bool CSourcePython::Load(	CreateInterfaceFn interfaceFactory, CreateInterfaceFn
 
 	DevMsg(1, MSG_PREFIX "Setting the new cache notifier...\n");
 	modelcache->SetCacheNotify(this);
+
+	g_EntityHooks.push_back(new PlayerHook(
+		"run_command",
+		(HookHandlerFn*) (void*) &PrePlayerRunCommand,
+		true, false));
+
+	InitHooks();
 	
 	Msg(MSG_PREFIX "Loaded successfully.\n");
 	return true;
@@ -609,6 +617,9 @@ void CSourcePython::OnEntityCreated( CBaseEntity *pEntity )
 		if (pServerUnknown)
 			pEdict->m_pNetworkable = pServerUnknown->GetNetworkable();
 	}
+
+	InitHooks(pEntity);
+
 	CALL_LISTENERS(OnEntityCreated, ptr((CBaseEntityWrapper*) pEntity));
 }
 

From cafa880baad5c3e8414cf7ae6d073232f28cc7d6 Mon Sep 17 00:00:00 2001
From: Ayuto22 <ayuto@web.de>
Date: Wed, 28 Feb 2018 22:41:46 +0100
Subject: [PATCH 2/8] Moved a log message

---
 src/core/sp_hooks.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/core/sp_hooks.cpp b/src/core/sp_hooks.cpp
index 415da71fa..e46dcd86e 100644
--- a/src/core/sp_hooks.cpp
+++ b/src/core/sp_hooks.cpp
@@ -81,12 +81,12 @@ bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
 		pHook = GetHookManager()->HookFunction(
 			(void*) func->m_ulAddr,
 			func->m_pCallingConvention);
-	}
 
-	if (!pHook)
-	{
-		PythonLog(0, "Could not find or create a hook.");
-		return false;
+		if (!pHook)
+		{
+			PythonLog(0, "Could create a hook.");
+			return false;
+		}
 	}
 
 	if (this->pre)

From d5ea6ad63bf59cb8414c3382ca6ae76b7033f488 Mon Sep 17 00:00:00 2001
From: Ayuto22 <ayuto@web.de>
Date: Thu, 1 Mar 2018 21:34:10 +0100
Subject: [PATCH 3/8] Implemented OnPlayerRunCommand and OnButtonStateChanged
 in C++

---
 .../packages/source-python/__init__.py        | 12 ----
 .../source-python/listeners/__init__.py       |  6 +-
 .../source-python/listeners/_run_command.py   | 67 -------------------
 .../modules/listeners/listeners_manager.h     |  4 ++
 src/core/modules/listeners/listeners_wrap.cpp |  5 ++
 src/core/sp_hooks.cpp                         | 45 ++++++++++++-
 src/makefiles/branch/blade.cmake              |  2 +-
 src/makefiles/branch/bms.cmake                |  2 +-
 src/makefiles/branch/csgo.cmake               |  2 +-
 src/makefiles/branch/css.cmake                |  2 +-
 src/makefiles/branch/dods.cmake               |  2 +-
 src/makefiles/branch/gmod.cmake               |  2 +-
 src/makefiles/branch/hl2dm.cmake              |  2 +-
 src/makefiles/branch/l4d2.cmake               |  2 +-
 src/makefiles/branch/tf2.cmake                |  2 +-
 15 files changed, 65 insertions(+), 92 deletions(-)
 delete mode 100644 addons/source-python/packages/source-python/listeners/_run_command.py

diff --git a/addons/source-python/packages/source-python/__init__.py b/addons/source-python/packages/source-python/__init__.py
index 2d592d7aa..b58a9ec1e 100644
--- a/addons/source-python/packages/source-python/__init__.py
+++ b/addons/source-python/packages/source-python/__init__.py
@@ -94,7 +94,6 @@ def load():
     setup_auth()
     setup_user_settings()
     setup_entities_listener()
-    setup_run_command_listener()
     setup_versioning()
     setup_sqlite()
 
@@ -407,17 +406,6 @@ def remove_entities_listener():
             _sp_plugin)
 
 
-# =============================================================================
-# >> RUN COMMAND LISTENER
-# =============================================================================
-def setup_run_command_listener():
-    """Set up the run command listener."""
-    _sp_logger.log_debug('Setting up run command listener...')
-
-    # This is done here to fix a cyclic import
-    import listeners._run_command
-
-
 # =============================================================================
 # >> PLUGINS
 # =============================================================================
diff --git a/addons/source-python/packages/source-python/listeners/__init__.py b/addons/source-python/packages/source-python/listeners/__init__.py
index 6903873f1..c7c19e220 100644
--- a/addons/source-python/packages/source-python/listeners/__init__.py
+++ b/addons/source-python/packages/source-python/listeners/__init__.py
@@ -66,6 +66,8 @@
 from _listeners import on_server_activate_listener_manager
 from _listeners import on_tick_listener_manager
 from _listeners import on_server_output_listener_manager
+from _listeners import on_player_run_command_listener_manager
+from _listeners import on_button_state_changed_listener_manager
 
 
 # =============================================================================
@@ -137,6 +139,8 @@
            'on_tick_listener_manager',
            'on_version_update_listener_manager',
            'on_server_output_listener_manager',
+           'on_player_run_command_listener_manager',
+           'on_button_state_changed_listener_manager',
            )
 
 
@@ -153,8 +157,6 @@
 on_plugin_loading_manager = ListenerManager()
 on_plugin_unloading_manager = ListenerManager()
 on_level_end_listener_manager = ListenerManager()
-on_player_run_command_listener_manager = ListenerManager()
-on_button_state_changed_listener_manager = ListenerManager()
 on_entity_output_listener_manager = ListenerManager()
 
 _check_for_update = ConVar(
diff --git a/addons/source-python/packages/source-python/listeners/_run_command.py b/addons/source-python/packages/source-python/listeners/_run_command.py
deleted file mode 100644
index e5c942c5f..000000000
--- a/addons/source-python/packages/source-python/listeners/_run_command.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# ../listeners/_run_command.py
-
-"""Provides a run command listener."""
-
-# =============================================================================
-# >> IMPORTS
-# =============================================================================
-# Source.Python Imports
-#   Core
-from core import SOURCE_ENGINE_BRANCH
-#   Entities
-from entities.hooks import EntityPreHook
-from entities.hooks import EntityCondition
-#   Listeners
-from listeners import on_player_run_command_listener_manager
-from listeners import on_button_state_changed_listener_manager
-#   Players
-from players import UserCmd
-from players.entity import Player
-#   Memory
-from memory import make_object
-from memory import get_object_pointer
-from memory import get_size
-
-
-# =============================================================================
-# >> CONSTANTS
-# =============================================================================
-USER_CMD_SIZE = get_size(UserCmd)
-
-
-# =============================================================================
-# >> CALLBACKS
-# =============================================================================
-@EntityPreHook(EntityCondition.is_player, 'run_command')
-def _pre_player_run_command(args):
-    if (not on_player_run_command_listener_manager
-            and not on_button_state_changed_listener_manager):
-        return
-
-    player = make_object(Player, args[0])
-
-    # https://github.com/Source-Python-Dev-Team/Source.Python/issues/149
-    use_rtti_fix = SOURCE_ENGINE_BRANCH == 'tf2' and player.is_fake_client()
-    if use_rtti_fix:
-        user_cmd = UserCmd()
-        user_cmd_ptr = get_object_pointer(user_cmd)
-        args[1].copy(user_cmd_ptr, USER_CMD_SIZE)
-    else:
-        user_cmd = make_object(UserCmd, args[1])
-
-    if on_player_run_command_listener_manager:
-        on_player_run_command_listener_manager.notify(player, user_cmd)
-
-    if on_button_state_changed_listener_manager:
-        _handle_button_state_changed(player, player.buttons, user_cmd.buttons)
-
-    if use_rtti_fix:
-        user_cmd_ptr.copy(args[1], USER_CMD_SIZE)
-
-
-def _handle_button_state_changed(player, old_buttons, new_buttons):
-    if old_buttons == new_buttons:
-        return
-
-    on_button_state_changed_listener_manager.notify(
-        player, old_buttons, new_buttons)
diff --git a/src/core/modules/listeners/listeners_manager.h b/src/core/modules/listeners/listeners_manager.h
index a7744552e..ff8361baa 100644
--- a/src/core/modules/listeners/listeners_manager.h
+++ b/src/core/modules/listeners/listeners_manager.h
@@ -55,6 +55,10 @@
 		END_BOOST_PY_NORET() \
 	}
 
+#define GET_LISTENER_MANAGER(name, ret_var) \
+	extern CListenerManager* Get##name##ListenerManager(); \
+	CListenerManager* ret_var = Get##name##ListenerManager();
+
 
 //-----------------------------------------------------------------------------
 // CListenerManager class.
diff --git a/src/core/modules/listeners/listeners_wrap.cpp b/src/core/modules/listeners/listeners_wrap.cpp
index e9e1b7d5c..8df591e2a 100644
--- a/src/core/modules/listeners/listeners_wrap.cpp
+++ b/src/core/modules/listeners/listeners_wrap.cpp
@@ -58,6 +58,8 @@ DEFINE_MANAGER_ACCESSOR(OnDataLoaded)
 DEFINE_MANAGER_ACCESSOR(OnCombinerPreCache)
 DEFINE_MANAGER_ACCESSOR(OnDataUnloaded)
 DEFINE_MANAGER_ACCESSOR(OnServerOutput)
+DEFINE_MANAGER_ACCESSOR(OnPlayerRunCommand)
+DEFINE_MANAGER_ACCESSOR(OnButtonStateChanged)
 
 
 //-----------------------------------------------------------------------------
@@ -150,4 +152,7 @@ void export_listener_managers(scope _listeners)
 	_listeners.attr("on_data_unloaded_listener_manager") = object(ptr(GetOnDataUnloadedListenerManager()));
 	
 	_listeners.attr("on_server_output_listener_manager") = object(ptr(GetOnServerOutputListenerManager()));
+	
+	_listeners.attr("on_player_run_command_listener_manager") = object(ptr(GetOnPlayerRunCommandListenerManager()));
+	_listeners.attr("on_button_state_changed_listener_manager") = object(ptr(GetOnButtonStateChangedListenerManager()));
 }
diff --git a/src/core/sp_hooks.cpp b/src/core/sp_hooks.cpp
index e46dcd86e..ab096a79a 100644
--- a/src/core/sp_hooks.cpp
+++ b/src/core/sp_hooks.cpp
@@ -31,11 +31,16 @@
 #include "boost/python.hpp"
 using namespace boost::python;
 
+// SDK
+#include "edict.h"
+#include "game/shared/usercmd.h"
+
 // Source.Python
 #include "sp_hooks.h"
 #include "utilities/conversions.h"
 #include "utilities/call_python.h"
 #include "modules/entities/entities_entity.h"
+#include "modules/listeners/listeners_manager.h"
 
 
 //---------------------------------------------------------------------------------
@@ -161,7 +166,43 @@ void InitHooks(CBaseEntity* pEntity)
 //---------------------------------------------------------------------------------
 bool PrePlayerRunCommand(HookType_t hook_type, CHook* pHook)
 {
-	// Test
-	puts("PrePlayerRunCommand");
+	GET_LISTENER_MANAGER(OnPlayerRunCommand, run_command_manager);
+	GET_LISTENER_MANAGER(OnButtonStateChanged, button_state_manager);
+
+	if (!run_command_manager->GetCount() && !button_state_manager->GetCount())
+		return false;
+
+	static object Player = import("players.entity").attr("Player");
+
+	CBaseEntity* pEntity = pHook->GetArgument<CBaseEntity*>(0);
+	unsigned int index;
+	if (!IndexFromBaseEntity(pEntity, index))
+		return false;
+	
+	// https://github.com/Source-Python-Dev-Team/Source.Python/issues/149
+#if defined(ENGINE_BRANCH_TF2)
+	CUserCmd cmd = *pHook->GetArgument<CUserCmd*>(1);
+	CUserCmd* pCmd = &cmd;
+#else
+	CUserCmd* pCmd = pHook->GetArgument<CUserCmd*>(1);
+#endif
+
+	object player = Player(index);
+	CALL_LISTENERS(OnPlayerRunCommand, player, ptr(pCmd));
+
+	if (button_state_manager->GetCount())
+	{
+		int buttons = extract<int>(player.attr("buttons"));
+		if (buttons != pCmd->buttons)
+		{
+			CALL_LISTENERS(OnButtonStateChanged, player, buttons, pCmd->buttons);
+		}
+	}
+	
+#if defined(ENGINE_BRANCH_TF2)
+	CUserCmd* pRealCmd = pHook->GetArgument<CUserCmd*>(1);
+	memcpy(pRealCmd, pCmd, sizeof(CUserCmd));
+#endif
+
 	return false;
 }
diff --git a/src/makefiles/branch/blade.cmake b/src/makefiles/branch/blade.cmake
index fe69ac3cd..0b179b039 100644
--- a/src/makefiles/branch/blade.cmake
+++ b/src/makefiles/branch/blade.cmake
@@ -7,4 +7,4 @@
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "blade")
-add_definitions(-DENGINE_BLADE)
\ No newline at end of file
+add_definitions(-DENGINE_BLADE -DENGINE_BRANCH_BLADE)
\ No newline at end of file
diff --git a/src/makefiles/branch/bms.cmake b/src/makefiles/branch/bms.cmake
index 5be869988..29184b859 100644
--- a/src/makefiles/branch/bms.cmake
+++ b/src/makefiles/branch/bms.cmake
@@ -7,4 +7,4 @@
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "bms")
-add_definitions(-DENGINE_BMS)
\ No newline at end of file
+add_definitions(-DENGINE_BMS -DENGINE_BRANCH_BMS)
\ No newline at end of file
diff --git a/src/makefiles/branch/csgo.cmake b/src/makefiles/branch/csgo.cmake
index ed1c7487c..2650ce181 100644
--- a/src/makefiles/branch/csgo.cmake
+++ b/src/makefiles/branch/csgo.cmake
@@ -12,4 +12,4 @@ include("makefiles/branch/${BRANCH}/usermessages.cmake")
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "csgo")
-add_definitions(-DENGINE_CSGO -DUSE_PROTOBUF)
\ No newline at end of file
+add_definitions(-DENGINE_CSGO -DUSE_PROTOBUF -DENGINE_BRANCH_CSGO)
\ No newline at end of file
diff --git a/src/makefiles/branch/css.cmake b/src/makefiles/branch/css.cmake
index c45be94dd..6dacbc028 100644
--- a/src/makefiles/branch/css.cmake
+++ b/src/makefiles/branch/css.cmake
@@ -7,4 +7,4 @@
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "orangebox")
-add_definitions(-DENGINE_ORANGEBOX)
\ No newline at end of file
+add_definitions(-DENGINE_ORANGEBOX -DENGINE_BRANCH_CSS)
\ No newline at end of file
diff --git a/src/makefiles/branch/dods.cmake b/src/makefiles/branch/dods.cmake
index 8dea3fe68..aa9c43160 100644
--- a/src/makefiles/branch/dods.cmake
+++ b/src/makefiles/branch/dods.cmake
@@ -7,4 +7,4 @@
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "orangebox")
-add_definitions(-DENGINE_ORANGEBOX)
\ No newline at end of file
+add_definitions(-DENGINE_ORANGEBOX -DENGINE_BRANCH_DODS)
\ No newline at end of file
diff --git a/src/makefiles/branch/gmod.cmake b/src/makefiles/branch/gmod.cmake
index aefcbed68..bd656f66f 100644
--- a/src/makefiles/branch/gmod.cmake
+++ b/src/makefiles/branch/gmod.cmake
@@ -7,4 +7,4 @@
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "gmod")
-add_definitions(-DENGINE_GMOD)
\ No newline at end of file
+add_definitions(-DENGINE_GMOD -DENGINE_BRANCH_GMOD)
\ No newline at end of file
diff --git a/src/makefiles/branch/hl2dm.cmake b/src/makefiles/branch/hl2dm.cmake
index 83d00b296..45bce0f2c 100644
--- a/src/makefiles/branch/hl2dm.cmake
+++ b/src/makefiles/branch/hl2dm.cmake
@@ -7,4 +7,4 @@
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "orangebox")
-add_definitions(-DENGINE_ORANGEBOX)
\ No newline at end of file
+add_definitions(-DENGINE_ORANGEBOX -DENGINE_BRANCH_HL2DM)
\ No newline at end of file
diff --git a/src/makefiles/branch/l4d2.cmake b/src/makefiles/branch/l4d2.cmake
index 85351f538..40ddd31ad 100644
--- a/src/makefiles/branch/l4d2.cmake
+++ b/src/makefiles/branch/l4d2.cmake
@@ -7,4 +7,4 @@
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "l4d2")
-add_definitions(-DENGINE_LEFT4DEAD2)
\ No newline at end of file
+add_definitions(-DENGINE_LEFT4DEAD2 -DENGINE_BRANCH_L4D2)
\ No newline at end of file
diff --git a/src/makefiles/branch/tf2.cmake b/src/makefiles/branch/tf2.cmake
index 7dd5302df..7936d0de2 100644
--- a/src/makefiles/branch/tf2.cmake
+++ b/src/makefiles/branch/tf2.cmake
@@ -7,4 +7,4 @@
 # Set the engine version.
 # ------------------------------------------------------------------
 Set(SOURCE_ENGINE "orangebox")
-add_definitions(-DENGINE_ORANGEBOX)
\ No newline at end of file
+add_definitions(-DENGINE_ORANGEBOX -DENGINE_BRANCH_TF2)
\ No newline at end of file

From bce45e418bfb9062c0700e12918fd8a52acc6f32 Mon Sep 17 00:00:00 2001
From: Ayuto22 <ayuto@web.de>
Date: Sun, 4 Mar 2018 16:01:11 +0100
Subject: [PATCH 4/8] Fixed a crash if the import of Entity or retrieval of the
 function failed

---
 src/core/sp_hooks.cpp | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/core/sp_hooks.cpp b/src/core/sp_hooks.cpp
index ab096a79a..c3ddc50df 100644
--- a/src/core/sp_hooks.cpp
+++ b/src/core/sp_hooks.cpp
@@ -75,10 +75,23 @@ bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
 	
 	PythonLog(3, "Initializing core hook (%s)...", this->func_name);
 
-	static object Entity = import("entities.entity").attr("Entity");
+	CFunction* func = NULL;
+	try
+	{
+		static object Entity = import("entities.entity").attr("Entity");
+
+		object entity = Entity(index);
+		func = extract<CFunction*>(entity.attr(this->func_name));
+	}
+	catch (...)
+	{
+		PyErr_Print();
+		PyErr_Clear();
+
+		PythonLog(0, "Failed to initialize the core hook.");
 
-	object entity = Entity(index);
-	CFunction* func = extract<CFunction*>(entity.attr(this->func_name));
+		return true;
+	}
 
 	CHook* pHook = GetHookManager()->FindHook((void*) func->m_ulAddr);
 	if (!pHook)

From 369aa88a983b0bec563016748ee8764318f3ad72 Mon Sep 17 00:00:00 2001
From: Ayuto22 <ayuto@web.de>
Date: Sun, 4 Mar 2018 21:48:30 +0100
Subject: [PATCH 5/8] Updated installation documentation

---
 .../source/general/installation.rst           | 27 ++++++++++++++-----
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/addons/source-python/docs/source-python/source/general/installation.rst b/addons/source-python/docs/source-python/source/general/installation.rst
index 7397a23b8..aceb017ee 100644
--- a/addons/source-python/docs/source-python/source/general/installation.rst
+++ b/addons/source-python/docs/source-python/source/general/installation.rst
@@ -22,7 +22,7 @@ Debian/Ubuntu 64 bit              apt-get install zlib1g:i386        apt-get ins
 Windows
 ^^^^^^^
 
-1. `Visual C++ Redistributable for Visual Studio 2015 <https://www.microsoft.com/en-us/download/details.aspx?id=48145>`_ is required. Most machines already have a redistributable installed, so you only need to install it if Source.Python didn't load properly due to the missing redistributable.
+1. `Visual C++ Redistributable for Visual Studio 2010 (x86) <https://www.microsoft.com/en-US/download/details.aspx?id=5555>`_ is required. Most machines already have a redistributable installed, so you only need to install it if Source.Python didn't load properly due to the missing redistributable.
 
 
 Installation steps
@@ -30,11 +30,24 @@ Installation steps
 
 1. Download the latest Source.Python version from our `buildbot <http://builds.sourcepython.com/job/Source.Python/lastSuccessfulBuild/>`_.
 2. Extract the archive in your server's game folder. E.g. ``../my_servers/css/cstrike/``
-3. Start/restart your server and validate the installation:
-
-   1. Enter the server command ``plugin_print``. It should show the following entry: ``Source.Python, (C) 2012-2015, Source Python Team.``
-   2. Enter the server variable ``sp_version``. It should show the Source.Python version that is installed.
+3. Start/restart your server and validate the installation by typing ``sp info`` into the server console. It should print something like this:
+
+.. code-block:: none
+
+    IMPORTANT: Please copy the full output.
+    --------------------------------------------------------
+    Checksum      : b2471f6d7fbd05548c831307f1903218
+    Date          : 2018-03-04 20:35:02.237858
+    OS            : Windows-10-10.0.16299
+    Game          : css
+    SP version    : 646
+    Github commit : 3aee743f47727579a0bf2b013d54b4d4ba0aff4c
+    Server plugins:
+       00: Source.Python, (C) 2012-2018, Source.Python Team.
+    SP plugins:
+    --------------------------------------------------------
 
 4. If you validated the installation, you successfully installed Source.Python!
-5. Edit your server's ``../cfg/source-python/core_settings.ini`` file to your liking.
-6. If you are having problems installing Source.Python, please let us know on our `forums <http://forums.sourcepython.com/>`_!
+5. You can now edit your server's ``../cfg/source-python/core_settings.ini`` file to your liking.
+
+If you are having problems installing Source.Python (e. g. ``Unknown command "sp"``), please let us know on our `forums <http://forums.sourcepython.com/>`_!

From ea4a3aefad49b845b03b9331b39fa3660d1d5a86 Mon Sep 17 00:00:00 2001
From: Ayuto22 <ayuto@web.de>
Date: Wed, 7 Mar 2018 19:17:47 +0100
Subject: [PATCH 6/8] Replaced pre/post parameters by HookType_t

- Improved retrieval of m_nButtons.
- Updated log messages.
- Return "true" if hooking the function failed.
---
 src/core/sp_hooks.cpp | 37 ++++++++++++++-----------------------
 src/core/sp_hooks.h   | 11 +++++++----
 src/core/sp_main.cpp  |  2 +-
 3 files changed, 22 insertions(+), 28 deletions(-)

diff --git a/src/core/sp_hooks.cpp b/src/core/sp_hooks.cpp
index c3ddc50df..8117d0061 100644
--- a/src/core/sp_hooks.cpp
+++ b/src/core/sp_hooks.cpp
@@ -52,12 +52,11 @@ std::vector<IEntityHook*> g_EntityHooks;
 //---------------------------------------------------------------------------------
 // ISimpleEntityHook
 //---------------------------------------------------------------------------------
-ISimpleEntityHook::ISimpleEntityHook(const char* func_name, HookHandlerFn* hook_handler, bool pre, bool post)
+ISimpleEntityHook::ISimpleEntityHook(const char* func_name, HookHandlerFn* hook_handler, HookType_t hook_type)
 {
 	this->func_name = func_name;
 	this->hook_handler = hook_handler;
-	this->pre = pre;
-	this->post = post;
+	this->hook_type = hook_type;
 }
 
 bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
@@ -73,7 +72,7 @@ bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
 		return false;
 	}
 	
-	PythonLog(3, "Initializing core hook (%s)...", this->func_name);
+	PythonLog(4, "Initializing core hook (%s)...", this->func_name);
 
 	CFunction* func = NULL;
 	try
@@ -88,8 +87,7 @@ bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
 		PyErr_Print();
 		PyErr_Clear();
 
-		PythonLog(0, "Failed to initialize the core hook.");
-
+		PythonLog(0, "Failed to import entities.entity.Entity or to retrieve %s.", this->func_name);
 		return true;
 	}
 
@@ -102,24 +100,14 @@ bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
 
 		if (!pHook)
 		{
-			PythonLog(0, "Could create a hook.");
-			return false;
+			PythonLog(0, "Could not create a hook for %s.", this->func_name);
+			return true;
 		}
 	}
 
-	if (this->pre)
-	{
-		PythonLog(4, "Adding a pre hook.");
-		pHook->AddCallback(HOOKTYPE_PRE, this->hook_handler);
-	}
-
-	if (this->post)
-	{
-		PythonLog(4, "Adding a post hook.");
-		pHook->AddCallback(HOOKTYPE_POST, this->hook_handler);
-	}
+	pHook->AddCallback(this->hook_type, this->hook_handler);
 	
-	PythonLog(3, "Core hook has been initialized.");
+	PythonLog(3, "Core hook (%s) has been initialized.", this->func_name);
 	return true;
 }
 
@@ -127,8 +115,8 @@ bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
 //---------------------------------------------------------------------------------
 // PlayerHook
 //---------------------------------------------------------------------------------
-PlayerHook::PlayerHook(const char* func_name, HookHandlerFn* hook_handler, bool pre, bool post)
-	:ISimpleEntityHook(func_name, hook_handler, pre, post)
+PlayerHook::PlayerHook(const char* func_name, HookHandlerFn* hook_handler, HookType_t hook_type)
+	:ISimpleEntityHook(func_name, hook_handler, hook_type)
 {
 }
 
@@ -205,7 +193,10 @@ bool PrePlayerRunCommand(HookType_t hook_type, CHook* pHook)
 
 	if (button_state_manager->GetCount())
 	{
-		int buttons = extract<int>(player.attr("buttons"));
+		CBaseEntityWrapper* pWrapper = (CBaseEntityWrapper*) pEntity;
+		static int offset = pWrapper->FindDatamapPropertyOffset("m_nButtons");
+
+		int buttons = pWrapper->GetDatamapPropertyByOffset<int>(offset);
 		if (buttons != pCmd->buttons)
 		{
 			CALL_LISTENERS(OnButtonStateChanged, player, buttons, pCmd->buttons);
diff --git a/src/core/sp_hooks.h b/src/core/sp_hooks.h
index d254982a5..94af24612 100644
--- a/src/core/sp_hooks.h
+++ b/src/core/sp_hooks.h
@@ -44,6 +44,10 @@ class CBaseEntity;
 class IEntityHook
 {
 public:
+	/* This method is called for every existing entity and all future entities
+	*  that are created. If true is returned, this object is removed from the
+	*  queue and deleted.
+	*/
 	virtual bool Initialize(CBaseEntity* pEntity) = NULL;
 };
 
@@ -55,7 +59,7 @@ extern std::vector<IEntityHook*> g_EntityHooks;
 class ISimpleEntityHook: public IEntityHook
 {
 public:
-	ISimpleEntityHook(const char* func_name, HookHandlerFn* hook_handler, bool pre, bool post);
+	ISimpleEntityHook(const char* func_name, HookHandlerFn* hook_handler, HookType_t hook_type);
 	
 	virtual bool Initialize(CBaseEntity* pEntity);
 	virtual bool Test(CBaseEntity* pEntity) = NULL;
@@ -63,8 +67,7 @@ class ISimpleEntityHook: public IEntityHook
 public:
 	const char* func_name;
 	HookHandlerFn* hook_handler;
-	bool pre;
-	bool post;
+	HookType_t hook_type;
 };
 
 //---------------------------------------------------------------------------------
@@ -73,7 +76,7 @@ class ISimpleEntityHook: public IEntityHook
 class PlayerHook: public ISimpleEntityHook
 {
 public:
-	PlayerHook(const char* func_name, HookHandlerFn* hook_handler, bool pre, bool post);
+	PlayerHook(const char* func_name, HookHandlerFn* hook_handler, HookType_t hook_type);
 
 	virtual bool Test(CBaseEntity* pEntity);
 };
diff --git a/src/core/sp_main.cpp b/src/core/sp_main.cpp
index 92df38832..0eca2e88a 100644
--- a/src/core/sp_main.cpp
+++ b/src/core/sp_main.cpp
@@ -358,7 +358,7 @@ bool CSourcePython::Load(	CreateInterfaceFn interfaceFactory, CreateInterfaceFn
 	g_EntityHooks.push_back(new PlayerHook(
 		"run_command",
 		(HookHandlerFn*) (void*) &PrePlayerRunCommand,
-		true, false));
+		HOOKTYPE_PRE));
 
 	InitHooks();
 	

From b46ec67b0b711610b84125f975fabd13759232ef Mon Sep 17 00:00:00 2001
From: Ayuto22 <ayuto@web.de>
Date: Wed, 7 Mar 2018 19:26:35 +0100
Subject: [PATCH 7/8] Also remove the hook if entity/index conversion fails

---
 src/core/sp_hooks.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/core/sp_hooks.cpp b/src/core/sp_hooks.cpp
index 8117d0061..75a305999 100644
--- a/src/core/sp_hooks.cpp
+++ b/src/core/sp_hooks.cpp
@@ -66,13 +66,14 @@ bool ISimpleEntityHook::Initialize(CBaseEntity* pEntity)
 		return false;
 	}
 
+	PythonLog(4, "Initializing core hook (%s)...", this->func_name);
+
 	unsigned int index;
 	if (!IndexFromBaseEntity(pEntity, index))
 	{
-		return false;
+		PythonLog(0, "Failed to convert the entity pointer to an index (%s)", this->func_name);
+		return true;
 	}
-	
-	PythonLog(4, "Initializing core hook (%s)...", this->func_name);
 
 	CFunction* func = NULL;
 	try

From 1a50c6492499bb966521ee2d17336f50f94fb2c5 Mon Sep 17 00:00:00 2001
From: Ayuto22 <ayuto@web.de>
Date: Wed, 7 Mar 2018 19:53:06 +0100
Subject: [PATCH 8/8] Fixed Linux compile errors

---
 src/core/sp_hooks.cpp | 1 +
 src/core/sp_hooks.h   | 5 +++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/core/sp_hooks.cpp b/src/core/sp_hooks.cpp
index 75a305999..91dddf374 100644
--- a/src/core/sp_hooks.cpp
+++ b/src/core/sp_hooks.cpp
@@ -33,6 +33,7 @@ using namespace boost::python;
 
 // SDK
 #include "edict.h"
+#include "game/shared/shareddefs.h"
 #include "game/shared/usercmd.h"
 
 // Source.Python
diff --git a/src/core/sp_hooks.h b/src/core/sp_hooks.h
index 94af24612..417e3b00f 100644
--- a/src/core/sp_hooks.h
+++ b/src/core/sp_hooks.h
@@ -48,7 +48,8 @@ class IEntityHook
 	*  that are created. If true is returned, this object is removed from the
 	*  queue and deleted.
 	*/
-	virtual bool Initialize(CBaseEntity* pEntity) = NULL;
+	virtual bool Initialize(CBaseEntity* pEntity) = 0;
+	virtual ~IEntityHook() {}
 };
 
 extern std::vector<IEntityHook*> g_EntityHooks;
@@ -62,7 +63,7 @@ class ISimpleEntityHook: public IEntityHook
 	ISimpleEntityHook(const char* func_name, HookHandlerFn* hook_handler, HookType_t hook_type);
 	
 	virtual bool Initialize(CBaseEntity* pEntity);
-	virtual bool Test(CBaseEntity* pEntity) = NULL;
+	virtual bool Test(CBaseEntity* pEntity) = 0;
 
 public:
 	const char* func_name;
