From 13ff1be75aae8c9f98ceee4c0f8620234e63095d Mon Sep 17 00:00:00 2001
From: maybegreat48 <96936658+maybegreat48@users.noreply.github.com>
Date: Tue, 3 Jan 2023 16:48:32 +0000
Subject: [PATCH] Reactions, remote kick and better protections (#807)

* feat(Scripts): Complete GlobalPlayerBD
* feat(Scripts): add GPBD_Kicking
* feat(Scripts): start work on GPBD_FM_3
* feat(Scripts): add more to GPBD_FM_3
* feat(Scripts): complete GPBD_FM_3
* feat(Scripts): start work on GPBD_FM
* feat(Scripts): improve GPBD_FM
* feat(Scripts): complete GPBD_FM
* feat(Reactions): Add reactions
* feat(Protections): Improve protections
* feat(RemoteKick): Add remote kick
* feat(Stats): add KillsOnPlayers and DeathsByPlayers
* fix(Classes): Fix compiler warnings
---
 scripts/gtav-classes.cmake                    |   2 +-
 src/backend/backend.cpp                       |   1 +
 .../commands/player/kick/bail_kick.cpp        |   4 +-
 .../player/kick/gamer_instruction_kick.cpp    |  47 ++++
 .../player/kick/lost_connection_kick.cpp      |   2 +-
 .../commands/player/toxic/explode_player.cpp  |  24 ++
 .../player/toxic/set_wanted_level.cpp         |  12 +-
 src/backend/looped/looped.hpp                 |   1 +
 src/backend/looped/player/good_options.cpp    |   2 +-
 src/backend/looped/player/toxic_options.cpp   |  52 ++++
 src/backend/reactions/interloper_reaction.cpp |  37 +++
 src/backend/reactions/interloper_reaction.hpp |  21 ++
 src/backend/reactions/reaction.cpp            |  76 ++++++
 src/backend/reactions/reaction.hpp            |  30 +++
 src/backend/script_patches.hpp                |   2 +-
 src/common.hpp                                |  11 +
 src/core/enums.hpp                            |  67 +----
 src/core/globals.hpp                          | 127 +++++-----
 src/function_types.hpp                        |  13 +-
 src/gta/array.hpp                             |   5 -
 src/gta/base.hpp                              |   4 -
 src/gta/enums.hpp                             | 220 -----------------
 src/gta/extensible.hpp                        |   7 -
 src/gta/natives.hpp                           |  22 +-
 src/gta/node_list.hpp                         |   2 +-
 src/gta/player.hpp                            |  11 -
 src/gta/ref_aware.hpp                         |   4 -
 src/gta/vector.hpp                            |  94 -------
 src/gta_util.hpp                              |   2 +-
 src/gui.cpp                                   |   1 -
 src/gui/components/components.hpp             |  11 -
 src/hooking.cpp                               |   2 -
 src/hooking.hpp                               |   3 +-
 .../misc/start_matchmaking_find_sessions.cpp  |   9 +-
 .../protections/increment_stat_event.cpp      |  10 +-
 src/hooks/protections/receive_net_message.cpp | 102 +++++---
 src/hooks/protections/received_event.cpp      |  43 +---
 .../protections/script_event_handler.cpp      | 233 +++++++++---------
 .../protections/update_presence_attribute.cpp |   6 +
 src/natives.hpp                               |   1 -
 src/packet.cpp                                |  12 +
 src/packet.hpp                                |   2 +
 src/pointers.cpp                              |  26 +-
 src/pointers.hpp                              |   6 +-
 src/script_mgr.cpp                            |   1 -
 src/services/gui/gui_service.hpp              |   4 +-
 .../matchmaking/matchmaking_service.cpp       |   6 +-
 src/services/players/player.hpp               |  11 +
 src/util/notify.hpp                           |  18 +-
 src/util/session.hpp                          | 155 +++++++++++-
 src/util/toxic.hpp                            |   2 +
 src/views/network/view_player_database.cpp    |   5 +
 src/views/network/view_session.cpp            |  18 +-
 src/views/players/player/player_info.cpp      |  34 +++
 src/views/players/player/player_kick.cpp      |   1 +
 src/views/players/player/player_misc.cpp      |  10 -
 src/views/players/player/player_toxic.cpp     |  14 ++
 .../settings/view_notification_settings.cpp   | 111 ---------
 .../settings/view_protection_settings.cpp     |  29 +--
 src/views/settings/view_reaction_settings.cpp | 126 ++++++++++
 src/views/view.hpp                            |   2 +-
 src/views/world/view_spawn_ped.cpp            |   5 +-
 62 files changed, 1039 insertions(+), 882 deletions(-)
 create mode 100644 src/backend/commands/player/kick/gamer_instruction_kick.cpp
 create mode 100644 src/backend/commands/player/toxic/explode_player.cpp
 create mode 100644 src/backend/looped/player/toxic_options.cpp
 create mode 100644 src/backend/reactions/interloper_reaction.cpp
 create mode 100644 src/backend/reactions/interloper_reaction.hpp
 create mode 100644 src/backend/reactions/reaction.cpp
 create mode 100644 src/backend/reactions/reaction.hpp
 delete mode 100644 src/gta/array.hpp
 delete mode 100644 src/gta/base.hpp
 delete mode 100644 src/gta/extensible.hpp
 delete mode 100644 src/gta/player.hpp
 delete mode 100644 src/gta/ref_aware.hpp
 delete mode 100644 src/gta/vector.hpp
 delete mode 100644 src/views/settings/view_notification_settings.cpp
 create mode 100644 src/views/settings/view_reaction_settings.cpp

diff --git a/scripts/gtav-classes.cmake b/scripts/gtav-classes.cmake
index 5b5707a7..77a71735 100644
--- a/scripts/gtav-classes.cmake
+++ b/scripts/gtav-classes.cmake
@@ -3,7 +3,7 @@ include(FetchContent)
 FetchContent_Declare(
     gtav_classes
     GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
-    GIT_TAG        985d0dcc4042ffb24dcb18c34ad5f42b10232510
+    GIT_TAG        a4a559be1abd84ffbd4d127c572cce036a8cc5ad
     GIT_PROGRESS TRUE
     CONFIGURE_COMMAND ""
     BUILD_COMMAND ""
diff --git a/src/backend/backend.cpp b/src/backend/backend.cpp
index 4bb3c5fe..b6fd033a 100644
--- a/src/backend/backend.cpp
+++ b/src/backend/backend.cpp
@@ -117,6 +117,7 @@ namespace big
 		while (g_running)
 		{
 			looped::player_good_options();
+			looped::player_toxic_options();
 			looped::player_spectate();
 			looped::player_remote_control_vehicle();
 
diff --git a/src/backend/commands/player/kick/bail_kick.cpp b/src/backend/commands/player/kick/bail_kick.cpp
index 17944907..05be91b1 100644
--- a/src/backend/commands/player/kick/bail_kick.cpp
+++ b/src/backend/commands/player/kick/bail_kick.cpp
@@ -3,6 +3,8 @@
 #include "pointers.hpp"
 #include "core/scr_globals.hpp"
 
+#include <script/globals/GPBD_FM_3.hpp>
+
 namespace big
 {
 	class bail_kick : player_command
@@ -21,7 +23,7 @@ namespace big
 			{
 				(int64_t)eRemoteEvent::NetworkBail,
 				(int64_t)self::id,
-				*scr_globals::gpbd_fm_3.at(player->id(), scr_globals::size::gpbd_fm_3).at(510).as<int64_t*>()
+				scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[player->id()].ScriptEventReplayProtectionCounter
 			};
 
 			g_pointers->m_trigger_script_event(1, args, arg_count, 1 << player->id());
diff --git a/src/backend/commands/player/kick/gamer_instruction_kick.cpp b/src/backend/commands/player/kick/gamer_instruction_kick.cpp
new file mode 100644
index 00000000..59d478f4
--- /dev/null
+++ b/src/backend/commands/player/kick/gamer_instruction_kick.cpp
@@ -0,0 +1,47 @@
+#include "backend/player_command.hpp"
+#include "natives.hpp"
+#include "pointers.hpp"
+#include "core/scr_globals.hpp"
+#include "packet.hpp"
+#include "gta_util.hpp"
+
+#include <network/Network.hpp>
+
+namespace big
+{
+	class gamer_instruction_kick : player_command
+	{
+		using player_command::player_command;
+
+		void gamer_handle_serialize(rage::rlGamerHandle& hnd, rage::datBitBuffer& buf)
+		{
+			buf.Write<uint8_t>(*reinterpret_cast<uint8_t*>(&hnd.m_platform), 8);
+			if (*reinterpret_cast<uint8_t*>(&hnd.m_platform) == 3)
+			{
+				buf.WriteInt64(*(int64_t*)&hnd.m_rockstar_id, 64);
+				buf.Write<uint8_t>(*reinterpret_cast<uint8_t*>(reinterpret_cast<__int64>(&hnd) + 9), 8);
+			}
+		}
+
+		virtual CommandAccessLevel get_access_level()
+		{
+			return CommandAccessLevel::TOXIC;
+		}
+
+		virtual void execute(player_ptr player, const std::vector<std::uint64_t>& _args, const std::shared_ptr<command_context> ctx)
+		{
+			packet msg;
+			msg.write_message(rage::eNetMessage::MsgTransitionGamerInstruction);
+			gamer_handle_serialize(g_player_service->get_selected()->get_net_data()->m_gamer_handle_2, msg.m_buffer); // src
+			gamer_handle_serialize(g_player_service->get_selected()->get_net_data()->m_gamer_handle_2, msg.m_buffer); // target
+			msg.write<bool>(false, 1); // string extend
+			msg.write<int>(0, 7); // string length
+			msg.write<int>(8, 32); // instruction type
+			msg.write<int>(0, 32);
+			msg.write<int>(0, 32);
+			msg.send(g_player_service->get_selected(), gta_util::get_network()->m_transition_session.m_connection_identifier);
+		}
+	};
+
+	gamer_instruction_kick g_gamer_instruction_kick("gikick", "Gamer Instruction Kick", "This is unlikely to kick menus that block NETWORK_BAIL", 0);
+}
\ No newline at end of file
diff --git a/src/backend/commands/player/kick/lost_connection_kick.cpp b/src/backend/commands/player/kick/lost_connection_kick.cpp
index b14074e5..4e6d6844 100644
--- a/src/backend/commands/player/kick/lost_connection_kick.cpp
+++ b/src/backend/commands/player/kick/lost_connection_kick.cpp
@@ -44,7 +44,7 @@ namespace big
 			{
 				if (plyr->is_host())
 				{
-					msg.send(plyr->get_session_player()->m_msg_id);
+					msg.send(plyr, gta_util::get_network()->m_game_session_ptr->m_connection_identifier);
 					break;
 				}
 			}
diff --git a/src/backend/commands/player/toxic/explode_player.cpp b/src/backend/commands/player/toxic/explode_player.cpp
new file mode 100644
index 00000000..74e68407
--- /dev/null
+++ b/src/backend/commands/player/toxic/explode_player.cpp
@@ -0,0 +1,24 @@
+#include "backend/player_command.hpp"
+#include "natives.hpp"
+#include "pointers.hpp"
+#include "util/toxic.hpp"
+
+namespace big
+{
+	class explode_player : player_command
+	{
+		using player_command::player_command;
+
+		virtual CommandAccessLevel get_access_level()
+		{
+			return CommandAccessLevel::AGGRESSIVE;
+		}
+
+		virtual void execute(player_ptr player, const std::vector<std::uint64_t>& _args, const std::shared_ptr<command_context> ctx)
+		{
+			toxic::blame_explode_player(player, player, EXP_TAG_SUBMARINE_BIG, 9999.0f, true, false, 9999.0f);
+		}
+	};
+
+	explode_player g_explode_player("explode", "Explode Player", "Blows the player up", 0);
+}
\ No newline at end of file
diff --git a/src/backend/commands/player/toxic/set_wanted_level.cpp b/src/backend/commands/player/toxic/set_wanted_level.cpp
index a651dd5c..e27422f6 100644
--- a/src/backend/commands/player/toxic/set_wanted_level.cpp
+++ b/src/backend/commands/player/toxic/set_wanted_level.cpp
@@ -5,6 +5,8 @@
 #include "util/globals.hpp"
 #include "script.hpp"
 
+#include <script/globals/GlobalPlayerBD.hpp>
+
 namespace big
 {
 	class set_wanted_level : player_command
@@ -50,14 +52,16 @@ namespace big
 
 				if (_args[0] > 0)
 				{
-					*scr_globals::globalplayer_bd.at(self::id, scr_globals::size::globalplayer_bd).at(214).as<Player*>() = id;
-					*scr_globals::globalplayer_bd.at(self::id, scr_globals::size::globalplayer_bd).at(215).as<int*>() = _args[0];
+					auto& gpbd = scr_globals::globalplayer_bd.as<GlobalPlayerBD*>()->Entries[self::id];
+
+					gpbd.RemoteWantedLevelPlayer = id;
+					gpbd.RemoteWantedLevelAmount = _args[0];
 
 					for (int i = 0; PLAYER::GET_PLAYER_WANTED_LEVEL(id) < _args[0] && i < 3600; i++)
 						script::get_current()->yield(1ms);
 
-					*scr_globals::globalplayer_bd.at(self::id, scr_globals::size::globalplayer_bd).at(214).as<Player*>() = -1; // reset to prevent wanted from being constantly set
-					*scr_globals::globalplayer_bd.at(self::id, scr_globals::size::globalplayer_bd).at(215).as<int*>() = -1;
+					gpbd.RemoteWantedLevelPlayer = -1;
+					gpbd.RemoteWantedLevelAmount = -1;
 				}
 			}
 		}
diff --git a/src/backend/looped/looped.hpp b/src/backend/looped/looped.hpp
index c8680bc9..78729688 100644
--- a/src/backend/looped/looped.hpp
+++ b/src/backend/looped/looped.hpp
@@ -11,6 +11,7 @@ namespace big
 		static void hud_transition_state();
 
 		static void player_good_options();
+		static void player_toxic_options();
 		static void player_spectate();
 		static void player_remote_control_vehicle();
 
diff --git a/src/backend/looped/player/good_options.cpp b/src/backend/looped/player/good_options.cpp
index 5a99c5e3..7d71d6f1 100644
--- a/src/backend/looped/player/good_options.cpp
+++ b/src/backend/looped/player/good_options.cpp
@@ -11,7 +11,7 @@ namespace big
 	static int neverWantedPlayer = 0;
 	void looped::player_good_options()
 	{
-		if (!NETWORK::NETWORK_IS_SESSION_STARTED())
+		if (!*g_pointers->m_is_session_started)
 			return;
 
 		offRadarPlayer++;
diff --git a/src/backend/looped/player/toxic_options.cpp b/src/backend/looped/player/toxic_options.cpp
new file mode 100644
index 00000000..5ec14df0
--- /dev/null
+++ b/src/backend/looped/player/toxic_options.cpp
@@ -0,0 +1,52 @@
+#include "gta/pickup_rewards.hpp"
+#include "backend/looped/looped.hpp"
+#include "services/players/player_service.hpp"
+#include "util/globals.hpp"
+#include "util/misc.hpp"
+#include "util/toxic.hpp"
+
+namespace big
+{
+	void looped::player_toxic_options()
+	{
+		if (!*g_pointers->m_is_session_started)
+			return;
+
+		int rotate_cam_bits = 0;
+
+		g_player_service->iterate([&rotate_cam_bits](const player_entry& entry)
+		{
+			if (g_player_service->get_self()->get_ped() && entry.second->get_ped() && entry.second->get_ped()->m_health > 0)
+			{
+				if (entry.second->kill_loop && !(entry.second->get_ped()->m_damage_bits & (1 << 8)))
+					g_pointers->m_send_network_damage((CEntity*)g_player_service->get_self()->get_ped(), (CEntity*)entry.second->get_ped(), entry.second->get_ped()->m_navigation->get_position(),
+						0, true, RAGE_JOAAT("weapon_explosion"), 10000.0f, 2, 0, (1 << 4), 0, 0, 0, false, false, true, true, nullptr);
+
+				if (entry.second->explosion_loop)
+					toxic::blame_explode_player(entry.second, entry.second, EXP_TAG_SUBMARINE_BIG, 9999.0f, true, false, 9999.0f);
+
+				if (entry.second->freeze_loop)
+					TASK::CLEAR_PED_TASKS_IMMEDIATELY(PLAYER::GET_PLAYER_PED_SCRIPT_INDEX(entry.second->id()));
+
+				if (entry.second->ragdoll_loop && entry.second->get_ped()->m_net_object)
+					g_pointers->m_request_ragdoll(entry.second->get_ped()->m_net_object->m_object_id);
+
+				if (entry.second->rotate_cam_loop)
+					rotate_cam_bits |= (1 << entry.second->id());
+			}
+
+			if (rotate_cam_bits)
+			{
+				const size_t arg_count = 3;
+				int64_t args[arg_count] =
+				{
+					(int64_t)eRemoteEvent::TSECommand,
+					(int64_t)self::id,
+					(int64_t)eRemoteEvent::TSECommandRotateCam
+				};
+
+				g_pointers->m_trigger_script_event(1, args, arg_count, rotate_cam_bits);
+			}
+		});
+	}
+}
diff --git a/src/backend/reactions/interloper_reaction.cpp b/src/backend/reactions/interloper_reaction.cpp
new file mode 100644
index 00000000..5dbecf40
--- /dev/null
+++ b/src/backend/reactions/interloper_reaction.cpp
@@ -0,0 +1,37 @@
+#include "interloper_reaction.hpp"
+#include "backend/player_command.hpp"
+#include "fiber_pool.hpp"
+#include "script.hpp"
+#include "hooking.hpp"
+#include "pointers.hpp"
+#include "util/notify.hpp"
+
+namespace big
+{
+	interloper_reaction::interloper_reaction(const char* event_name, const char* notify_message, const char* announce_message, bool blockable, bool karmaable) :
+		reaction(event_name, notify_message, announce_message),
+		m_blockable(blockable),
+		m_karmaable(karmaable)
+	{
+	}
+
+	void interloper_reaction::process(player_ptr attacker, player_ptr victim)
+	{
+		if (!attacker->is_valid() || !victim->is_valid())
+			return;
+
+		if (announce_in_chat)
+		{
+			g_fiber_pool->queue_job([attacker, victim, this]
+			{
+				char chat[255];
+				snprintf(chat, sizeof(chat), std::format("{} {}", g.session.chat_output_prefix, m_announce_message).data(), attacker->get_name(), victim->get_name());
+
+				if (g_hooking->get_original<hooks::send_chat_message>()(*g_pointers->m_send_chat_ptr, g_player_service->get_self()->get_net_data(), chat, false))
+					notify::draw_chat(chat, g_player_service->get_self()->get_name(), false);
+			});
+		}
+
+		process_common(attacker);
+	}
+}
\ No newline at end of file
diff --git a/src/backend/reactions/interloper_reaction.hpp b/src/backend/reactions/interloper_reaction.hpp
new file mode 100644
index 00000000..f423641a
--- /dev/null
+++ b/src/backend/reactions/interloper_reaction.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include "reaction.hpp"
+
+namespace big
+{
+	class interloper_reaction : public reaction
+	{
+	public:
+		interloper_reaction(const char* event_name, const char* notify_message, const char* announce_message, bool blockable, bool karmaable);
+
+		bool block = true;
+		bool karma = false;
+
+		bool m_blockable;
+		bool m_karmaable;
+
+		NLOHMANN_DEFINE_TYPE_INTRUSIVE(interloper_reaction, announce_in_chat, notify, log, add_to_player_db, block_joins, kick, block, karma) // json doesn't serialize parent fields automatically
+
+		virtual void process(player_ptr attacker, player_ptr victim);
+	};
+}
\ No newline at end of file
diff --git a/src/backend/reactions/reaction.cpp b/src/backend/reactions/reaction.cpp
new file mode 100644
index 00000000..bdc65fde
--- /dev/null
+++ b/src/backend/reactions/reaction.cpp
@@ -0,0 +1,76 @@
+#include "reaction.hpp"
+#include "backend/player_command.hpp"
+#include "services/player_database/player_database_service.hpp"
+#include "fiber_pool.hpp"
+#include "script.hpp"
+#include "hooking.hpp"
+#include "pointers.hpp"
+#include "util/notify.hpp"
+
+namespace big
+{
+	reaction::reaction(const char* event_name, const char* notify_message, const char* announce_message) :
+		m_event_name(event_name),
+		m_notify_message(notify_message),
+		m_announce_message(announce_message)
+	{
+	}
+
+	void reaction::process_common(player_ptr player)
+	{
+		if (notify)
+		{
+			char notification[500]{}; // I don't like using sprintf but there isn't an alternative afaik
+			snprintf(notification, sizeof(notification), m_notify_message, player->get_name());
+			g_notification_service->push_warning("Protections", notification);
+		}
+
+		if (log)
+		{
+			uint64_t rockstar_id = player->get_net_data() == nullptr ? 0 : player->get_net_data()->m_gamer_handle_2.m_rockstar_id;
+			LOG(WARNING) << std::format("Received {} from {} ({})", m_event_name, player->get_name(), rockstar_id);
+		}
+
+		if (add_to_player_db)
+		{
+			auto entry = g_player_database_service->get_or_create_player(player);
+
+			if (block_joins)
+			{
+				entry->block_join = true;
+				g_player_database_service->save();
+			}
+		}
+
+		if (kick)
+		{
+			g_fiber_pool->queue_job([player]
+			{
+				((player_command*)command::get(RAGE_JOAAT("bailkick")))->call(player, {});
+				((player_command*)command::get(RAGE_JOAAT("nfkick")))->call(player, {});
+				script::get_current()->yield(700ms);
+				((player_command*)command::get(RAGE_JOAAT("breakup")))->call(player, {});
+			});
+		}
+	}
+
+	void reaction::process(player_ptr player)
+	{
+		if (!player->is_valid())
+			return;
+
+		if (announce_in_chat)
+		{
+			g_fiber_pool->queue_job([player, this]
+			{
+				char chat[255];
+				snprintf(chat, sizeof(chat), std::format("{} {}", g.session.chat_output_prefix, m_announce_message).data(), player->get_name());
+
+				if (g_hooking->get_original<hooks::send_chat_message>()(*g_pointers->m_send_chat_ptr, g_player_service->get_self()->get_net_data(), chat, false))
+					notify::draw_chat(chat, g_player_service->get_self()->get_name(), false);
+			});
+		}
+
+		process_common(player);
+	}
+}
\ No newline at end of file
diff --git a/src/backend/reactions/reaction.hpp b/src/backend/reactions/reaction.hpp
new file mode 100644
index 00000000..d7912d1a
--- /dev/null
+++ b/src/backend/reactions/reaction.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+namespace big
+{
+	class player;
+	using player_ptr = std::shared_ptr<player>;
+
+	class reaction
+	{
+	protected:
+		void process_common(player_ptr player);
+
+	public:
+		bool announce_in_chat = false;
+		bool notify = true;
+		bool log = false;
+		bool add_to_player_db = false;
+		bool block_joins = false;
+		bool kick = false;
+
+		const char* m_event_name;
+		const char* m_notify_message;
+		const char* m_announce_message;
+
+		NLOHMANN_DEFINE_TYPE_INTRUSIVE(reaction, announce_in_chat, notify, log, add_to_player_db, block_joins, kick)
+
+		reaction(const char* event_name, const char* notify_message, const char* announce_message);
+		virtual void process(player_ptr player);
+	};
+}
\ No newline at end of file
diff --git a/src/backend/script_patches.hpp b/src/backend/script_patches.hpp
index b8073526..e436287e 100644
--- a/src/backend/script_patches.hpp
+++ b/src/backend/script_patches.hpp
@@ -7,7 +7,7 @@ namespace big
 	void register_script_patches()
 	{
 		g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 01 08 00 ? 38 00 5D ? ? ? 2A 06", 5, {0x71, 0x2E, 0x01, 0x01}, &g.session.decloak_players });
-		g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 01 04 00 ? 2C ? ? ? 5D ? ? ? 71 57 ? ? 2C", 5, { 0x2E, 0x01, 0x00 }, &g.protections.script_host_kick });
+		g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 01 04 00 ? 2C ? ? ? 5D ? ? ? 71 57 ? ? 2C", 5, { 0x2E, 0x01, 0x00 }, nullptr }); // script host kick
 		g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 00 07 00 00 5D ? ? ? 56 ? ? 71", 5, { 0x2E, 0x00, 0x00 }, &g.tunables.no_idle_kick });
 		g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "5D ? ? ? 76 57 ? ? 5D ? ? ? 76", 0, { 0x2E, 0x00, 0x00 }, nullptr }); // end session kick protection
 		g_script_patcher_service->add_patch({ RAGE_JOAAT("freemode"), "2D 01 09 00 00 5D ? ? ? 56 ? ? 2E", 5, { 0x2E, 0x01, 0x00 }, nullptr }); // disable death when undermap/spectating
diff --git a/src/common.hpp b/src/common.hpp
index 5e3bd985..ea87afff 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -77,4 +77,15 @@ namespace self
 	inline Vehicle veh;
 }
 
+template<size_t N>
+struct template_str
+{
+	constexpr template_str(const char(&str)[N])
+	{
+		std::copy_n(str, N, value);
+	}
+
+	char value[N];
+};
+
 #endif
diff --git a/src/core/enums.hpp b/src/core/enums.hpp
index 27a6a237..d1630bf4 100644
--- a/src/core/enums.hpp
+++ b/src/core/enums.hpp
@@ -157,7 +157,6 @@ namespace big
 		CeoMoney = 245065909, // Goon_Paid_Large
 		ClearWantedLevel = 2080651008,
 		ForceMission = 1858712297, // ), Var0.f_2, 1))
-		ForceMission2 = -1578682814, // TICK_PH_INVA
 		GiveCollectible = 697566862, // DLC_SUM20_HIDDEN_COLLECTIBLES xref
 		GtaBanner = -795380017, // NETWORK::NETWORK_IS_SCRIPT_ACTIVE("BUSINESS_BATTLES", -1, true, 0) second one
 		NetworkBail = 915462795, // NETWORK::NETWORK_BAIL(16, 0, 0); xref func
@@ -165,12 +164,12 @@ namespace big
 		RemoteOffradar = -162943635, // NETWORK::GET_TIME_DIFFERENCE(NETWORK::GET_NETWORK_TIME(), Var0.f_2)
 		SendToCutscene = 392606458, // (bVar3, bVar4, 125f, 1)
 		SendToCayoPerico = -910497748, // CELL_HI_INV
-		SendToLocation = 1214823473, // &Var222, 11);
+		SendToLocation = 1214823473, // &Var222, 11); TODO this isn't updated!
 		SoundSpam = -1891171016, // CELL_APTINVYACHT
 		Spectate = -1903870031, // SPEC_TCK1
 		Teleport = -168599209, // Mission_Pass_Notify
 		TeleportToWarehouse = 434937615, // .f_4 == 50
-		TransactionError = -768108950, // NETWORK_RECEIVE_PLAYER_JOBSHARE_CASH
+		TransactionError = -492741651, // NETWORK_RECEIVE_PLAYER_JOBSHARE_CASH
 		VehicleKick = -852914485, // PIM_RFMOC
 		MCTeleport = 879177392, // NETWORK::NETWORK_HASH_FROM_PLAYER_HANDLE(PLAYER::PLAYER_ID()) == (first one)
 		StartActivity = 243072129, // (Var0.f_2, -1); first match
@@ -189,6 +188,8 @@ namespace big
 		NotificationMoneyBanked = -1032040118, // TICK_TC_BANK
 		NotificationMoneyRemoved = -1197151915, // TICK_TC_REMO
 		NotificationMoneyStolen = -28878294, // TICK_TC_STOL
+
+		DestroyPersonalVehicle = -513394492 // CnC_PV_THEFT
 	};
 
 	enum class eCollectibleType
@@ -205,66 +206,6 @@ namespace big
 		Skydive = 10
 	};
 
-	enum class eActivityType
-	{
-		HeistPrep = 233,
-		Gunrunning = 180,
-		Sightseer = 142,
-		HeadHunter = 166,
-		BuySpecialCargo = 167,
-		SellSpecialCargo = 168,
-		DefendSpecialCargo = 169,
-		StealVehicle = 178,
-		ExportVehicle = 188,
-		Gunrunning2 = 225,
-		GunrunningSell = 226,
-		GunrunningDefend = 227,
-		BikerSell = 190,
-		BikerDefend = 191,
-		BusinessResupply = 192,
-		Survival = 3,
-		Darts = 14,
-		ArmWresling = 15,
-		GangAttack = 6,
-		PilotSchool = 122,
-		Golf = 11,
-		ShootingRange = 13,
-		Tennis = 12,
-		BaseJump = 8,
-		Deathmatch = 1,
-		ImpromptuDeathmatch = 5,
-		Mission = 0,
-		Race = 2,
-		ExecutiveDeathmatch = 148,
-		MarkedForDeath = 151,
-		PiracyPrevention = 152,
-		MostWanted = 153,
-		AssetRecovery = 157,
-		HostileTakeover = 159,
-		Point2Point = 162,
-		AmphibiousAssault = 216,
-		Velocity = 219,
-		GunsForHire = 185,
-		ByThePound = 182,
-		RippingItUp = 194,
-		RaceToPoint = 189,
-		HitAndRide = 193,
-		CriminalMischief = 205,
-		WeaponOfChoice = 186,
-		FragileGoods = 207,
-		Torched = 208,
-		Outrider = 209,
-		WheelieRider = 210,
-		POW = 183,
-		ExecutiveSearch = 199,
-		StandYourGround = 201,
-		AutoBuyout = 163,
-		DueDiligence = 160,
-		MarketManipulation = 154,
-		CourierService = 155,
-		Skydive = 267
-	};
-
 	enum class eSessionType
 	{
 		JOIN_PUBLIC,
diff --git a/src/core/globals.hpp b/src/core/globals.hpp
index 94befd60..31648a64 100644
--- a/src/core/globals.hpp
+++ b/src/core/globals.hpp
@@ -4,6 +4,8 @@
 #include <weapon/CWeaponInfo.hpp>
 #include "enums.hpp"
 #include "file_manager.hpp"
+#include "backend/reactions/reaction.hpp"
+#include "backend/reactions/interloper_reaction.hpp"
 #include <imgui.h>
 #include <bitset>
 
@@ -101,56 +103,6 @@ namespace big
 				NLOHMANN_DEFINE_TYPE_INTRUSIVE(pair, log, notify)
 			};
 
-			struct received_event
-			{
-				pair clear_ped_task{};
-				pair kick_vote{};
-				pair report_cash_spawn{};
-				pair modder_detect{};
-				pair request_control_event{};
-				pair vehicle_temp_action{};
-
-				NLOHMANN_DEFINE_TYPE_INTRUSIVE(received_event, clear_ped_task, kick_vote, report_cash_spawn, modder_detect, request_control_event, vehicle_temp_action)
-			} received_event{};
-
-			struct script_event_handler
-			{
-				pair bounty{};
-				pair ceo_ban{};
-				pair ceo_kick{};
-				pair ceo_money{};
-				pair clear_wanted_level{};
-				pair crash{};
-				pair fake_deposit{};
-				pair force_mission{};
-				pair force_teleport{};
-				pair gta_banner{};
-				pair mc_teleport{};
-				pair network_bail{};
-				pair personal_vehicle_destroyed{};
-				pair remote_off_radar{};
-				pair rotate_cam{};
-				pair send_to_cutscene{};
-				pair send_to_location{};
-				pair sound_spam{};
-				pair spectate{};
-				pair switch_player_model{};
-				pair transaction_error{};
-				pair tse_freeze{};
-				pair tse_sender_mismatch{};
-				pair vehicle_kick{};
-				pair teleport_to_warehouse{};
-				pair start_activity{};
-				pair null_function_kick{};
-				pair send_sms{};
-
-				NLOHMANN_DEFINE_TYPE_INTRUSIVE(script_event_handler,
-					bounty, ceo_ban, ceo_kick, clear_wanted_level, crash, fake_deposit, force_mission, force_teleport,
-					gta_banner, mc_teleport, network_bail, personal_vehicle_destroyed, remote_off_radar, rotate_cam,
-					send_to_cutscene, send_to_location, sound_spam, spectate, switch_player_model, transaction_error,
-					tse_freeze, tse_sender_mismatch, vehicle_kick, teleport_to_warehouse, start_activity, null_function_kick, send_sms)
-			} script_event_handler{};
-
 			pair gta_thread_kill{};
 			pair gta_thread_start{};
 
@@ -165,9 +117,8 @@ namespace big
 
 				NLOHMANN_DEFINE_TYPE_INTRUSIVE(player_join, above_map, log, notify)
 			} player_join{};
-			pair player_leave{};
 
-			pair reports{};
+			pair player_leave{};
 
 			pair send_net_info_to_lobby{};
 			pair transaction_rate_limit{};
@@ -175,11 +126,57 @@ namespace big
 			pair out_of_allowed_range_sync_type{};
 			pair invalid_sync{};
 
-			NLOHMANN_DEFINE_TYPE_INTRUSIVE(notifications,
-				received_event, script_event_handler, gta_thread_kill, gta_thread_start, network_player_mgr_init,
-				network_player_mgr_shutdown, player_join, player_leave, reports, send_net_info_to_lobby,
+			NLOHMANN_DEFINE_TYPE_INTRUSIVE(notifications, gta_thread_kill, gta_thread_start, network_player_mgr_init,
+				network_player_mgr_shutdown, player_join, player_leave, send_net_info_to_lobby,
 				transaction_rate_limit, mismatch_sync_type, out_of_allowed_range_sync_type, invalid_sync)
 		} notifications{};
+
+		struct reactions
+		{
+			reaction bounty{ "Bounty", "Blocked Bounty from %s", "%s tried to set a bounty on me!" };
+			reaction ceo_money{ "CEO Money", "Blocked CEO Money from %s", "%s tried to drop money on me!" };
+			reaction clear_wanted_level{ "Clear Wanted Level", "Blocked Clear Wanted Level from %s", "%s tried to clear my wanted level!" };
+			reaction crash{ "Crash", "Blocked Crash from %s", "%s tried to crash me!" };
+			reaction fake_deposit{ "Fake Deposit", "Blocked Fake Deposit from %s", "%s tried to show me a fake money notification!" };
+			reaction force_mission{ "Force Mission", "Blocked Force Mission from %s", "%s tried to force me into a mission!" };
+			reaction force_teleport{ "Force Teleport", "Blocked Force Teleport from %s", "%s tried to teleport me!" };
+			reaction gta_banner{ "GTA Banner", "Blocked GTA Banner from %s", "Blocked GTA Banner from %s" }; // please don't enable this
+			reaction mc_teleport{ "MC Teleport", "Blocked MC Teleport from %s", "%s tried to teleport me!" };
+			reaction network_bail{ "Network Bail", "Blocked Network Bail from %s", "%s tried to kick me out!" };
+			reaction personal_vehicle_destroyed{ "Personal Vehicle Destroyed", "Blocked Personal Vehicle Destroyed from %s", "%s tried to show me a fake insurance notification!" };
+			reaction remote_off_radar{ "Remote Off Radar", "Blocked Remote Off Radar from %s", "%s tried to give me off radar!" };
+			reaction rotate_cam{ "Rotate Cam", "Blocked Rotate Cam from %s", "%s tried to mess with my camera!" };
+			reaction send_to_cutscene{ "Send To Cutscene", "Blocked Send To Cutscene from %s", "%s tried to force me into a cutscene!" };
+			reaction send_to_location{ "Send To Location", "Blocked Send To Location from %s", "%s tried to send me to Cayo Perico!" };
+			reaction sound_spam{ "Sound Spam", "Blocked Sound Spam from %s", "%s tried to spam annoying sounds at me!" };
+			reaction spectate_notification{ "Spectate", "Blocked Spectate from %s", "Blocked Spectate from %s" };
+			reaction give_collectible{ "Give Collectible", "Blocked Give Collectible from %s", "%s tried to give me a collectible!" };
+			reaction transaction_error{ "Transaction Error", "Blocked Transaction Error from %s", "%s tried to show me a transaction error!" };
+			reaction tse_freeze{ "TSE Freeze", "Blocked TSE Freeze from %s", "%s tried to softlock my game!" };
+			reaction tse_sender_mismatch{ "TSE Sender Mismatch", "Blocked TSE Sender Mismatch from %s", "Blocked TSE Sender Mismatch from %s" };
+			reaction vehicle_kick{ "Vehicle Kick", "Blocked Vehicle Kick from %s", "%s tried to kick me from my vehicle!" };
+			reaction teleport_to_warehouse{ "Teleport To Warehouse", "Blocked Teleport To Warehouse from %s", "%s tried to teleport me to a warehouse!" };
+			reaction start_activity{ "Start Activity", "Blocked Start Activity from %s", "Blocked Start Activity from %s" };
+			reaction null_function_kick{ "Null Function Kick", "Blocked Null Function Kick from %s", "%s tried to kick me out!" };
+			reaction destroy_personal_vehicle{ "Destroy Personal Vehicle", "Blocked Destroy Personal Vehicle from %s", "%s tried to destroy my personal vehicle!" };
+
+			reaction clear_ped_tasks{ "Clear Ped Tasks", "Blocked Clear Ped Tasks from %s", "%s tried to freeze me!" };
+			reaction remote_ragdoll{ "Remote Ragdoll", "Blocked Remote Ragdoll from %s", "%s tried to ragdoll me!" };
+			reaction kick_vote{ "Kick Vote", "%s is voting to kick you!", "%s is voting to kick me!" };
+			reaction report_cash_spawn{ "Cash Spawn", "%s is spawning cash", "%s is spawning cash" };
+			reaction modder_detection{ "Modder Detection", "%s is detected as a modder by the anticheat", "%s is detected as a modder by the anticheat" };
+			reaction request_control_event{ "Request Control Event", "Blocked Request Control Event from %s", "%s tried to mess with my vehicle!" };
+			reaction report{ "Report", "Blocked Report from %s", "%s tried to report me!" };
+
+			interloper_reaction breakup_others{ "Breakup Kicks On Other Players", "%s is trying to breakup kick %s!", "%s is trying to breakup kick %s!", true, true }; // blockable only when host but we have no way to specify that atm
+			reaction lost_connection_kick{ "Lost Connection Kick", "Blocked Lost Connection Kick from %s", "%s tried to kick me out!" };
+			reaction gamer_instruction_kick{ "Gamer Instruction Kick", "Blocked Gamer Instruction Kick from %s", "%s tried to kick me out!" };
+			interloper_reaction lost_connection_kick_others{ "Lost Connection Kick On Other Players", "%s is trying to lost connection kick %s!", "%s is trying to lost connection kick %s!", true, false };
+
+			NLOHMANN_DEFINE_TYPE_INTRUSIVE(reactions, bounty, ceo_money, clear_wanted_level, crash, fake_deposit, force_mission, force_teleport, gta_banner, mc_teleport, network_bail, personal_vehicle_destroyed, remote_off_radar,
+				rotate_cam, send_to_cutscene, send_to_location, sound_spam, spectate_notification, give_collectible, transaction_error, tse_freeze, tse_sender_mismatch, vehicle_kick, teleport_to_warehouse, start_activity,
+				null_function_kick, destroy_personal_vehicle, clear_ped_tasks, remote_ragdoll, kick_vote, report_cash_spawn, modder_detection, request_control_event, report, breakup_others, gamer_instruction_kick, lost_connection_kick, lost_connection_kick_others)
+		} reactions{};
 		
 		struct player
 		{
@@ -195,17 +192,13 @@ namespace big
 			struct script_events 
 			{
 				bool bounty = true;
-				bool ceo_ban = true;
-				bool ceo_kick = true;
 				bool ceo_money = true;
 				bool clear_wanted_level = true;
-				bool crash = true;
 				bool fake_deposit = true;
 				bool force_mission = true;
 				bool force_teleport = true;
-				bool gta_banner = true;
+				bool gta_banner = false;
 				bool mc_teleport = true;
-				bool network_bail = true;
 				bool personal_vehicle_destroyed = true;
 				bool remote_off_radar = true;
 				bool rotate_cam = true;
@@ -213,27 +206,25 @@ namespace big
 				bool send_to_location = true;
 				bool sound_spam = true;
 				bool spectate = true;
-				bool switch_player_model = true;
-				bool transaction_error = true;
+				bool give_collectible = true;
 				bool vehicle_kick = true;
 				bool teleport_to_warehouse = true;
 				bool start_activity = true;
 				bool send_sms = true;
 
 				NLOHMANN_DEFINE_TYPE_INTRUSIVE(script_events,
-					bounty, ceo_ban, ceo_kick, ceo_money, clear_wanted_level, crash, fake_deposit,
-					force_mission, force_teleport, gta_banner, mc_teleport, network_bail,
+					bounty, ceo_money, clear_wanted_level, fake_deposit,
+					force_mission, force_teleport, gta_banner, mc_teleport,
 					personal_vehicle_destroyed, remote_off_radar, rotate_cam, send_to_cutscene,
-					send_to_location, sound_spam, spectate, switch_player_model, transaction_error,
+					send_to_location, sound_spam, spectate, give_collectible,
 					vehicle_kick, teleport_to_warehouse, start_activity, send_sms)
 			} script_events{};
 
 			bool desync_kick = false;
-			bool script_host_kick = true;
 			bool rid_join = false;
 			bool lessen_breakups = false; // disabled by default due to anticheat concerns
 
-			NLOHMANN_DEFINE_TYPE_INTRUSIVE(protections, script_events, script_host_kick, rid_join, lessen_breakups, desync_kick)
+			NLOHMANN_DEFINE_TYPE_INTRUSIVE(protections, script_events, rid_join, lessen_breakups, desync_kick)
 		} protections{};
 
 		struct self 
@@ -649,7 +640,7 @@ namespace big
 
 		NLOHMANN_DEFINE_TYPE_INTRUSIVE(menu_settings,
 			debug, tunables, notifications, player, protections, self, session, settings, spawn_vehicle, clone_pv,
-			spawn_ped, spoofing, vehicle, weapons, window, context_menu, esp, session_browser, ugc)
+			spawn_ped, spoofing, vehicle, weapons, window, context_menu, esp, session_browser, ugc, reactions)
 	};
 
 	inline auto g = menu_settings();
diff --git a/src/function_types.hpp b/src/function_types.hpp
index 82b4750a..46b32c4d 100644
--- a/src/function_types.hpp
+++ b/src/function_types.hpp
@@ -1,6 +1,7 @@
 #pragma once
 #include <datanodes/player/CPlayerGameStateDataNode.hpp>
 #include <datanodes/vehicle/CVehicleGadgetDataNode.hpp>
+#include <rage/rlTaskStatus.hpp>
 
 class CMsgJoinResponse;
 class NetworkGameFilterMatchmakingComponent;
@@ -15,6 +16,10 @@ namespace rage
 	class snPlayer;
 	class CDynamicEntity;
 	class netTimeSyncMsg;
+	class snConnectToPeerTaskData;
+	class snConnectToPeerTaskResult;
+	class rlScHandle;
+	class rlQueryPresenceAttributesContext;
 }
 
 namespace datafile_commands
@@ -84,8 +89,9 @@ namespace big::functions
 	using fipackfile_mount = bool(*)(rage::fiPackfile* this_, const char* mount_point);
 	using fipackfile_unmount = bool(*)(const char* mount_point);
 
-	using start_get_session_by_gamer_handle = bool(*)(int profile_index, rage::rlGamerHandle* handles, int count, rage::rlSessionByGamerTaskResult* result, int unk, bool* success, int* state);
-	using start_matchmaking_find_sessions = bool(*)(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, int* state);
+	using start_get_session_by_gamer_handle = bool(*)(int profile_index, rage::rlGamerHandle* handles, int count, rage::rlSessionByGamerTaskResult* result, int unk, bool* success, rage::rlTaskStatus* state);
+	using start_matchmaking_find_sessions = bool(*)(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* m_filter, unsigned int max_sessions, rage::rlSessionInfo* result_sessions, int* result_session_count, rage::rlTaskStatus* state);
+	using start_get_presence_attributes = bool(*)(int profile_index, rage::rlScHandle * handle, rage::rlQueryPresenceAttributesContext* contexts, int count, rage::rlTaskStatus* state);
 	using join_session_by_info = bool(*)(Network* network, rage::rlSessionInfo* info, int unk, int flags, rage::rlGamerHandle* handles, int handlecount);
 
 	using generate_uuid = bool(*)(std::uint64_t* uuid);
@@ -112,10 +118,13 @@ namespace big::functions
 
 	using encode_session_info = bool(*)(rage::rlSessionInfo* info, char* buffer, int buffer_size, int* bytes_written);
 	using decode_session_info = bool(*)(rage::rlSessionInfo* out_info, char* buffer, int* bytes_read);
+	using decode_peer_info = bool(*)(rage::rlGamerInfoBase* info, char* buffer, int* bytes_read);
 
 	using load_cloud_file = void(*)(sCloudFile** out_cloud_file, char* buffer, int size, const char* reason);
 	using set_as_active_cloud_file = void(*)(datafile_commands::SveFileObject* object, sCloudFile** file);
 	using save_json_data = char*(*)(datafile_commands::SveFileObject* object, int* out_length, const char* reason);
 
 	using sync_network_time = bool(*)(rage::netConnectionManager* mgr, rage::netConnectionPeer* peer, int connection_id, rage::netTimeSyncMsg* msg, int flags);
+	using send_packet = bool(*)(rage::netConnectionManager* mgr, rage::netConnectionPeer* peer, int connection_id, void* data, int size, int flags);
+	using connect_to_peer = bool(*)(rage::netConnectionManager* mgr, rage::rlGamerInfoBase* gamer_info, rage::snConnectToPeerTaskData* data, rage::snConnectToPeerTaskResult* result, rage::rlTaskStatus* status);
 }
diff --git a/src/gta/array.hpp b/src/gta/array.hpp
deleted file mode 100644
index a2693cbc..00000000
--- a/src/gta/array.hpp
+++ /dev/null
@@ -1,5 +0,0 @@
-#pragma once
-#include <cstdint>
-#include "fwddec.hpp"
-#include "sysMemAllocator.hpp"
-#include <rage/atArray.hpp>
\ No newline at end of file
diff --git a/src/gta/base.hpp b/src/gta/base.hpp
deleted file mode 100644
index 7d41bef5..00000000
--- a/src/gta/base.hpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-#include "base/datBase.hpp"
-#include "base/pgBase.hpp"
\ No newline at end of file
diff --git a/src/gta/enums.hpp b/src/gta/enums.hpp
index f52da8ea..c02904f5 100644
--- a/src/gta/enums.hpp
+++ b/src/gta/enums.hpp
@@ -372,226 +372,6 @@ enum class ControllerInputs : std::uint32_t
 	SCRIPTED_INPUT_LAST
 };
 
-enum class HudColor : std::uint32_t
-{
-	HUD_COLOUR_PURE_WHITE,
-	HUD_COLOUR_WHITE,
-	HUD_COLOUR_BLACK,
-	HUD_COLOUR_GREY,
-	HUD_COLOUR_GREYLIGHT,
-	HUD_COLOUR_GREYDARK,
-	HUD_COLOUR_RED,
-	HUD_COLOUR_REDLIGHT,
-	HUD_COLOUR_REDDARK,
-	HUD_COLOUR_BLUE,
-	HUD_COLOUR_BLUELIGHT,
-	HUD_COLOUR_BLUEDARK,
-	HUD_COLOUR_YELLOW,
-	HUD_COLOUR_YELLOWLIGHT,
-	HUD_COLOUR_YELLOWDARK,
-	HUD_COLOUR_ORANGE,
-	HUD_COLOUR_ORANGELIGHT,
-	HUD_COLOUR_ORANGEDARK,
-	HUD_COLOUR_GREEN,
-	HUD_COLOUR_GREENLIGHT,
-	HUD_COLOUR_GREENDARK,
-	HUD_COLOUR_PURPLE,
-	HUD_COLOUR_PURPLELIGHT,
-	HUD_COLOUR_PURPLEDARK,
-	HUD_COLOUR_PINK,
-	HUD_COLOUR_RADAR_HEALTH,
-	HUD_COLOUR_RADAR_ARMOUR,
-	HUD_COLOUR_RADAR_DAMAGE,
-	HUD_COLOUR_NET_PLAYER1,
-	HUD_COLOUR_NET_PLAYER2,
-	HUD_COLOUR_NET_PLAYER3,
-	HUD_COLOUR_NET_PLAYER4,
-	HUD_COLOUR_NET_PLAYER5,
-	HUD_COLOUR_NET_PLAYER6,
-	HUD_COLOUR_NET_PLAYER7,
-	HUD_COLOUR_NET_PLAYER8,
-	HUD_COLOUR_NET_PLAYER9,
-	HUD_COLOUR_NET_PLAYER10,
-	HUD_COLOUR_NET_PLAYER11,
-	HUD_COLOUR_NET_PLAYER12,
-	HUD_COLOUR_NET_PLAYER13,
-	HUD_COLOUR_NET_PLAYER14,
-	HUD_COLOUR_NET_PLAYER15,
-	HUD_COLOUR_NET_PLAYER16,
-	HUD_COLOUR_NET_PLAYER17,
-	HUD_COLOUR_NET_PLAYER18,
-	HUD_COLOUR_NET_PLAYER19,
-	HUD_COLOUR_NET_PLAYER20,
-	HUD_COLOUR_NET_PLAYER21,
-	HUD_COLOUR_NET_PLAYER22,
-	HUD_COLOUR_NET_PLAYER23,
-	HUD_COLOUR_NET_PLAYER24,
-	HUD_COLOUR_NET_PLAYER25,
-	HUD_COLOUR_NET_PLAYER26,
-	HUD_COLOUR_NET_PLAYER27,
-	HUD_COLOUR_NET_PLAYER28,
-	HUD_COLOUR_NET_PLAYER29,
-	HUD_COLOUR_NET_PLAYER30,
-	HUD_COLOUR_NET_PLAYER31,
-	HUD_COLOUR_NET_PLAYER32,
-	HUD_COLOUR_SIMPLEBLIP_DEFAULT,
-	HUD_COLOUR_MENU_BLUE,
-	HUD_COLOUR_MENU_GREY_LIGHT,
-	HUD_COLOUR_MENU_BLUE_EXTRA_DARK,
-	HUD_COLOUR_MENU_YELLOW,
-	HUD_COLOUR_MENU_YELLOW_DARK,
-	HUD_COLOUR_MENU_GREEN,
-	HUD_COLOUR_MENU_GREY,
-	HUD_COLOUR_MENU_GREY_DARK,
-	HUD_COLOUR_MENU_HIGHLIGHT,
-	HUD_COLOUR_MENU_STANDARD,
-	HUD_COLOUR_MENU_DIMMED,
-	HUD_COLOUR_MENU_EXTRA_DIMMED,
-	HUD_COLOUR_BRIEF_TITLE,
-	HUD_COLOUR_MID_GREY_MP,
-	HUD_COLOUR_NET_PLAYER1_DARK,
-	HUD_COLOUR_NET_PLAYER2_DARK,
-	HUD_COLOUR_NET_PLAYER3_DARK,
-	HUD_COLOUR_NET_PLAYER4_DARK,
-	HUD_COLOUR_NET_PLAYER5_DARK,
-	HUD_COLOUR_NET_PLAYER6_DARK,
-	HUD_COLOUR_NET_PLAYER7_DARK,
-	HUD_COLOUR_NET_PLAYER8_DARK,
-	HUD_COLOUR_NET_PLAYER9_DARK,
-	HUD_COLOUR_NET_PLAYER10_DARK,
-	HUD_COLOUR_NET_PLAYER11_DARK,
-	HUD_COLOUR_NET_PLAYER12_DARK,
-	HUD_COLOUR_NET_PLAYER13_DARK,
-	HUD_COLOUR_NET_PLAYER14_DARK,
-	HUD_COLOUR_NET_PLAYER15_DARK,
-	HUD_COLOUR_NET_PLAYER16_DARK,
-	HUD_COLOUR_NET_PLAYER17_DARK,
-	HUD_COLOUR_NET_PLAYER18_DARK,
-	HUD_COLOUR_NET_PLAYER19_DARK,
-	HUD_COLOUR_NET_PLAYER20_DARK,
-	HUD_COLOUR_NET_PLAYER21_DARK,
-	HUD_COLOUR_NET_PLAYER22_DARK,
-	HUD_COLOUR_NET_PLAYER23_DARK,
-	HUD_COLOUR_NET_PLAYER24_DARK,
-	HUD_COLOUR_NET_PLAYER25_DARK,
-	HUD_COLOUR_NET_PLAYER26_DARK,
-	HUD_COLOUR_NET_PLAYER27_DARK,
-	HUD_COLOUR_NET_PLAYER28_DARK,
-	HUD_COLOUR_NET_PLAYER29_DARK,
-	HUD_COLOUR_NET_PLAYER30_DARK,
-	HUD_COLOUR_NET_PLAYER31_DARK,
-	HUD_COLOUR_NET_PLAYER32_DARK,
-	HUD_COLOUR_BRONZE,
-	HUD_COLOUR_SILVER,
-	HUD_COLOUR_GOLD,
-	HUD_COLOUR_PLATINUM,
-	HUD_COLOUR_GANG1,
-	HUD_COLOUR_GANG2,
-	HUD_COLOUR_GANG3,
-	HUD_COLOUR_GANG4,
-	HUD_COLOUR_SAME_CREW,
-	HUD_COLOUR_FREEMODE,
-	HUD_COLOUR_PAUSE_BG,
-	HUD_COLOUR_FRIENDLY,
-	HUD_COLOUR_ENEMY,
-	HUD_COLOUR_LOCATION,
-	HUD_COLOUR_PICKUP,
-	HUD_COLOUR_PAUSE_SINGLEPLAYER,
-	HUD_COLOUR_FREEMODE_DARK,
-	HUD_COLOUR_INACTIVE_MISSION,
-	HUD_COLOUR_DAMAGE,
-	HUD_COLOUR_PINKLIGHT,
-	HUD_COLOUR_PM_MITEM_HIGHLIGHT,
-	HUD_COLOUR_SCRIPT_VARIABLE,
-	HUD_COLOUR_YOGA,
-	HUD_COLOUR_TENNIS,
-	HUD_COLOUR_GOLF,
-	HUD_COLOUR_SHOOTING_RANGE,
-	HUD_COLOUR_FLIGHT_SCHOOL,
-	HUD_COLOUR_NORTH_BLUE,
-	HUD_COLOUR_SOCIAL_CLUB,
-	HUD_COLOUR_PLATFORM_BLUE,
-	HUD_COLOUR_PLATFORM_GREEN,
-	HUD_COLOUR_PLATFORM_GREY,
-	HUD_COLOUR_FACEBOOK_BLUE,
-	HUD_COLOUR_INGAME_BG,
-	HUD_COLOUR_DARTS,
-	HUD_COLOUR_WAYPOINT,
-	HUD_COLOUR_MICHAEL,
-	HUD_COLOUR_FRANKLIN,
-	HUD_COLOUR_TREVOR,
-	HUD_COLOUR_GOLF_P1,
-	HUD_COLOUR_GOLF_P2,
-	HUD_COLOUR_GOLF_P3,
-	HUD_COLOUR_GOLF_P4,
-	HUD_COLOUR_WAYPOINTLIGHT,
-	HUD_COLOUR_WAYPOINTDARK,
-	HUD_COLOUR_PANEL_LIGHT,
-	HUD_COLOUR_MICHAEL_DARK,
-	HUD_COLOUR_FRANKLIN_DARK,
-	HUD_COLOUR_TREVOR_DARK,
-	HUD_COLOUR_OBJECTIVE_ROUTE,
-	HUD_COLOUR_PAUSEMAP_TINT,
-	HUD_COLOUR_PAUSE_DESELECT,
-	HUD_COLOUR_PM_WEAPONS_PURCHASABLE,
-	HUD_COLOUR_PM_WEAPONS_LOCKED,
-	HUD_COLOUR_END_SCREEN_BG,
-	HUD_COLOUR_CHOP,
-	HUD_COLOUR_PAUSEMAP_TINT_HALF,
-	HUD_COLOUR_NORTH_BLUE_OFFICIAL,
-	HUD_COLOUR_SCRIPT_VARIABLE_2,
-	HUD_COLOUR_H,
-	HUD_COLOUR_HDARK,
-	HUD_COLOUR_T,
-	HUD_COLOUR_TDARK,
-	HUD_COLOUR_HSHARD,
-	HUD_COLOUR_CONTROLLER_MICHAEL,
-	HUD_COLOUR_CONTROLLER_FRANKLIN,
-	HUD_COLOUR_CONTROLLER_TREVOR,
-	HUD_COLOUR_CONTROLLER_CHOP,
-	HUD_COLOUR_VIDEO_EDITOR_VIDEO,
-	HUD_COLOUR_VIDEO_EDITOR_AUDIO,
-	HUD_COLOUR_VIDEO_EDITOR_TEXT,
-	HUD_COLOUR_HB_BLUE,
-	HUD_COLOUR_HB_YELLOW,
-	HUD_COLOUR_VIDEO_EDITOR_SCORE,
-	HUD_COLOUR_VIDEO_EDITOR_AUDIO_FADEOUT,
-	HUD_COLOUR_VIDEO_EDITOR_TEXT_FADEOUT,
-	HUD_COLOUR_VIDEO_EDITOR_SCORE_FADEOUT,
-	HUD_COLOUR_HEIST_BACKGROUND,
-	HUD_COLOUR_VIDEO_EDITOR_AMBIENT,
-	HUD_COLOUR_VIDEO_EDITOR_AMBIENT_FADEOUT,
-	HUD_COLOUR_GB,
-	HUD_COLOUR_G,
-	HUD_COLOUR_B,
-	HUD_COLOUR_LOW_FLOW,
-	HUD_COLOUR_LOW_FLOW_DARK,
-	HUD_COLOUR_G1,
-	HUD_COLOUR_G2,
-	HUD_COLOUR_G3,
-	HUD_COLOUR_G4,
-	HUD_COLOUR_G5,
-	HUD_COLOUR_G6,
-	HUD_COLOUR_G7,
-	HUD_COLOUR_G8,
-	HUD_COLOUR_G9,
-	HUD_COLOUR_G10,
-	HUD_COLOUR_G11,
-	HUD_COLOUR_G12,
-	HUD_COLOUR_G13,
-	HUD_COLOUR_G14,
-	HUD_COLOUR_G15,
-	HUD_COLOUR_ADVERSARY,
-	HUD_COLOUR_DEGEN_RED,
-	HUD_COLOUR_DEGEN_YELLOW,
-	HUD_COLOUR_DEGEN_GREEN,
-	HUD_COLOUR_DEGEN_CYAN,
-	HUD_COLOUR_DEGEN_BLUE,
-	HUD_COLOUR_DEGEN_MAGENTA,
-	HUD_COLOUR_STUNT_1,
-	HUD_COLOUR_STUNT_2
-};
-
 enum class RadioStationIndexes : std::uint32_t
 {
 	RADIO_LSROCKRADIO,
diff --git a/src/gta/extensible.hpp b/src/gta/extensible.hpp
deleted file mode 100644
index 15808182..00000000
--- a/src/gta/extensible.hpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-#include <cstdint>
-#include "ref_aware.hpp"
-
-#include "base/fwExtension.hpp"
-#include "base/fwExtensibleBase.hpp"
-#include "base/fwExtensionContainer.hpp"
\ No newline at end of file
diff --git a/src/gta/natives.hpp b/src/gta/natives.hpp
index 5126d74e..cd7eb51a 100644
--- a/src/gta/natives.hpp
+++ b/src/gta/natives.hpp
@@ -2,7 +2,8 @@
 #include <cstdint>
 #include <utility>
 #include "fwddec.hpp"
-#include "vector.hpp"
+
+#include <script/types.hpp>
 
 namespace rage
 {
@@ -108,21 +109,4 @@ namespace rage
 		bool m_initialized;
 	};
 #pragma pack(pop)
-}
-
-using Void = void;
-using Any = int;
-using Hash = std::uint32_t;
-using Entity = std::int32_t;
-using Player = std::int32_t;
-using FireId = std::int32_t;
-using Interior = std::int32_t;
-using Ped = Entity;
-using Vehicle = Entity;
-using Cam = std::int32_t;
-using Object = Entity;
-using Pickup = Object;
-using Blip = std::int32_t;
-using Camera = Entity;
-using ScrHandle = Entity;
-using Vector3 = rage::scrVector;
+}
\ No newline at end of file
diff --git a/src/gta/node_list.hpp b/src/gta/node_list.hpp
index d4576c47..e23ddb38 100644
--- a/src/gta/node_list.hpp
+++ b/src/gta/node_list.hpp
@@ -1,6 +1,6 @@
 #pragma once
 #include "fwddec.hpp"
-#include "base.hpp"
+#include <base/datBase.hpp>
 
 namespace rage
 {
diff --git a/src/gta/player.hpp b/src/gta/player.hpp
deleted file mode 100644
index 3e6fd319..00000000
--- a/src/gta/player.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-#include <cstdint>
-#include "fwddec.hpp"
-#include "extensible.hpp"
-#include "vector.hpp"
-#include "gta/natives.hpp"
-
-namespace gta
-{
-	inline constexpr auto num_players = 32;
-}
diff --git a/src/gta/ref_aware.hpp b/src/gta/ref_aware.hpp
deleted file mode 100644
index 3eff1cd4..00000000
--- a/src/gta/ref_aware.hpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-#include "base.hpp"
-
-#include "gta/ref_aware.hpp"
\ No newline at end of file
diff --git a/src/gta/vector.hpp b/src/gta/vector.hpp
deleted file mode 100644
index 9480f1b1..00000000
--- a/src/gta/vector.hpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma once
-
-namespace rage
-{
-#pragma pack(push, 1)
-	class scrVector
-	{
-	public:
-		scrVector() = default;
-
-		scrVector(rage::fvector3 vec) :
-			x(vec.x), y(vec.y), z(vec.z)
-		{}
-
-		scrVector(float x, float y, float z) :
-			x(x), y(y), z(z)
-		{}
-
-		scrVector operator+(const scrVector& other)
-		{
-			scrVector vec;
-			vec.x = this->x + other.x;
-			vec.y = this->y + other.y;
-			vec.z = this->z + other.z;
-			return vec;
-		}
-
-		scrVector operator-(const scrVector& other)
-		{
-			scrVector vec;
-			vec.x = this->x - other.x;
-			vec.y = this->y - other.y;
-			vec.z = this->z - other.z;
-			return vec;
-		}
-
-		scrVector operator*(const scrVector& other)
-		{
-			scrVector vec;
-			vec.x = this->x * other.x;
-			vec.y = this->y * other.y;
-			vec.z = this->z * other.z;
-			return vec;
-		}
-
-		scrVector operator*(const float& other)
-		{
-			scrVector vec;
-			vec.x = this->x * other;
-			vec.y = this->y * other;
-			vec.z = this->z * other;
-			return vec;
-		}
-	public:
-		float x{};
-	private:
-		char m_padding1[0x04];
-	public:
-		float y{};
-	private:
-		char m_padding2[0x04];
-	public:
-		float z{};
-	private:
-		char m_padding3[0x04];
-	};
-#pragma pack(pop)
-}
-
-class Vector2 final
-{
-public:
-	Vector2() = default;
-
-	Vector2(float x, float y)
-		: x(x), y(y)
-	{}
-
-public:
-	float x, y;
-};
-
-class Vector4 final
-{
-public:
-	Vector4() = default;
-
-	Vector4(float x, float y, float z, float w)
-		: x(x), y(y), z(z), w(w)
-	{}
-
-public:
-	float x, y, z, w;
-};
diff --git a/src/gta_util.hpp b/src/gta_util.hpp
index bc348339..fcae1f62 100644
--- a/src/gta_util.hpp
+++ b/src/gta_util.hpp
@@ -1,8 +1,8 @@
 #pragma once
-#include "gta/array.hpp"
 #include "gta/script_thread.hpp"
 #include "gta/tls_context.hpp"
 #include "pointers.hpp"
+
 #include <ped/CPedFactory.hpp>
 #include <network/CNetworkPlayerMgr.hpp>
 #include <script/scrProgramTable.hpp>
diff --git a/src/gui.cpp b/src/gui.cpp
index 8cadd2ee..15151421 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -1,5 +1,4 @@
 #include "common.hpp"
-#include "gta/player.hpp"
 #include "gui.hpp"
 #include "natives.hpp"
 #include "script.hpp"
diff --git a/src/gui/components/components.hpp b/src/gui/components/components.hpp
index e01a2211..14ad3457 100644
--- a/src/gui/components/components.hpp
+++ b/src/gui/components/components.hpp
@@ -31,17 +31,6 @@ namespace big
 
 		static bool script_patch_checkbox(const std::string_view text, bool* option, const std::string_view tooltip = "");
 
-		template<size_t N>
-		struct template_str
-		{
-			constexpr template_str(const char(&str)[N])
-			{
-				std::copy_n(str, N, value);
-			}
-
-			char value[N];
-		};
-
 		template<template_str cmd_str>
 		static void command_button(const std::vector<std::uint64_t> args = {}, std::optional<const std::string_view> label_override = std::nullopt)
 		{
diff --git a/src/hooking.cpp b/src/hooking.cpp
index 914143c6..e6d1f9d2 100644
--- a/src/hooking.cpp
+++ b/src/hooking.cpp
@@ -1,8 +1,6 @@
 #include "common.hpp"
 #include "function_types.hpp"
 #include "logger.hpp"
-#include "gta/array.hpp"
-#include "gta/player.hpp"
 #include "gta/script_thread.hpp"
 #include "gui.hpp"
 #include "hooking.hpp"
diff --git a/src/hooking.hpp b/src/hooking.hpp
index 163b17ed..122de153 100644
--- a/src/hooking.hpp
+++ b/src/hooking.hpp
@@ -34,6 +34,7 @@ namespace rage
 	class netConnectionManager;
 	class datBitBuffer;
 	class rlMetric;
+	class rlTaskStatus;
 
 	namespace netConnection
 	{
@@ -122,7 +123,7 @@ namespace big
 		static bool serialize_player_data_msg(CNetGamePlayerDataMsg* msg, rage::datBitBuffer* buffer);
 		static bool serialize_join_request_message(RemoteGamerInfoMsg* info, void* data, int size, int* bits_serialized);
 
-		static bool start_matchmaking_find_sessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* filter, unsigned int max_sessions, rage::rlSessionInfo* results, int* num_sessions_found, int* status);
+		static bool start_matchmaking_find_sessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* filter, unsigned int max_sessions, rage::rlSessionInfo* results, int* num_sessions_found, rage::rlTaskStatus* status);
 
 		static unsigned int broadcast_net_array(rage::netArrayHandlerBase* _this, CNetGamePlayer* target, rage::datBitBuffer* bit_buffer, uint16_t counter, uint32_t* elem_start, bool silent);
 
diff --git a/src/hooks/misc/start_matchmaking_find_sessions.cpp b/src/hooks/misc/start_matchmaking_find_sessions.cpp
index 2934d2f1..ea5ad4b8 100644
--- a/src/hooks/misc/start_matchmaking_find_sessions.cpp
+++ b/src/hooks/misc/start_matchmaking_find_sessions.cpp
@@ -2,16 +2,17 @@
 #include "services/matchmaking/matchmaking_service.hpp"
 #include <network/Network.hpp>
 #include "fiber_pool.hpp"
+#include "function_types.hpp"
 
 namespace big
 {
-	bool hooks::start_matchmaking_find_sessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* filter, unsigned int max_sessions, rage::rlSessionInfo* results, int* num_sessions_found, int* status)
+	bool hooks::start_matchmaking_find_sessions(int profile_index, int available_slots, NetworkGameFilterMatchmakingComponent* filter, unsigned int max_sessions, rage::rlSessionInfo* results, int* num_sessions_found, rage::rlTaskStatus* status)
 	{
 		int discriminator = filter->m_param_values[0]; // this is guaranteed to work
 
 		if (g.session_browser.replace_game_matchmaking && filter->m_filter_type == 1)
 		{
-			*status = 1;
+			status->status = 1;
 			g_fiber_pool->queue_job([max_sessions, results, num_sessions_found, status, discriminator]
 			{
 				bool result = false;
@@ -35,11 +36,11 @@ namespace big
 						}
 					}
 
-					*status = 3;
+					status->status = 3;
 				}
 				else
 				{
-					*status = 2;
+					status->status = 2;
 				}
 			});
 			return true;
diff --git a/src/hooks/protections/increment_stat_event.cpp b/src/hooks/protections/increment_stat_event.cpp
index 78851b3d..f856fc5c 100644
--- a/src/hooks/protections/increment_stat_event.cpp
+++ b/src/hooks/protections/increment_stat_event.cpp
@@ -1,5 +1,6 @@
 #include "hooking.hpp"
 #include "gta/net_game_event.hpp"
+#include "services/players/player_service.hpp"
 #include <network/CNetGamePlayer.hpp>
 
 namespace big
@@ -18,14 +19,7 @@ namespace big
 		case RAGE_JOAAT("MPPLY_TC_HATE"):
 		case RAGE_JOAAT("MPPLY_VC_ANNOYINGME"):
 		case RAGE_JOAAT("MPPLY_VC_HATE"):
-			const auto report = std::format("From: {}", sender->get_name());
-
-			if (g.notifications.reports.log)
-				LOG(INFO) << "Blocked report; " << report;
-
-			if (g.notifications.reports.notify)
-				g_notification_service->push_warning("BLOCKED REPORT", report);
-
+			g.reactions.report.process(g_player_service->get_by_id(sender->m_player_id));
 			return true;
 		}
 
diff --git a/src/hooks/protections/receive_net_message.cpp b/src/hooks/protections/receive_net_message.cpp
index 82001518..76cdcc6f 100644
--- a/src/hooks/protections/receive_net_message.cpp
+++ b/src/hooks/protections/receive_net_message.cpp
@@ -13,6 +13,27 @@
 #include <network/Network.hpp>
 #include <network/netTime.hpp>
 
+inline void gamer_handle_deserialize(rage::rlGamerHandle& hnd, rage::datBitBuffer& buf)
+{
+	constexpr int PC_PLATFORM = 3;
+	if ((hnd.m_platform = buf.Read<uint8_t>(8)) != PC_PLATFORM)
+		return;
+
+	buf.ReadInt64((int64_t*)&hnd.m_rockstar_id, 64);
+	hnd.unk_0009 = buf.Read<uint8_t>(8);
+}
+
+inline bool is_kick_instruction(rage::datBitBuffer& buffer)
+{
+	rage::rlGamerHandle sender, receiver;
+	char name[18];
+	gamer_handle_deserialize(sender, buffer);
+	gamer_handle_deserialize(receiver, buffer);
+	buffer.ReadString(name, 17);
+	int instruction = buffer.Read<int>(32);
+	return instruction == 8;
+}
+
 namespace big
 {
 	bool get_msg_type(rage::eNetMessage& msgType, rage::datBitBuffer& buffer)
@@ -33,16 +54,6 @@ namespace big
 			return false;
 	}
 
-	void gamer_handle_deserialize(rage::rlGamerHandle& hnd, rage::datBitBuffer& buf)
-	{
-		constexpr int PC_PLATFORM = 3;
-		if ((hnd.m_platform = buf.Read<uint8_t>(8)) != PC_PLATFORM)
-			return;
-
-		buf.ReadInt64((int64_t*)&hnd.m_rockstar_id, 64);
-		hnd.unk_0009 = buf.Read<uint8_t>(8);
-	}
-
 	static void script_id_deserialize(CGameScriptId& id, rage::datBitBuffer& buffer)
 	{
 		id.m_hash = buffer.Read<uint32_t>(32);
@@ -165,14 +176,22 @@ namespace big
 					{
 						if (g_player_service->get_self()->is_host())
 						{
-							g_notification_service->push_error("Warning!", std::format("{} tried to breakup kick {}!", player->get_name(), pl->get_name()));
+							g.reactions.breakup_others.process(player, pl);
 							session::add_infraction(player, Infraction::BREAKUP_KICK_DETECTED);
-							return true;
+
+							if (g.reactions.breakup_others.block)
+								return true;
+
+							if (g.reactions.breakup_others.karma)
+								((player_command*)command::get(RAGE_JOAAT("breakup")))->call(player, {});
 						}
 						else
 						{
-							g_notification_service->push_error("Warning!", std::format("{} breakup kicked {}!", player->get_name(), pl->get_name()));
+							g.reactions.breakup_others.process(player, pl);
 							session::add_infraction(player, Infraction::BREAKUP_KICK_DETECTED);
+
+							if (g.reactions.breakup_others.karma)
+								((player_command*)command::get(RAGE_JOAAT("breakup")))->call(player, {});
 						}
 					}
 
@@ -189,7 +208,7 @@ namespace big
 					if (self->get_net_data() && self->get_net_data()->m_gamer_handle_2.m_rockstar_id == handle.m_rockstar_id)
 					{
 						session::add_infraction(player, Infraction::TRIED_KICK_PLAYER);
-						g_notification_service->push_error("Protections", std::format("{} tried to lost connection kick you!", player->get_name()));
+						g.reactions.lost_connection_kick.process(player);
 						return true;
 					}
 
@@ -198,8 +217,12 @@ namespace big
 						if (plyr->get_net_data() && plyr != player && plyr->get_net_data()->m_gamer_handle_2.m_rockstar_id == handle.m_rockstar_id)
 						{
 							session::add_infraction(player, Infraction::LOST_CONNECTION_KICK_DETECTED);
-							g_notification_service->push_error("Protections", std::format("{} tried to lost connection kick {}!", player->get_name(), plyr->get_name()));
-							return true;
+							g.reactions.lost_connection_kick_others.process(player, plyr);
+
+							if (g.reactions.lost_connection_kick_others.block)
+								return true;
+							else
+								break;
 						}
 					}
 
@@ -208,7 +231,7 @@ namespace big
 				case rage::eNetMessage::MsgSessionEstablished:
 				{
 					rage::rlGamerHandle handle{ 0 };
-					if (player && player->get_net_data())
+					if (player->get_net_data())
 					{
 						uint64_t session_id;
 						buffer.ReadQWord(&session_id, 64);
@@ -243,7 +266,7 @@ namespace big
 				}
 				case rage::eNetMessage::MsgRequestObjectIds:
 				{
-					if (player && player->block_join)
+					if (player->block_join)
 					{
 						g_notification_service->push("Join Blocker", std::format("Trying to prevent {} from joining...", player->get_name()));
 						return true;
@@ -262,21 +285,27 @@ namespace big
 				}
 				case rage::eNetMessage::MsgNetTimeSync:
 				{
-					if (player)
-					{
-						int action = buffer.Read<int>(2);
-						uint32_t counter = buffer.Read<uint32_t>(32);
-						uint32_t token = buffer.Read<uint32_t>(32);
-						uint32_t timestamp = buffer.Read<uint32_t>(32);
-						uint32_t time_diff = (*g_pointers->m_network_time)->m_time_offset + frame->m_timestamp;
+					int action = buffer.Read<int>(2);
+					uint32_t counter = buffer.Read<uint32_t>(32);
+					uint32_t token = buffer.Read<uint32_t>(32);
+					uint32_t timestamp = buffer.Read<uint32_t>(32);
+					uint32_t time_diff = (*g_pointers->m_network_time)->m_time_offset + frame->m_timestamp;
 
-						if (action == 0)
-						{
-							player->player_time_value = timestamp;
-							player->player_time_value_received_time = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
-							if (!player->time_difference || time_diff > player->time_difference.value())
-								player->time_difference = time_diff;
-						}
+					if (action == 0)
+					{
+						player->player_time_value = timestamp;
+						player->player_time_value_received_time = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
+						if (!player->time_difference || time_diff > player->time_difference.value())
+							player->time_difference = time_diff;
+					}
+					break;
+				}
+				case rage::eNetMessage::MsgTransitionGamerInstruction:
+				{
+					if (is_kick_instruction(buffer))
+					{
+						g.reactions.gamer_instruction_kick.process(player);
+						return true;
 					}
 					break;
 				}
@@ -353,6 +382,15 @@ namespace big
 
 					return true;
 				}
+				case rage::eNetMessage::MsgTransitionGamerInstruction:
+				{
+					if (is_kick_instruction(buffer))
+					{
+						g_notification_service->push_error("Warning!", "Someone tried to gamer instruction kick you remotely!");
+						return true;
+					}
+					break;
+				}
 			}
 		}
 		
diff --git a/src/hooks/protections/received_event.cpp b/src/hooks/protections/received_event.cpp
index 7239c5e4..45187707 100644
--- a/src/hooks/protections/received_event.cpp
+++ b/src/hooks/protections/received_event.cpp
@@ -348,6 +348,8 @@ namespace big
 			return;
 		}
 
+		auto plyr = g_player_service->get_by_id(source_player->m_player_id);
+
 		switch (static_cast<eNetworkEvents>(event_id))
 		{
 		case eNetworkEvents::KICK_VOTES_EVENT:
@@ -355,10 +357,7 @@ namespace big
 			std::uint32_t player_bitfield = buffer->Read<uint32_t>(32);
 			if (player_bitfield & (1 << target_player->m_player_id))
 			{
-				if (g.notifications.received_event.kick_vote.log)
-					LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " is voting to kick us.";
-				if (g.notifications.received_event.kick_vote.notify)
-					g_notification_service->push_warning("Kick Vote", std::format("{} is voting to kick us.", source_player->get_name()));
+				g.reactions.kick_vote.process(plyr);
 			}
 			buffer->Seek(0);
 			break;
@@ -420,12 +419,7 @@ namespace big
 			if (g_local_player && g_local_player->m_net_object && g_local_player->m_net_object->m_object_id == net_id)
 			{
 				g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
-
-				if (g.notifications.received_event.clear_ped_task.log)
-					LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent CLEAR_PED_TASKS event.";
-				if (g.notifications.received_event.clear_ped_task.notify)
-					g_notification_service->push_warning("Protections", std::format("{} tried to freeze player.", source_player->get_name()));
-
+				g.reactions.clear_ped_tasks.process(plyr);
 				return;
 			}
 
@@ -439,12 +433,7 @@ namespace big
 			if (g_local_player && g_local_player->m_net_object && g_local_player->m_net_object->m_object_id == net_id)
 			{
 				g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
-
-				if (g.notifications.received_event.clear_ped_task.log)
-					LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent RAGDOLL_REQUEST event.";
-				if (g.notifications.received_event.clear_ped_task.notify)
-					g_notification_service->push_warning("Protections", std::format("{} tried to ragdoll player.", source_player->get_name()));
-
+				g.reactions.remote_ragdoll.process(plyr);
 				return;
 			}
 
@@ -463,10 +452,7 @@ namespace big
 
 			if (money >= 2000)
 			{
-				if (g.notifications.received_event.report_cash_spawn.log)
-					LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent REPORT_CASH_SPAWN event.";
-				if (g.notifications.received_event.report_cash_spawn.notify)
-					g_notification_service->push_warning("Protections", std::format("{} is spawning cash.", source_player->get_name()));
+				g.reactions.report_cash_spawn.process(plyr);
 			}
 
 			break;
@@ -474,14 +460,10 @@ namespace big
 		// player sending this event is a modder
 		case eNetworkEvents::REPORT_MYSELF_EVENT:
 		{
-			if (g.notifications.received_event.modder_detect.log)
-				LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " sent modder event.";
-			if (g.notifications.received_event.modder_detect.notify)
-				g_notification_service->push_warning("Protections", std::format("{} sent out a modder event.", source_player->get_name()));
-
 			if (auto plyr = g_player_service->get_by_id(source_player->m_player_id))
 				session::add_infraction(plyr, Infraction::TRIGGERED_ANTICHEAT);
 
+			g.reactions.modder_detection.process(plyr);
 			break;
 		}
 		case eNetworkEvents::REQUEST_CONTROL_EVENT:
@@ -490,12 +472,7 @@ namespace big
 			if (g_local_player && g_local_player->m_vehicle && g_local_player->m_vehicle->m_net_object && g_local_player->m_vehicle->m_net_object->m_object_id == net_id)
 			{
 				g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
-
-				if (g.notifications.received_event.request_control_event.log)
-					LOG(INFO) << "RECEIVED_EVENT_HANDLER : " << source_player->get_name() << " requested control of player vehicle.";
-				if (g.notifications.received_event.request_control_event.notify)
-					g_notification_service->push_warning("Protections", std::format("Denied player control request from {}", source_player->get_name()));
-
+				g.reactions.request_control_event.process(plyr);
 				return;
 			}
 			buffer->Seek(0);
@@ -615,7 +592,6 @@ namespace big
 		}
 		case eNetworkEvents::NETWORK_PLAY_SOUND_EVENT:
 		{
-			auto plyr = g_player_service->get_by_id(source_player->m_player_id);
 			if (plyr && plyr->m_play_sound_rate_limit.process())
 			{
 				if (plyr->m_play_sound_rate_limit.exceeded_last_process())
@@ -648,8 +624,7 @@ namespace big
 
 			if (sound_hash == RAGE_JOAAT("Remote_Ring") && plyr)
 			{
-				g_notification_service->push_warning("Protections", std::format("Blocked sound annoyance from {}", plyr->get_name()));
-				g_pointers->m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
+				g.reactions.sound_spam.process(plyr);
 				return;
 			}
 
diff --git a/src/hooks/protections/script_event_handler.cpp b/src/hooks/protections/script_event_handler.cpp
index e5e05b2c..4b33af4c 100644
--- a/src/hooks/protections/script_event_handler.cpp
+++ b/src/hooks/protections/script_event_handler.cpp
@@ -2,7 +2,11 @@
 #include "gta_util.hpp"
 #include "util/session.hpp"
 #include "gta/net_game_event.hpp"
+#include "backend/player_command.hpp"
+
 #include <network/CNetGamePlayer.hpp>
+#include <network/Network.hpp>
+#include <script/globals/GPBD_FM_3.hpp>
 
 namespace big
 {
@@ -17,6 +21,34 @@ namespace big
 			);
 	}
 
+	inline bool is_player_driver_of_local_vehicle(Player sender)
+	{
+		auto plyr = g_player_service->get_by_id(sender);
+
+		if (!plyr || !plyr->get_current_vehicle() || !g_player_service->get_self()->get_current_vehicle())
+			return false;
+
+		return g_player_service->get_self()->get_current_vehicle()->m_driver == plyr->get_ped();
+	}
+
+	inline bool is_player_our_goon(Player sender)
+	{
+		auto& boss_goon = scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[self::id].BossGoon;
+
+		if (boss_goon.Boss != self::id)
+			return false;
+
+		for (int i = 0; i < boss_goon.Goons.Size; i++)
+		{
+			if (boss_goon.Goons[i] == sender)
+			{
+				return true;
+			}
+		}
+
+		return false;
+	}
+
 	bool hooks::scripted_game_event(CScriptedGameEvent* scripted_game_event, CNetGamePlayer* player)
 	{
 		const auto args = scripted_game_event->m_args;
@@ -24,56 +56,38 @@ namespace big
 		const auto hash = static_cast<eRemoteEvent>(args[0]);
 		const auto player_name = player->get_name();
 
-		const auto& notify = g.notifications.script_event_handler;
+		auto plyr = g_player_service->get_by_id(player->m_player_id);
 
 		switch (hash)
 		{
 		case eRemoteEvent::Bounty:
-			if (g.protections.script_events.bounty)
+			if (g.protections.script_events.bounty && args[2] == self::id)
 			{
-				format_string(player_name, "Bounty", notify.bounty.log, notify.bounty.notify);
-
-				return true;
-			}
-			break;
-		case eRemoteEvent::CeoBan:
-			if (g.protections.script_events.ceo_ban)
-			{
-				format_string(player_name, "Ceo Ban", notify.ceo_ban.log, notify.ceo_ban.notify);
-
-				return true;
-			}
-			break;
-		case eRemoteEvent::CeoKick:
-			if (g.protections.script_events.ceo_kick)
-			{
-				format_string(player_name, "Ceo Kick", notify.ceo_kick.log, notify.ceo_kick.notify);
-
+				g.reactions.bounty.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::CeoMoney:
-			if (g.protections.script_events.ceo_money)
+			if (g.protections.script_events.ceo_money && player->m_player_id != scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[self::id].BossGoon.Boss)
 			{
-				format_string(player_name, "Ceo Money", notify.ceo_money.log, notify.ceo_money.notify);
-
+				g.reactions.ceo_money.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::ClearWantedLevel:
-			if (g.protections.script_events.clear_wanted_level)
+			if (g.protections.script_events.clear_wanted_level && !is_player_driver_of_local_vehicle(player->m_player_id))
 			{
-				format_string(player_name, "Clear Wanted Level", notify.clear_wanted_level.log, notify.clear_wanted_level.notify);
-
+				g.reactions.clear_wanted_level.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::Crash:
+			g.reactions.crash.process(plyr);
+			return true;
 		case eRemoteEvent::Crash2:
-			if (g.protections.script_events.crash)
+			if (args[2] > 32) // actual crash condition is if args[2] is above 255
 			{
-				format_string(player_name, "TSE Crash", notify.crash.log, notify.crash.notify);
-
+				g.reactions.crash.process(plyr);
 				return true;
 			}
 			break;
@@ -85,8 +99,7 @@ namespace big
 			case eRemoteEvent::NotificationMoneyStolen:
 				if (g.protections.script_events.fake_deposit)
 				{
-					format_string(player_name, "Fake Deposit", notify.fake_deposit.log, notify.fake_deposit.notify);
-
+					g.reactions.fake_deposit.process(plyr);
 					return true;
 				}
 				break;
@@ -95,81 +108,68 @@ namespace big
 		case eRemoteEvent::ForceMission:
 			if (g.protections.script_events.force_mission)
 			{
-				format_string(player_name, "Force Mission", notify.force_mission.log, notify.force_mission.notify);
-
+				g.reactions.force_mission.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::GiveCollectible:
-			if (g.protections.script_events.switch_player_model)
+			if (g.protections.script_events.give_collectible)
 			{
-				if (args[2] == 8)
-				{
-					format_string(player_name, "Switch Player Model", notify.switch_player_model.log, notify.switch_player_model.notify);
-
-					return true;
-				}
+				g.reactions.give_collectible.process(plyr);
+				return true;
 			}
 			break;
 		case eRemoteEvent::GtaBanner:
 			if (g.protections.script_events.gta_banner)
 			{
-				format_string(player_name, "GTA Banner", notify.gta_banner.log, notify.gta_banner.notify);
-
+				g.reactions.gta_banner.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::MCTeleport:
 			if (g.protections.script_events.mc_teleport && args[3] <= 32)
 			{
-				format_string(player_name, "Remote Teleport", notify.mc_teleport.log, notify.mc_teleport.notify);
-
+				g.reactions.mc_teleport.process(plyr);
 				return true;
 			}
-			else if (g.protections.script_events.crash && args[3] > 32)
+			else if (args[3] > 32)
 			{
-				format_string(player_name, "TSE Crash", notify.crash.log, notify.crash.notify);
-
+				g.reactions.crash.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::PersonalVehicleDestroyed:
 			if (g.protections.script_events.personal_vehicle_destroyed)
 			{
-				format_string(player_name, "Personal Vehicle Destroyed", notify.personal_vehicle_destroyed.log, notify.personal_vehicle_destroyed.notify);
-
+				g.reactions.personal_vehicle_destroyed.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::RemoteOffradar:
-			if (g.protections.script_events.remote_off_radar)
+			if (g.protections.script_events.remote_off_radar && player->m_player_id != scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[self::id].BossGoon.Boss)
 			{
-				format_string(player_name, "Off Radar", notify.remote_off_radar.log, notify.remote_off_radar.notify);
-
+				g.reactions.remote_off_radar.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::TSECommand:
-			if (g.protections.script_events.rotate_cam && static_cast<eRemoteEvent>(args[2]) == eRemoteEvent::TSECommandRotateCam)
+			if (g.protections.script_events.rotate_cam && static_cast<eRemoteEvent>(args[2]) == eRemoteEvent::TSECommandRotateCam && !gta_util::get_network()->m_is_activity_session)
 			{
-				format_string(player_name, "Rotate Cam", notify.rotate_cam.log, notify.rotate_cam.notify);
-
+				g.reactions.rotate_cam.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::SendToCayoPerico:
-			if (g.protections.script_events.send_to_location)
+			if (g.protections.script_events.send_to_location && args[3] == 0)
 			{
-				format_string(player_name, "Send to Cayo Perico", notify.send_to_location.log, notify.send_to_location.notify);
-
+				g.reactions.send_to_location.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::SendToCutscene:
-			if (g.protections.script_events.send_to_cutscene)
+			if (g.protections.script_events.send_to_cutscene && player->m_player_id != scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[self::id].BossGoon.Boss)
 			{
-				format_string(player_name, "Send to Cutscene", notify.send_to_cutscene.log, notify.send_to_cutscene.notify);
-
+				g.reactions.send_to_cutscene.process(plyr);
 				return true;
 			}
 			break;
@@ -185,8 +185,7 @@ namespace big
 
 					if (g.protections.script_events.send_to_location)
 					{
-						format_string(player_name, "Send to Beach", notify.send_to_location.log, notify.send_to_location.notify);
-
+						g.reactions.send_to_location.process(plyr);
 						return true;
 					}
 				}
@@ -196,8 +195,7 @@ namespace big
 
 					if (g.protections.script_events.send_to_location)
 					{
-						format_string(player_name, "Send to Cayo Perico", notify.send_to_location.log, notify.send_to_location.notify);
-
+						g.reactions.send_to_location.process(plyr);
 						return true;
 					}
 				}
@@ -205,74 +203,56 @@ namespace big
 
 			if (!known_location)
 			{
-				format_string(player_name, "TSE Freeze", notify.tse_freeze.log, notify.tse_freeze.notify);
-
+				g.reactions.tse_freeze.process(plyr);
 				return true;
 			}
 			break;
 		}
 		case eRemoteEvent::SoundSpam:
-			if (g.protections.script_events.sound_spam)
-			{
-				format_string(player_name, "Sound Spamn", notify.sound_spam.log, notify.sound_spam.notify);
+		{
+			auto plyr = g_player_service->get_by_id(player->m_player_id);
 
+			if (g.protections.script_events.sound_spam && (!plyr || plyr->m_invites_rate_limit.process()))
+			{
+				if (plyr && plyr->m_invites_rate_limit.exceeded_last_process())
+					g.reactions.sound_spam.process(plyr);
 				return true;
 			}
 			break;
+		}
 		case eRemoteEvent::Spectate:
 			if (g.protections.script_events.spectate)
 			{
-				format_string(player_name, "Spectate", notify.spectate.log, notify.spectate.notify);
-
+				g.reactions.spectate_notification.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::Teleport:
-			if (g.protections.script_events.force_teleport)
+			if (g.protections.script_events.force_teleport && !is_player_driver_of_local_vehicle(player->m_player_id))
 			{
-				format_string(player_name, "Apartment Invite", notify.force_teleport.log, notify.force_teleport.notify);
-
+				g.reactions.force_teleport.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::TransactionError:
-			if (g.protections.script_events.transaction_error)
-			{
-				format_string(player_name, "Transaction Error", notify.transaction_error.log, notify.transaction_error.notify);
-
-				return true;
-			}
-			break;
+			g.reactions.transaction_error.process(plyr);
+			return true;
 		case eRemoteEvent::VehicleKick:
 			if (g.protections.script_events.vehicle_kick)
 			{
-				format_string(player_name, "Vehicle Kick", notify.vehicle_kick.log, notify.vehicle_kick.notify);
-
-				return true;
-			}
-			break;
-		case eRemoteEvent::ForceMission2:
-			if (g.protections.script_events.force_mission)
-			{
-				format_string(player_name, "Force Mission", notify.force_mission.log, notify.force_mission.notify);
-
+				g.reactions.vehicle_kick.process(plyr);
 				return true;
 			}
 			break;
 		case eRemoteEvent::NetworkBail:
-			if (g.protections.script_events.network_bail)
-			{
-				if (auto plyr = g_player_service->get_by_id(player->m_player_id))
-					session::add_infraction(plyr, Infraction::TRIED_KICK_PLAYER);
-				format_string(player_name, "Network Bail", notify.network_bail.log, notify.network_bail.notify);
-				return true;
-			}
-			break;
+			if (auto plyr = g_player_service->get_by_id(player->m_player_id))
+				session::add_infraction(plyr, Infraction::TRIED_KICK_PLAYER);
+			g.reactions.network_bail.process(plyr);
+			return true;
 		case eRemoteEvent::TeleportToWarehouse:
-			if (g.protections.script_events.teleport_to_warehouse)
+			if (g.protections.script_events.teleport_to_warehouse && !is_player_driver_of_local_vehicle(player->m_player_id))
 			{
-				format_string(player_name, "Teleport To Warehouse", notify.teleport_to_warehouse.log, notify.teleport_to_warehouse.notify);
-
+				g.reactions.teleport_to_warehouse.process(plyr);
 				return true;
 			}
 			break;
@@ -283,42 +263,42 @@ namespace big
 			{
 				if (activity == eActivityType::Survival || activity == eActivityType::Mission || activity == eActivityType::Deathmatch || activity == eActivityType::BaseJump || activity == eActivityType::Race)
 				{
-					format_string(player_name, "Softlock Game", notify.start_activity.log, notify.start_activity.notify);
-
+					g.reactions.tse_freeze.process(plyr);
 					return true;
 				}
 				else if (activity == eActivityType::Darts)
 				{
-					format_string(player_name, "Send To Darts", notify.start_activity.log, notify.start_activity.notify);
-
+					g.reactions.start_activity.process(plyr);
 					return true;
 				}
 				else if (activity == eActivityType::PilotSchool)
 				{
-					format_string(player_name, "Send To Flight School", notify.start_activity.log, notify.start_activity.notify);
-
+					g.reactions.start_activity.process(plyr);
 					return true;
 				}
 				else if (activity == eActivityType::ImpromptuDeathmatch)
 				{
-					format_string(player_name, "Start Impromptu Deathmatch", notify.start_activity.log, notify.start_activity.notify);
-
+					g.reactions.start_activity.process(plyr);
 					return true;
 				}
 				else if (activity == eActivityType::DefendSpecialCargo || activity == eActivityType::GunrunningDefend || activity == eActivityType::BikerDefend)
 				{
-					format_string(player_name, "Trigger Business Raid", notify.start_activity.log, notify.start_activity.notify);
-
+					g.reactions.start_activity.process(plyr);
 					return true;
 				}
-				// there are MANY more
 			}
-			else if (g.protections.script_events.crash && activity == eActivityType::Tennis)
+			else if (activity == eActivityType::Tennis)
 			{
-				format_string(player_name, "TSE Crash (Start Tennis)", notify.crash.log, notify.crash.notify);
-
+				g.reactions.crash.process(plyr);
 				return true;
 			}
+
+			if (g.protections.script_events.start_activity && !is_player_our_goon(player->m_player_id))
+			{
+				g.reactions.start_activity.process(plyr);
+				return true;
+			}
+
 			break;
 		}
 		case eRemoteEvent::InteriorControl:
@@ -328,9 +308,7 @@ namespace big
 			{
 				if (auto plyr = g_player_service->get_by_id(player->m_player_id))
 					session::add_infraction(plyr, Infraction::TRIED_KICK_PLAYER);
-
-				format_string(player_name, "Null Function Kick", notify.null_function_kick.log, notify.null_function_kick.notify);
-
+				g.reactions.null_function_kick.process(plyr);
 				return true;
 			}
 			break;
@@ -338,18 +316,27 @@ namespace big
 		case eRemoteEvent::SMS:
 			if (g.protections.script_events.send_sms)
 			{
-				format_string(player_name, "Send SMS", notify.send_sms.log, notify.send_sms.notify);
+				if (g.session.kick_chat_spammers)
+				{
+					if (auto plyr = g_player_service->get_by_id(player->m_player_id))
+					{
+						((player_command*)command::get(RAGE_JOAAT("breakup")))->call(plyr, {});
+					}
+				}
 
 				return true;
 			}
 			break;
+		case eRemoteEvent::DestroyPersonalVehicle:
+			g.reactions.destroy_personal_vehicle.process(plyr);
+			return true;
 		}
 
 		// detect pasted menus setting args[1] to something other than PLAYER_ID()
 		if (*(int*)&args[1] != player->m_player_id && player->m_player_id != -1)
 		{
 			LOG(INFO) << "Hash = " << (int)args[0];
-			format_string(player_name, "TSE sender mismatch", notify.tse_sender_mismatch.log, notify.tse_sender_mismatch.notify);
+			g.reactions.tse_sender_mismatch.process(plyr);
 			return true;
 		}
 
@@ -361,7 +348,7 @@ namespace big
 				if (i)
 					script_args += ", ";
 
-				script_args += std::to_string(args[i]);
+				script_args += std::to_string((int)args[i]);
 			}
 			script_args += " };";
 
diff --git a/src/hooks/protections/update_presence_attribute.cpp b/src/hooks/protections/update_presence_attribute.cpp
index c5f806a8..7c5ae4a2 100644
--- a/src/hooks/protections/update_presence_attribute.cpp
+++ b/src/hooks/protections/update_presence_attribute.cpp
@@ -22,6 +22,12 @@ namespace big
 			return true;
 		}
 
+		// shouldn't have any side effects
+		if (hash == RAGE_JOAAT("peeraddr"))
+		{
+			value = (char*)"";
+		}
+
 		return g_hooking->get_original<hooks::update_presence_attribute_string>()(presence_data, profile_index, attr, value);
 	}
 }
\ No newline at end of file
diff --git a/src/natives.hpp b/src/natives.hpp
index 29d413d7..2fa13e77 100644
--- a/src/natives.hpp
+++ b/src/natives.hpp
@@ -1,7 +1,6 @@
 #pragma once
 #include "common.hpp"
 #include "gta/natives.hpp"
-#include "gta/vector.hpp"
 #include "invoker.hpp"
 
 template <typename Ret, typename ...Args>
diff --git a/src/packet.cpp b/src/packet.cpp
index 4bb2bc4f..1a18a392 100644
--- a/src/packet.cpp
+++ b/src/packet.cpp
@@ -15,4 +15,16 @@ namespace big
 	{
 		g_pointers->m_queue_packet(gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr, msg_id, m_data, (m_buffer.m_curBit + 7) >> 3, 1, nullptr);
 	}
+
+	void packet::send(player_ptr player, int connection_id)
+	{
+		send(player->get_session_player()->m_player_data.m_peer_id_2, connection_id);
+	}
+
+	void packet::send(int peer_id, int connection_id)
+	{
+		auto mgr = gta_util::get_network()->m_game_session_ptr->m_net_connection_mgr;
+		auto peer = g_pointers->m_get_connection_peer(mgr, peer_id);
+		g_pointers->m_send_packet(mgr, peer, connection_id, m_data, (m_buffer.m_curBit + 7) >> 3, 0x1000000);
+	}
 }
\ No newline at end of file
diff --git a/src/packet.hpp b/src/packet.hpp
index 61ab26df..00121308 100644
--- a/src/packet.hpp
+++ b/src/packet.hpp
@@ -13,6 +13,8 @@ namespace big
 
 		packet();
 		void send(uint32_t msg_id);
+		void send(player_ptr player, int connection_id);
+		void send(int peer_id, int connection_id);
 
 		inline operator rage::datBitBuffer& ()
 		{
diff --git a/src/pointers.cpp b/src/pointers.cpp
index c7847147..d2bc36e1 100644
--- a/src/pointers.cpp
+++ b/src/pointers.cpp
@@ -693,6 +693,12 @@ namespace big
 			m_decode_session_info = ptr.as<functions::decode_session_info>();
 		});
 
+		// Decode Peer Info
+		main_batch.add("DPI", "48 8B C4 48 89 58 08 48 89 70 10 57 48 81 EC A0 00 00 00 48 8B DA", [this](memory::handle ptr)
+		{
+			m_decode_peer_info = ptr.as<functions::decode_peer_info>();
+		});
+
 		// Can Start Session Joining Check
 		main_batch.add("CSSJC", "77 DB ? ? ? ? ? ? ? 74 09", [this](memory::handle ptr)
 		{
@@ -774,12 +780,24 @@ namespace big
 			memory::byte_patch::make(ptr.add(13).as<void*>(), bytes)->apply();
 		});
 
-		// Metric
+		// Prepare Metric For Sending
 		main_batch.add("PMFS", "48 8B C4 48 89 58 08 48 89 68 10 48 89 70 18 48 89 78 20 41 56 48 83 EC 30 49 8B E8 4C 8D 40 EC 49 8B F1 48 8B D9 40 32 FF E8", [this](memory::handle ptr)
 		{
 			m_prepare_metric_for_sending = ptr.as<PVOID>();
 		});
 
+		// Send Packet
+		main_batch.add("SP", "48 8B C4 48 89 58 08 48 89 70 10 48 89 78 18 4C 89 48 20 55 41 54 41 55 41 56 41 57 48 8D A8 98", [this](memory::handle ptr)
+		{
+			m_send_packet = ptr.as<functions::send_packet>();
+		});
+
+		// Connect To Peer
+		main_batch.add("CTP", "48 89 5C 24 08 4C 89 44 24 18 55 56 57 41 54 41 55 41 56 41 57 48 81 EC 80", [this](memory::handle ptr)
+		{
+			m_connect_to_peer = ptr.as<functions::connect_to_peer>();
+		});
+    
 		// Fragment Physics Crash
 		main_batch.add("FPC", "E8 ? ? ? ? 44 8B 4D 1C", [this](memory::handle ptr)
 		{
@@ -805,6 +823,12 @@ namespace big
 			m_update_presence_attribute_string = presence_data_vft[3];
 		});
 
+		// Start Get Presence Attributes
+		socialclub_batch.add("SGPA", "48 8B C4 48 89 58 08 48 89 68 10 48 89 70 18 48 89 78 20 41 54 41 56 41 57 48 83 EC 40 33 DB 41", [this](memory::handle ptr)
+		{
+			m_start_get_presence_attributes = ptr.as<functions::start_get_presence_attributes>();
+		});
+
 		auto sc_module = memory::module("socialclub.dll");
 		if (sc_module.wait_for_module())
 		{
diff --git a/src/pointers.hpp b/src/pointers.hpp
index a5206cde..710cb4bc 100644
--- a/src/pointers.hpp
+++ b/src/pointers.hpp
@@ -133,6 +133,7 @@ namespace big
 
 		functions::start_get_session_by_gamer_handle m_start_get_session_by_gamer_handle;
 		functions::start_matchmaking_find_sessions m_start_matchmaking_find_sessions;
+		functions::start_get_presence_attributes m_start_get_presence_attributes;
 		functions::join_session_by_info m_join_session_by_info;
 
 		memory::byte_patch* m_bypass_max_count_of_active_sticky_bombs;
@@ -217,6 +218,7 @@ namespace big
 
 		functions::encode_session_info m_encode_session_info;
 		functions::decode_session_info m_decode_session_info;
+		functions::decode_peer_info m_decode_peer_info;
 
 		datafile_commands::SveFileObject* m_main_file_object;
 		functions::load_cloud_file m_load_cloud_file;
@@ -228,9 +230,11 @@ namespace big
 
 		rage::rlGamerInfo* m_chat_gamer_info;
 
+		functions::send_packet m_send_packet;
+		functions::connect_to_peer m_connect_to_peer;
+    
 		PVOID m_fragment_physics_crash;
 		PVOID m_fragment_physics_crash_2;
-
 	};
 
 	inline pointers* g_pointers{};
diff --git a/src/script_mgr.cpp b/src/script_mgr.cpp
index e4b88e9c..d9aa1ed4 100644
--- a/src/script_mgr.cpp
+++ b/src/script_mgr.cpp
@@ -1,5 +1,4 @@
 #include "common.hpp"
-#include "gta/array.hpp"
 #include "gta/script_thread.hpp"
 #include "gta/tls_context.hpp"
 #include "gta_util.hpp"
diff --git a/src/services/gui/gui_service.hpp b/src/services/gui/gui_service.hpp
index 318b0214..e4c75bca 100644
--- a/src/services/gui/gui_service.hpp
+++ b/src/services/gui/gui_service.hpp
@@ -38,7 +38,7 @@ namespace big
 		CONTEXT_MENU_SETTINGS,
 		ESP_SETTINGS,
 		GUI_SETTINGS,
-		NOTIFICATION_SETTINGS,
+		REACTION_SETTINGS,
 		PROTECTION_SETTINGS,
 		DEBUG,
 
@@ -89,7 +89,7 @@ namespace big
 				{ tabs::CONTEXT_MENU_SETTINGS, { "Context Menu", view::context_menu_settings}},
 				{ tabs::ESP_SETTINGS, { "ESP", view::esp_settings}},
 				{ tabs::GUI_SETTINGS, { "GUI", view::gui_settings}},
-				{ tabs::NOTIFICATION_SETTINGS, { "Notifications", view::notification_settings}},
+				{ tabs::REACTION_SETTINGS, { "Reactions", view::reaction_settings}},
 				{ tabs::PROTECTION_SETTINGS, { "Protection", view::protection_settings}},
 				{ tabs::DEBUG, { "Debug", nullptr }},
 			}}},
diff --git a/src/services/matchmaking/matchmaking_service.cpp b/src/services/matchmaking/matchmaking_service.cpp
index e8f719b3..0982e890 100644
--- a/src/services/matchmaking/matchmaking_service.cpp
+++ b/src/services/matchmaking/matchmaking_service.cpp
@@ -38,17 +38,17 @@ namespace big
 			component.SetParameter("MMATTR_DISCRIMINATOR", 1, constraint.value());
 		}
 
-		int state = 0;
+		rage::rlTaskStatus state{};
 		static rage::rlSessionInfo result_sessions[MAX_SESSIONS_TO_FIND];
 
 		m_active = true;
 		
 		if (g_hooking->get_original<hooks::start_matchmaking_find_sessions>()(0, 1, &component, MAX_SESSIONS_TO_FIND, result_sessions, &m_num_sessions_found, &state))
 		{
-			while (state == 1)
+			while (state.status == 1)
 				script::get_current()->yield();
 
-			if (state == 3)
+			if (state.status == 3)
 			{
 				for (int i = 0; i < m_num_sessions_found; i++)
 				{
diff --git a/src/services/players/player.hpp b/src/services/players/player.hpp
index e4d38802..849735b3 100644
--- a/src/services/players/player.hpp
+++ b/src/services/players/player.hpp
@@ -3,11 +3,15 @@
 #include "rate_limiter.hpp"
 
 class CVehicle;
+class CPed;
+class CNetGamePlayer;
+class CPlayerInfo;
 
 namespace rage
 {
 	class snPlayer;
 	class snPeer;
+	class rlGamerInfo;
 }
 
 namespace big
@@ -53,8 +57,15 @@ namespace big
 		bool never_wanted = false;
 		bool semi_godmode = false;
 
+		bool kill_loop = false;
+		bool explosion_loop = false;
+		bool freeze_loop = false;
+		bool ragdoll_loop = false;
+		bool rotate_cam_loop = false;
+
 		rate_limiter m_host_migration_rate_limit{ 1s, 20 };
 		rate_limiter m_play_sound_rate_limit{ 1s, 10 };
+		rate_limiter m_invites_rate_limit{ 10s, 2 };
 
 		bool exposed_desync_protection = false;
 		bool is_modder = false;
diff --git a/src/util/notify.hpp b/src/util/notify.hpp
index 115cc526..daf65b26 100644
--- a/src/util/notify.hpp
+++ b/src/util/notify.hpp
@@ -3,6 +3,8 @@
 #include "natives.hpp"
 #include "script.hpp"
 #include "session.hpp"
+#include "gta/enums.hpp"
+#include <script/HudColor.hpp>
 
 namespace big::notify
 {
@@ -26,9 +28,12 @@ namespace big::notify
 			g_notification_service->push_error("Protections", std::format("Blocked {} crash from unknown player", crash));
 		}
 
-		if (auto plyr = g_player_service->get_by_id(player->m_player_id))
+		if (player)
 		{
-			session::add_infraction(plyr, Infraction::TRIED_CRASH_PLAYER);
+			if (auto plyr = g_player_service->get_by_id(player->m_player_id))
+			{
+				session::add_infraction(plyr, Infraction::TRIED_CRASH_PLAYER);
+			}
 		}
 	}
 
@@ -66,6 +71,15 @@ namespace big::notify
 		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME_STRING(player_name); // player name
 		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_LITERAL_STRING(msg); // content
 		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_TEXTURE_NAME_STRING(HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(is_team ? "MP_CHAT_TEAM" : "MP_CHAT_ALL")); // scope
+		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(false); // teamOnly
+		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT((int)HudColor::HUD_COLOUR_PURE_WHITE); // eHudColour
+		GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
+		GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(scaleform, "SET_FOCUS");
+		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(1); // VISIBLE_STATE_DEFAULT
+		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0); // scopeType (unused)
+		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0); // scope (unused)
+		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME_STRING(player_name); // player
+		GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT((int)HudColor::HUD_COLOUR_PURE_WHITE); // eHudColour
 		GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
 		GRAPHICS::DRAW_SCALEFORM_MOVIE_FULLSCREEN(scaleform, 255, 255, 255, 255, 0);
 	}
diff --git a/src/util/session.hpp b/src/util/session.hpp
index 46454a1a..844b9dd3 100644
--- a/src/util/session.hpp
+++ b/src/util/session.hpp
@@ -13,9 +13,26 @@
 #include "services/api/api_service.hpp"
 #include "thread_pool.hpp"
 #include "fiber_pool.hpp"
+#include "packet.hpp"
+#include "gta_util.hpp"
+
+#include <network/Network.hpp>
+#include <network/snConnectToPeerTask.hpp>
+#include <rage/rlScHandle.hpp>
+#include <rage/rlQueryPresenceAttributesContext.hpp>
 
 namespace big::session
 {
+	static void gamer_handle_serialize(rage::rlGamerHandle& hnd, rage::datBitBuffer& buf)
+	{
+		buf.Write<uint8_t>(*reinterpret_cast<uint8_t*>(&hnd.m_platform), 8);
+		if (*reinterpret_cast<uint8_t*>(&hnd.m_platform) == 3)
+		{
+			buf.WriteInt64(*(int64_t*)&hnd.m_rockstar_id, 64);
+			buf.Write<uint8_t>(*reinterpret_cast<uint8_t*>(reinterpret_cast<__int64>(&hnd) + 9), 8);
+		}
+	}
+
 	inline void join_type(eSessionType session)
 	{
 		*script_global(2695915).as<int*>() = (session == eSessionType::SC_TV ? 1 : 0); // If SC TV Then Enable Spectator Mode
@@ -88,13 +105,14 @@ namespace big::session
 		rage::rlGamerHandle player_handle(rid);
 		rage::rlSessionByGamerTaskResult result;
 		bool success = false;
-		int state = 0;
+		rage::rlTaskStatus state{};
+
 		if (g_pointers->m_start_get_session_by_gamer_handle(0, &player_handle, 1, &result, 1, &success, &state))
 		{
-			while (state == 1)
+			while (state.status == 1)
 				script::get_current()->yield();
 
-			if (state == 3 && success)
+			if (state.status == 3 && success)
 			{
 				join_session(result.m_session_info);
 				return;
@@ -104,6 +122,120 @@ namespace big::session
 		g_notification_service->push_error("RID Joiner", "Target player is offline?");
 	}
 
+	inline void kick_by_rockstar_id(uint64_t rid)
+	{
+		rage::rlGamerHandle player_handle(rid);
+		rage::rlScHandle socialclub_handle(rid);
+		rage::rlSessionByGamerTaskResult get_session_result;
+		bool get_session_success = false;
+		rage::rlTaskStatus get_session_state{};
+
+		rage::snConnectToPeerTaskData connect_to_peer_data{};
+		rage::snConnectToPeerTaskResult connect_to_peer_result{};
+		rage::rlTaskStatus connect_to_peer_status{};
+
+		rage::rlQueryPresenceAttributesContext query_presence_attributes_context{};
+		rage::rlTaskStatus query_presence_attributes_status{};
+		rage::rlGamerInfoBase peer_address{};
+
+		query_presence_attributes_context.m_presence_attibute_type = 3; // string
+		strcpy(query_presence_attributes_context.m_presence_attribute_key, "peeraddr");
+
+		if (!g_pointers->m_start_get_presence_attributes(0, &socialclub_handle, &query_presence_attributes_context, 1, &query_presence_attributes_status))
+		{
+			g_notification_service->push_error("RID Kick", "Cannot start the query presence attributes rline task");
+			return;
+		}
+
+		while (query_presence_attributes_status.status == 1)
+			script::get_current()->yield();
+
+		if (query_presence_attributes_status.status != 3)
+		{
+			g_notification_service->push_error("RID Kick", "Querying presence attributes failed");
+			return;
+		}
+
+		g_pointers->m_decode_peer_info(&peer_address, query_presence_attributes_context.m_presence_attribute_value, nullptr);
+
+		if (!g_pointers->m_start_get_session_by_gamer_handle(0, &player_handle, 1, &get_session_result, 1, &get_session_success, &get_session_state))
+		{
+			g_notification_service->push_error("RID Kick", "Cannot start the get session by gamer handle task");
+			return;
+		}
+
+		while (get_session_state.status == 1)
+			script::get_current()->yield();
+
+		if (get_session_state.status != 3 || !get_session_success)
+		{
+			g_notification_service->push_error("RID Kick", "Getting session info failed");
+			return;
+		}
+
+		connect_to_peer_data.m_unk = 0;
+		connect_to_peer_data.m_reason = 5;
+		connect_to_peer_data.m_session_token = 0; // get_session_result.m_session_info.m_session_token; still works?
+
+		if (!g_pointers->m_connect_to_peer(gta_util::get_network()->m_game_session.m_net_connection_mgr, &peer_address, &connect_to_peer_data, &connect_to_peer_result, &connect_to_peer_status))
+		{
+			g_notification_service->push_error("RID Kick", "Failed to start a connection with player");
+			return;
+		}
+
+		g_notification_service->push("RID Kick", "Connecting to player...");
+
+		while (connect_to_peer_status.status == 1)
+			script::get_current()->yield();
+
+		if (connect_to_peer_status.status != 3)
+		{
+			g_notification_service->push_warning("RID Kick", "Failed to connect to player, not attempting to send gamer instruction kick");
+		}
+		else
+		{
+			// you can send a MsgTransitionLaunchNotify with an invalid connection ID to crash the player instead (remove the session obtaining code above to make it work in SP)
+			packet msg;
+			msg.write_message(rage::eNetMessage::MsgTransitionGamerInstruction);
+			gamer_handle_serialize(player_handle, msg.m_buffer); // src
+			gamer_handle_serialize(player_handle, msg.m_buffer); // target
+			msg.write<bool>(false, 1); // string extend
+			msg.write<int>(0, 7); // string length
+			msg.write<int>(8, 32); // instruction type
+			msg.write<int>(0, 32);
+			msg.write<int>(0, 32);
+			msg.send(connect_to_peer_result.m_peer_id, gta_util::get_network()->m_transition_session_ptr->m_connection_identifier);
+			g_notification_service->push("RID Kick", "Sent gamer instruction kick");
+		}
+
+		memset(&connect_to_peer_result, 0, sizeof(connect_to_peer_result));
+		connect_to_peer_status.status = 0;
+
+		if (!g_pointers->m_connect_to_peer(gta_util::get_network()->m_game_session.m_net_connection_mgr, &get_session_result.m_session_info.m_net_player_data, &connect_to_peer_data, &connect_to_peer_result, &connect_to_peer_status))
+		{
+			g_notification_service->push_error("RID Kick", "Failed to start a connection with the host");
+			return;
+		}
+
+		g_notification_service->push("RID Kick", "Connecting to host...");
+
+		while (connect_to_peer_status.status == 1)
+			script::get_current()->yield();
+
+		if (connect_to_peer_status.status != 3)
+		{
+			g_notification_service->push_error("RID Kick", "Failed to connect to the host");
+			return;
+		}
+
+		packet msg{};
+		msg.write_message(rage::eNetMessage::MsgLostConnectionToHost);
+		msg.write<uint64_t>(get_session_result.m_session_info.m_unk, 64);
+		gamer_handle_serialize(player_handle, msg);
+		msg.send(connect_to_peer_result.m_peer_id, gta_util::get_network()->m_game_session_ptr->m_connection_identifier);
+		g_notification_service->push("RID Kick", "Sent lost connection kick");
+	}
+
 	inline void join_by_username(std::string username)
 	{
 		g_thread_pool->push([username]
@@ -121,6 +253,23 @@ namespace big::session
 		});
 	}
 
+	inline void kick_by_username(std::string username)
+	{
+		g_thread_pool->push([username]
+		{
+			uint64_t rid;
+			if (g_api_service->get_rid_from_username(username, rid))
+			{
+				g_fiber_pool->queue_job([rid]
+				{
+					kick_by_rockstar_id(rid);
+				});
+				return;
+			}
+			g_notification_service->push_error("RID Kick", "Target player is offline?");
+		});
+	}
+
 	inline void add_infraction(player_ptr player, Infraction infraction)
 	{
 		auto plyr = g_player_database_service->get_or_create_player(player);
diff --git a/src/util/toxic.hpp b/src/util/toxic.hpp
index f3b863a9..8c59c3f0 100644
--- a/src/util/toxic.hpp
+++ b/src/util/toxic.hpp
@@ -8,8 +8,10 @@
 #include "util/scripts.hpp"
 #include "services/gta_data/gta_data_service.hpp"
 #include "util/system.hpp"
+
 #include <network/Network.hpp>
 #include <network/netTime.hpp>
+#include <script/globals/GPBD_FM_3.hpp>
 
 #include <timeapi.h>
 #pragma comment(lib, "winmm.lib")
diff --git a/src/views/network/view_player_database.cpp b/src/views/network/view_player_database.cpp
index 3d8a3e32..27f82637 100644
--- a/src/views/network/view_player_database.cpp
+++ b/src/views/network/view_player_database.cpp
@@ -123,6 +123,11 @@ namespace big
 					}
 				}
 
+				components::button("Kick", []
+				{
+					session::kick_by_rockstar_id(current_player.rockstar_id);
+				});
+
 				components::button("Join Session", []
 				{
 					session::join_by_rockstar_id(current_player.rockstar_id);
diff --git a/src/views/network/view_session.cpp b/src/views/network/view_session.cpp
index 0c8e461a..dd286d91 100644
--- a/src/views/network/view_session.cpp
+++ b/src/views/network/view_session.cpp
@@ -9,9 +9,11 @@
 #include "core/data/apartment_names.hpp"
 #include "core/data/warehouse_names.hpp"
 #include "core/data/command_access_levels.hpp"
-#include <network/Network.hpp>
 #include "hooking.hpp"
 
+#include <network/Network.hpp>
+#include <script/globals/GPBD_FM_3.hpp>
+
 namespace big
 {
 	void view::session()
@@ -22,6 +24,11 @@ namespace big
 		{
 			session::join_by_rockstar_id(rid);
 		});
+		ImGui::SameLine();
+		components::button("Kick by RID", []
+		{
+			session::kick_by_rockstar_id(rid);
+		});
 
 		static char username[20];
 		ImGui::InputText("Input Username", username, sizeof(username));
@@ -29,7 +36,12 @@ namespace big
 		{
 			session::join_by_username(username);
 		};
-    
+		ImGui::SameLine();
+		if (components::button("Kick by Username"))
+		{
+			session::kick_by_username(username);
+		};
+
 		static char base64[500]{};
 		ImGui::InputText("Session Info", base64, sizeof(base64));
 		components::button("Join Session Info", []
@@ -200,6 +212,8 @@ namespace big
 		}
 
 		components::command_button<"killall">({ }, "Kill Everyone");
+		ImGui::SameLine();
+		components::command_button<"explodeall">({ }, "Explode Everyone");
 
 		ImGui::SameLine();
 
diff --git a/src/views/players/player/player_info.cpp b/src/views/players/player/player_info.cpp
index 74e8b019..ef53a14b 100644
--- a/src/views/players/player/player_info.cpp
+++ b/src/views/players/player/player_info.cpp
@@ -1,6 +1,11 @@
 #include "views/view.hpp"
 #include "services/player_database/player_database_service.hpp"
 #include "core/data/command_access_levels.hpp"
+#include "core/scr_globals.hpp"
+#include "core/data/language_codes.hpp"
+#include <script/globals/GlobalPlayerBD.hpp>
+#include <script/globals/GPBD_FM_3.hpp>
+#include <script/globals/GPBD_FM.hpp>
 
 namespace big
 {
@@ -119,6 +124,35 @@ namespace big
 					net_player_data->m_external_port).data());
 			}
 
+			ImGui::Separator();
+
+			auto id = g_player_service->get_selected()->id();
+
+			if (id != -1)
+			{
+				auto& stats = scr_globals::gpbd_fm_1.as<GPBD_FM*>()->Entries[id].PlayerStats;
+				auto& boss_goon = scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[id].BossGoon;
+
+				if (boss_goon.Language >= 0 && boss_goon.Language < 13)
+					ImGui::Text("Language: %s", languages[boss_goon.Language].name);
+
+				ImGui::Text("CEO Name: %s", boss_goon.GangName);
+				ImGui::Text("MC Name: %s", boss_goon.MCName);
+				ImGui::Text("Money In Wallet: %d", stats.WalletBalance);
+				ImGui::Text("Money In Bank: %d", stats.Money - stats.WalletBalance);
+				ImGui::Text("Total Money: %d", stats.Money);
+				ImGui::Text("Rank: %d (RP %d)", stats.Rank, stats.RP);
+				ImGui::Text("K/D Ratio: %f", stats.KdRatio);
+				ImGui::Text("Kills On Players: %d", stats.KillsOnPlayers);
+				ImGui::Text("Deaths By Players: %d", stats.DeathsByPlayers);
+				ImGui::Text("Prostitutes Frequented: %d", stats.ProstitutesFrequented);
+				ImGui::Text("Lap Dances Bought: %d", stats.LapDancesBought);
+				ImGui::Text("Missions Created: %d", stats.MissionsCreated);
+				ImGui::Text("Meltdown Complete: %s", scr_globals::gpbd_fm_1.as<GPBD_FM*>()->Entries[id].MeltdownComplete ? "Yes" : "No"); // curious to see if anyone has actually played singleplayer
+
+				ImGui::Separator();
+			}
+
 			if (ImGui::BeginCombo("Chat Command Permissions", COMMAND_ACCESS_LEVELS[g_player_service->get_selected()->command_access_level.value_or(g.session.chat_command_default_access_level)]))
 			{
 				for (const auto& [type, name] : COMMAND_ACCESS_LEVELS)
diff --git a/src/views/players/player/player_kick.cpp b/src/views/players/player/player_kick.cpp
index 8897fb28..aed4ab0f 100644
--- a/src/views/players/player/player_kick.cpp
+++ b/src/views/players/player/player_kick.cpp
@@ -20,6 +20,7 @@ namespace big
 			components::player_command_button<"bailkick">(g_player_service->get_selected());
 			components::player_command_button<"nfkick">(g_player_service->get_selected());
 			components::player_command_button<"oomkick">(g_player_service->get_selected());
+			components::player_command_button<"gikick">(g_player_service->get_selected());
 			components::player_command_button<"shkick">(g_player_service->get_selected());
 			components::player_command_button<"endkick">(g_player_service->get_selected());
 			components::player_command_button<"desync">(g_player_service->get_selected());
diff --git a/src/views/players/player/player_misc.cpp b/src/views/players/player/player_misc.cpp
index 2a0c5e2e..67b99936 100644
--- a/src/views/players/player/player_misc.cpp
+++ b/src/views/players/player/player_misc.cpp
@@ -1,14 +1,4 @@
 #include "views/view.hpp"
-#include "util/teleport.hpp"
-#include "core/scr_globals.hpp"
-#include "util/ped.hpp"
-#include "util/vehicle.hpp"
-#include "util/globals.hpp"
-#include "services/pickups/pickup_service.hpp"
-#include "gta/net_object_mgr.hpp"
-#include "util/scripts.hpp"
-#include "util/session.hpp"
-#include "script_function.hpp"
 
 namespace big
 {
diff --git a/src/views/players/player/player_toxic.cpp b/src/views/players/player/player_toxic.cpp
index c147a7fe..cc270d6c 100644
--- a/src/views/players/player/player_toxic.cpp
+++ b/src/views/players/player/player_toxic.cpp
@@ -11,6 +11,8 @@ namespace big
 		if (ImGui::TreeNode("Toxic"))
 		{
 			components::player_command_button<"kill">(g_player_service->get_selected(), {});
+			ImGui::SameLine();
+			components::player_command_button<"explode">(g_player_service->get_selected(), {});
 
 			components::player_command_button<"ceokick">(g_player_service->get_selected(), {});
 			ImGui::SameLine();
@@ -122,6 +124,18 @@ namespace big
 			if (ImGui::IsItemHovered())
 				ImGui::SetTooltip("This cannot be reversed. Use with caution");
 
+			ImGui::Checkbox("Kill Loop", &g_player_service->get_selected()->kill_loop);
+			ImGui::SameLine();
+			ImGui::Checkbox("Explosion Loop", &g_player_service->get_selected()->explosion_loop);
+			ImGui::SameLine();
+			ImGui::Checkbox("Freeze Loop", &g_player_service->get_selected()->freeze_loop);
+
+			ImGui::Checkbox("Ragdoll Loop", &g_player_service->get_selected()->ragdoll_loop);
+			ImGui::SameLine();
+			ImGui::Checkbox("Rotate Cam Loop", &g_player_service->get_selected()->rotate_cam_loop);
+			if (ImGui::IsItemHovered())
+				ImGui::SetTooltip("Also brings the player out of godmode if the event isn't blocked");
+
 			ImGui::TreePop();
 		}
 	}
diff --git a/src/views/settings/view_notification_settings.cpp b/src/views/settings/view_notification_settings.cpp
deleted file mode 100644
index c25a1b8f..00000000
--- a/src/views/settings/view_notification_settings.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-#include "views/view.hpp"
-
-namespace big
-{
-	void draw_pair_option(const std::string_view name, decltype(g.notifications.gta_thread_kill)& option)
-	{
-		ImGui::Text("%s", name.data());
-
-		ImGui::PushID(name.data());
-		ImGui::Checkbox("Log", &option.log);
-		ImGui::Checkbox("Notify", &option.notify);
-		ImGui::PopID();
-	}
-
-	void view::notification_settings()
-	{
-		components::sub_title("GTA Threads");
-
-		draw_pair_option("Terminate", g.notifications.gta_thread_kill);
-		draw_pair_option("Start", g.notifications.gta_thread_start);
-
-		components::sub_title("Network Player Manager");
-
-		ImGui::Text("Player Join");
-
-		ImGui::Checkbox("Above Map", &g.notifications.player_join.above_map);
-		ImGui::Checkbox("Log", &g.notifications.player_join.log);
-		ImGui::Checkbox("Notify", &g.notifications.player_join.notify);
-
-		draw_pair_option("Player Leave", g.notifications.player_leave);
-
-		draw_pair_option("Init", g.notifications.network_player_mgr_init);
-		draw_pair_option("Shutdown", g.notifications.network_player_mgr_shutdown);
-
-		components::sub_title("Received Event");
-
-		auto& received_event = g.notifications.received_event;
-
-		ImGui::BeginGroup();
-		draw_pair_option("Clear Ped Tasks", received_event.clear_ped_task);
-		draw_pair_option("Kick Votes Notification", received_event.kick_vote);
-		draw_pair_option("Detect Modder Events", received_event.modder_detect);
-		ImGui::EndGroup();
-
-		ImGui::SameLine();
-
-		ImGui::BeginGroup();
-		draw_pair_option("Report Cash Spawn", received_event.report_cash_spawn);
-		draw_pair_option("Request Control Event", received_event.request_control_event);
-		draw_pair_option("Vehicle Temp Action", received_event.vehicle_temp_action);
-		ImGui::EndGroup();
-
-		components::sub_title("Script Event Handler");
-
-		auto& script_event_handler = g.notifications.script_event_handler;
-
-		ImGui::BeginGroup();
-		draw_pair_option("Bounty", script_event_handler.bounty);
-		draw_pair_option("CEO Ban", script_event_handler.ceo_ban);
-		draw_pair_option("CEO Kick", script_event_handler.ceo_kick);
-		draw_pair_option("CEO Money", script_event_handler.ceo_money);
-		draw_pair_option("Destroy Personal Vehicle", script_event_handler.personal_vehicle_destroyed);
-		draw_pair_option("Fake Deposit", script_event_handler.fake_deposit);
-		draw_pair_option("Force Mission", script_event_handler.force_mission);
-		draw_pair_option("Force Teleport", script_event_handler.force_teleport);
-		ImGui::EndGroup();
-
-		ImGui::SameLine();
-
-		ImGui::BeginGroup();
-		draw_pair_option("GTA Banner", script_event_handler.gta_banner);
-		draw_pair_option("MC Teleport", script_event_handler.mc_teleport);
-		draw_pair_option("Network Bail", script_event_handler.network_bail);
-		draw_pair_option("Remote Off Radar", script_event_handler.remote_off_radar);
-		draw_pair_option("Rotate Cam", script_event_handler.rotate_cam);
-		draw_pair_option("Send to Cutscene", script_event_handler.send_to_cutscene);
-		draw_pair_option("Send to Location", script_event_handler.send_to_location);
-		draw_pair_option("Sound Spam", script_event_handler.sound_spam);
-		ImGui::EndGroup();
-
-		ImGui::SameLine();
-
-		ImGui::BeginGroup();
-		draw_pair_option("Spectate", script_event_handler.spectate);
-		draw_pair_option("Transaction Error", script_event_handler.transaction_error);
-		draw_pair_option("TSE Crash", script_event_handler.crash);
-		draw_pair_option("TSE Freeze", script_event_handler.tse_freeze);
-		draw_pair_option("TSE Sender Mismatch", script_event_handler.tse_sender_mismatch);
-		draw_pair_option("Vehicle Kick", script_event_handler.vehicle_kick);
-		draw_pair_option("Wanted Level", script_event_handler.clear_wanted_level);
-		ImGui::EndGroup();
-
-		ImGui::SameLine();
-
-		ImGui::BeginGroup();
-		draw_pair_option("Teleport To Warehouse", script_event_handler.teleport_to_warehouse);
-		draw_pair_option("Start Activity", script_event_handler.start_activity);
-		draw_pair_option("Null Function Kick", script_event_handler.null_function_kick);
-		draw_pair_option("Send SMS", script_event_handler.send_sms);
-		ImGui::EndGroup();
-
-		components::sub_title("Other");
-
-		draw_pair_option("Reports", g.notifications.reports);
-		draw_pair_option("Transaction Error / Rate Limit", g.notifications.transaction_rate_limit);
-		draw_pair_option("Mismatch sync type", g.notifications.mismatch_sync_type);
-		draw_pair_option("Out of allowed range sync type", g.notifications.out_of_allowed_range_sync_type);
-		draw_pair_option("Invalid sync", g.notifications.invalid_sync);
-	}
-
-}
diff --git a/src/views/settings/view_protection_settings.cpp b/src/views/settings/view_protection_settings.cpp
index 6e78c6a2..35cb6719 100644
--- a/src/views/settings/view_protection_settings.cpp
+++ b/src/views/settings/view_protection_settings.cpp
@@ -6,24 +6,12 @@ namespace big
 	{
 		ImGui::BeginGroup();
 		ImGui::Checkbox("Bounty", &g.protections.script_events.bounty);
-		ImGui::Checkbox("CEO Ban", &g.protections.script_events.ceo_ban);
-		ImGui::Checkbox("CEO Kick", &g.protections.script_events.ceo_kick);
 		ImGui::Checkbox("CEO Money", &g.protections.script_events.ceo_money);
-		ImGui::Checkbox("TSE Crash", &g.protections.script_events.crash);
 		ImGui::Checkbox("Fake Deposit", &g.protections.script_events.fake_deposit);
 		ImGui::Checkbox("Force Mission", &g.protections.script_events.force_mission);
-		ImGui::EndGroup();
-
-		ImGui::SameLine();
-
-		ImGui::BeginGroup();
 		ImGui::Checkbox("Force Teleport", &g.protections.script_events.force_teleport);
 		ImGui::Checkbox("GTA Banner", &g.protections.script_events.gta_banner);
 		ImGui::Checkbox("MC Teleport", &g.protections.script_events.mc_teleport);
-		ImGui::Checkbox("Network Bail", &g.protections.script_events.network_bail);
-		ImGui::Checkbox("Personal Vehicle Destroyed", &g.protections.script_events.personal_vehicle_destroyed);
-		ImGui::Checkbox("Remote Off Radar", &g.protections.script_events.remote_off_radar);
-		ImGui::Checkbox("Rotate Cam", &g.protections.script_events.rotate_cam);
 		ImGui::EndGroup();
 
 		ImGui::SameLine();
@@ -32,20 +20,25 @@ namespace big
 		ImGui::Checkbox("Send to Cutscene", &g.protections.script_events.send_to_cutscene);
 		ImGui::Checkbox("Send to Location", &g.protections.script_events.send_to_location);
 		ImGui::Checkbox("Sound Spam", &g.protections.script_events.sound_spam);
-		ImGui::Checkbox("Spectate", &g.protections.script_events.spectate);
-		ImGui::Checkbox("Transaction Error", &g.protections.script_events.transaction_error);
-		ImGui::Checkbox("Vehicle Kick", &g.protections.script_events.vehicle_kick);
-		ImGui::Checkbox("Wanted Level", &g.protections.script_events.clear_wanted_level);
+		ImGui::Checkbox("Personal Vehicle Destroyed", &g.protections.script_events.personal_vehicle_destroyed);
+		ImGui::Checkbox("Remote Off Radar", &g.protections.script_events.remote_off_radar);
+		ImGui::Checkbox("Rotate Cam", &g.protections.script_events.rotate_cam);
+		ImGui::Checkbox("Teleport To Warehouse", &g.protections.script_events.teleport_to_warehouse);
 		ImGui::EndGroup();
 
 		ImGui::SameLine();
 
 		ImGui::BeginGroup();
-		ImGui::Checkbox("Teleport To Warehouse", &g.protections.script_events.teleport_to_warehouse);
 		ImGui::Checkbox("Start Activity", &g.protections.script_events.start_activity);
 		ImGui::Checkbox("Send SMS", &g.protections.script_events.send_sms);
-		components::script_patch_checkbox("Script Host Kick", &g.protections.script_host_kick);
+		ImGui::Checkbox("Spectate", &g.protections.script_events.spectate);
+		ImGui::Checkbox("Vehicle Kick", &g.protections.script_events.vehicle_kick);
+		ImGui::Checkbox("Wanted Level", &g.protections.script_events.clear_wanted_level);
 		ImGui::Checkbox("Desync Kick", &g.protections.desync_kick);
+		ImGui::EndGroup();
+		ImGui::SameLine();
+
+		ImGui::BeginGroup();
 		ImGui::Checkbox("RID Join", &g.protections.rid_join);
 		if (ImGui::IsItemHovered())
 			ImGui::SetTooltip("This will block anyone trying to join, kick or crash you with your Rockstar ID, including your friends");
diff --git a/src/views/settings/view_reaction_settings.cpp b/src/views/settings/view_reaction_settings.cpp
new file mode 100644
index 00000000..2353eb40
--- /dev/null
+++ b/src/views/settings/view_reaction_settings.cpp
@@ -0,0 +1,126 @@
+#include "views/view.hpp"
+
+namespace big
+{
+	void draw_pair_option(const std::string_view name, decltype(g.notifications.gta_thread_kill)& option)
+	{
+		ImGui::Text("%s", name.data());
+
+		ImGui::PushID(name.data());
+		ImGui::Checkbox("Log", &option.log);
+		ImGui::Checkbox("Notify", &option.notify);
+		ImGui::PopID();
+	}
+
+	void draw_reaction(reaction& reaction)
+	{
+		if (ImGui::TreeNode(reaction.m_event_name))
+		{
+			ImGui::Checkbox("Announce In Chat", &reaction.announce_in_chat);
+			ImGui::Checkbox("Notify", &reaction.notify);
+			ImGui::Checkbox("Log", &reaction.log);
+			ImGui::Checkbox("Add Player To Database", &reaction.add_to_player_db);
+			if (reaction.add_to_player_db)
+				ImGui::Checkbox("Block Joins", &reaction.block_joins);
+			ImGui::Checkbox("Kick Player", &reaction.kick);
+			ImGui::TreePop();
+		}
+	}
+
+	// TODO code duplication
+	void draw_interloper_reaction(interloper_reaction& reaction)
+	{
+		if (ImGui::TreeNode(reaction.m_event_name))
+		{
+			ImGui::Checkbox("Announce In Chat", &reaction.announce_in_chat);
+			ImGui::Checkbox("Notify", &reaction.notify);
+			ImGui::Checkbox("Log", &reaction.log);
+			ImGui::Checkbox("Add Attacker To Database", &reaction.add_to_player_db);
+			if (reaction.add_to_player_db)
+				ImGui::Checkbox("Block Joins", &reaction.block_joins);
+			ImGui::Checkbox("Kick Attacker", &reaction.kick);
+
+			if (reaction.m_blockable || reaction.m_karmaable)
+				ImGui::Separator();
+
+			if (reaction.m_blockable)
+				ImGui::Checkbox("Block", &reaction.block);
+
+			if (reaction.m_karmaable)
+				ImGui::Checkbox("Karma", &reaction.karma);
+
+			ImGui::TreePop();
+		}
+	}
+
+	void view::reaction_settings()
+	{
+		components::title("Reactions");
+		draw_reaction(g.reactions.bounty);
+		draw_reaction(g.reactions.ceo_money);
+		draw_reaction(g.reactions.clear_wanted_level);
+		draw_reaction(g.reactions.crash);
+		draw_reaction(g.reactions.destroy_personal_vehicle);
+		draw_reaction(g.reactions.fake_deposit);
+		draw_reaction(g.reactions.force_mission);
+		draw_reaction(g.reactions.force_teleport);
+		draw_reaction(g.reactions.give_collectible);
+		draw_reaction(g.reactions.gta_banner);
+		draw_reaction(g.reactions.mc_teleport);
+		draw_reaction(g.reactions.network_bail);
+		draw_reaction(g.reactions.null_function_kick);
+		draw_reaction(g.reactions.personal_vehicle_destroyed);
+		draw_reaction(g.reactions.remote_off_radar);
+		draw_reaction(g.reactions.rotate_cam);
+		draw_reaction(g.reactions.send_to_cutscene);
+		draw_reaction(g.reactions.send_to_location);
+		draw_reaction(g.reactions.sound_spam);
+		draw_reaction(g.reactions.spectate_notification);
+		draw_reaction(g.reactions.start_activity);
+		draw_reaction(g.reactions.teleport_to_warehouse);
+		draw_reaction(g.reactions.transaction_error);
+		draw_reaction(g.reactions.tse_freeze);
+		draw_reaction(g.reactions.tse_sender_mismatch);
+		draw_reaction(g.reactions.vehicle_kick);
+		ImGui::Separator();
+		draw_reaction(g.reactions.clear_ped_tasks);
+		draw_reaction(g.reactions.remote_ragdoll);
+		draw_reaction(g.reactions.kick_vote);
+		draw_reaction(g.reactions.modder_detection);
+		draw_reaction(g.reactions.report);
+		draw_reaction(g.reactions.report_cash_spawn);
+		draw_reaction(g.reactions.request_control_event);
+		ImGui::Separator();
+		draw_reaction(g.reactions.lost_connection_kick);
+		draw_reaction(g.reactions.gamer_instruction_kick);
+		draw_interloper_reaction(g.reactions.lost_connection_kick_others);
+		draw_interloper_reaction(g.reactions.breakup_others);
+
+		components::title("Notifications");
+		components::sub_title("GTA Threads");
+
+		draw_pair_option("Terminate", g.notifications.gta_thread_kill);
+		draw_pair_option("Start", g.notifications.gta_thread_start);
+
+		components::sub_title("Network Player Manager");
+
+		ImGui::Text("Player Join");
+
+		ImGui::Checkbox("Above Map", &g.notifications.player_join.above_map);
+		ImGui::Checkbox("Log", &g.notifications.player_join.log);
+		ImGui::Checkbox("Notify", &g.notifications.player_join.notify);
+
+		draw_pair_option("Player Leave", g.notifications.player_leave);
+
+		draw_pair_option("Init", g.notifications.network_player_mgr_init);
+		draw_pair_option("Shutdown", g.notifications.network_player_mgr_shutdown);
+
+		components::sub_title("Other");
+
+		draw_pair_option("Transaction Error / Rate Limit", g.notifications.transaction_rate_limit);
+		draw_pair_option("Mismatch sync type", g.notifications.mismatch_sync_type);
+		draw_pair_option("Out of allowed range sync type", g.notifications.out_of_allowed_range_sync_type);
+		draw_pair_option("Invalid sync", g.notifications.invalid_sync);
+	}
+
+}
diff --git a/src/views/view.hpp b/src/views/view.hpp
index c249fe64..ab6f46be 100644
--- a/src/views/view.hpp
+++ b/src/views/view.hpp
@@ -19,7 +19,7 @@ namespace big
 		static void gui_settings();
 		static void handling_current_profile();
 		static void handling_saved_profiles();
-		static void notification_settings();
+		static void reaction_settings();
 		static void protection_settings();
 		static void heading();
 		static void mobile();
diff --git a/src/views/world/view_spawn_ped.cpp b/src/views/world/view_spawn_ped.cpp
index 6b11be92..b0efd473 100644
--- a/src/views/world/view_spawn_ped.cpp
+++ b/src/views/world/view_spawn_ped.cpp
@@ -131,8 +131,9 @@ namespace big
 				weapon.m_weapon_type == weapon_type_arr[selected_ped_weapon_type]
 			) {
 				if (
-					selected_ped_weapon_hash == 0 ||
-					weapon.m_hash == selected_ped_weapon_hash
+					(selected_ped_weapon_hash == 0 ||
+					weapon.m_hash == selected_ped_weapon_hash)
+					&& weapon.m_hash != RAGE_JOAAT("WEAPON_UNARMED")
 				) {
 					WEAPON::GIVE_WEAPON_TO_PED(ped, weapon.m_hash, 9999, false, selected_ped_weapon_hash != 0);
 				}