diff --git a/Configs/RenX.Logging.ini b/Configs/RenX.Logging.ini
index 2d1fc92..ff5174f 100644
--- a/Configs/RenX.Logging.ini
+++ b/Configs/RenX.Logging.ini
@@ -69,6 +69,7 @@ MuteOwnExecute=true
 ;GamePublic=true
 ;GameOverPublic=true
 ;ExecutePublic=false
+;PlayerCommandPuiblic=false
 ;SubscribePublic=false
 ;RCONPublic=false
 ;AdminLoginPublic=true
@@ -105,6 +106,8 @@ MuteOwnExecute=true
 ;PartFormat=12[Part] {NAME} left the {TEAML}.
 ;KickFormat=04[Kick] {NAME} was 04kicked (04{MESSAGE})
 ;PlayerExecuteFormat={NAME}07 executed: {MESSAGE}
+;PlayerCommandSuccessFormat={NAME}: 10{MESSAGE}
+;PlayerCommandFailFormat=
 ;PlayerFormat=12[Player] {MESSAGE}
 ;NameChangeFormat={NAME} changed their name to {NNAME}.
 ;TeamChangeFormat={NAME} switched teams!
diff --git a/Release/Plugins/RenX.Core.lib b/Release/Plugins/RenX.Core.lib
index 8946302..a8e0d29 100644
Binary files a/Release/Plugins/RenX.Core.lib and b/Release/Plugins/RenX.Core.lib differ
diff --git a/RenX.Core/RenX_Plugin.cpp b/RenX.Core/RenX_Plugin.cpp
index beeb3dc..49ade7f 100644
--- a/RenX.Core/RenX_Plugin.cpp
+++ b/RenX.Core/RenX_Plugin.cpp
@@ -131,6 +131,11 @@ void RenX::Plugin::RenX_OnExecute(Server *, const PlayerInfo *, const Jupiter::R
 	return;
 }
 
+void RenX::Plugin::RenX_OnPlayerCommand(Server *, const PlayerInfo *, const Jupiter::ReadableString &, GameCommand *)
+{
+	return;
+}
+
 void RenX::Plugin::RenX_OnSpeedHack(Server *, const PlayerInfo *)
 {
 	return;
diff --git a/RenX.Core/RenX_Plugin.h b/RenX.Core/RenX_Plugin.h
index f0f3c20..915394f 100644
--- a/RenX.Core/RenX_Plugin.h
+++ b/RenX.Core/RenX_Plugin.h
@@ -36,6 +36,7 @@ namespace RenX
 	struct BuildingInfo;
 	class Plugin;
 	class Server;
+	class GameCommand;
 
 	class RENX_API Plugin : public Jupiter::Plugin
 	{
@@ -64,6 +65,7 @@ namespace RenX
 		virtual void RenX_OnRank(Server *server, const PlayerInfo *player);
 		virtual void RenX_OnDev(Server *server, const PlayerInfo *player);
 		virtual void RenX_OnExecute(Server *server, const PlayerInfo *player, const Jupiter::ReadableString &command);
+		virtual void RenX_OnPlayerCommand(Server *server, const PlayerInfo *player, const Jupiter::ReadableString &message, GameCommand *command);
 		virtual void RenX_OnSpeedHack(Server *server, const PlayerInfo *player);
 		virtual void RenX_OnPlayer(Server *server, const Jupiter::ReadableString &raw);
 		
diff --git a/RenX.Core/RenX_Server.cpp b/RenX.Core/RenX_Server.cpp
index ae6450d..019a14c 100644
--- a/RenX.Core/RenX_Server.cpp
+++ b/RenX.Core/RenX_Server.cpp
@@ -1088,10 +1088,10 @@ unsigned int RenX::Server::getCommandCount() const
 	return RenX::Server::commands.size();
 }
 
-unsigned int RenX::Server::triggerCommand(const Jupiter::ReadableString &trigger, RenX::PlayerInfo *player, const Jupiter::ReadableString &parameters)
+RenX::GameCommand *RenX::Server::triggerCommand(const Jupiter::ReadableString &trigger, RenX::PlayerInfo *player, const Jupiter::ReadableString &parameters)
 {
-	unsigned int r = 0;
 	RenX::GameCommand *cmd;
+
 	for (size_t i = 0; i < RenX::Server::commands.size(); i++)
 	{
 		cmd = RenX::Server::commands.get(i);
@@ -1101,10 +1101,12 @@ unsigned int RenX::Server::triggerCommand(const Jupiter::ReadableString &trigger
 				cmd->trigger(this, player, parameters);
 			else
 				RenX::Server::sendMessage(player, "Access Denied."_jrs);
-			++r;
+
+			return cmd;
 		}
 	}
-	return r;
+
+	return nullptr;
 }
 
 void RenX::Server::addCommand(RenX::GameCommand *command)
@@ -2734,7 +2736,10 @@ void RenX::Server::processLine(const Jupiter::ReadableString &line)
 						RenX::PlayerInfo *player = parseGetPlayerOrAdd(tokens.getToken(2));
 						Jupiter::ReferenceString message = gotoToken(3);
 
-						this->triggerCommand(Jupiter::ReferenceString::getWord(message, 0, WHITESPACE), player, Jupiter::ReferenceString::gotoWord(message, 1, WHITESPACE));
+						RenX::GameCommand *command = this->triggerCommand(Jupiter::ReferenceString::getWord(message, 0, WHITESPACE), player, Jupiter::ReferenceString::gotoWord(message, 1, WHITESPACE));
+
+						for (size_t i = 0; i < xPlugins.size(); i++)
+							xPlugins.get(i)->RenX_OnPlayerCommand(this, player, message, command);
 					}
 					else
 					{
diff --git a/RenX.Core/RenX_Server.h b/RenX.Core/RenX_Server.h
index bb58ce4..e8b50d1 100644
--- a/RenX.Core/RenX_Server.h
+++ b/RenX.Core/RenX_Server.h
@@ -819,12 +819,13 @@ namespace RenX
 		unsigned int getCommandCount() const;
 
 		/**
-		* @brief Triggers commands matching a specified trigger.
+		* @brief Finds and triggers a matching GameCommand with the given parameters
 		*
-		* @param trigger Trigger of the command to fire.
-		* @return Number of commands triggered.
+		* @param trigger Trigger of the command to fire
+		* @param parameters Parameters to pass to the command
+		* @return Command executed if a match is found, nullptr otherwise.
 		*/
-		unsigned int triggerCommand(const Jupiter::ReadableString &trigger, RenX::PlayerInfo *player, const Jupiter::ReadableString &parameters);
+		RenX::GameCommand *triggerCommand(const Jupiter::ReadableString &trigger, RenX::PlayerInfo *player, const Jupiter::ReadableString &parameters);
 
 		/**
 		* @brief Adds a command to the server's game command list.
diff --git a/RenX.Logging/RenX_Logging.cpp b/RenX.Logging/RenX_Logging.cpp
index e3bda27..5b57b15 100644
--- a/RenX.Logging/RenX_Logging.cpp
+++ b/RenX.Logging/RenX_Logging.cpp
@@ -83,6 +83,7 @@ bool RenX_LoggingPlugin::initialize()
 	RenX_LoggingPlugin::gamePublic = this->config.getBool(Jupiter::ReferenceString::empty, "GamePublic"_jrs, true);
 	RenX_LoggingPlugin::gameOverPublic = this->config.getBool(Jupiter::ReferenceString::empty, "GameOverPublic"_jrs, true);
 	RenX_LoggingPlugin::executePublic = this->config.getBool(Jupiter::ReferenceString::empty, "ExecutePublic"_jrs, false);
+	RenX_LoggingPlugin::playerCommandPublic = this->config.getBool(Jupiter::ReferenceString::empty, "PlayerCommandPublic"_jrs, false);
 	RenX_LoggingPlugin::subscribePublic = this->config.getBool(Jupiter::ReferenceString::empty, "SubscribePublic"_jrs, false);
 	RenX_LoggingPlugin::RCONPublic = this->config.getBool(Jupiter::ReferenceString::empty, "RCONPublic"_jrs, false);
 	RenX_LoggingPlugin::adminLoginPublic = this->config.getBool(Jupiter::ReferenceString::empty, "AdminLoginPublic"_jrs, true);
@@ -129,6 +130,11 @@ bool RenX_LoggingPlugin::initialize()
 	RenX_LoggingPlugin::playerExecuteFmt = this->config.get(Jupiter::ReferenceString::empty, "PlayerExecuteFormat"_jrs,
 		Jupiter::StringS::Format("%.*s" IRCCOLOR "07 executed: %.*s", RenX::tags->nameTag.size(), RenX::tags->nameTag.ptr(), RenX::tags->messageTag.size(), RenX::tags->messageTag.ptr()));
 
+	RenX_LoggingPlugin::playerCommandSuccessFmt = this->config.get(Jupiter::ReferenceString::empty, "PlayerCommandSuccessFormat"_jrs,
+		Jupiter::StringS::Format("%.*s" IRCCOLOR ": " IRCCOLOR "10%.*s", RenX::tags->nameTag.size(), RenX::tags->nameTag.ptr(), RenX::tags->messageTag.size(), RenX::tags->messageTag.ptr()));
+
+	RenX_LoggingPlugin::playerCommandFailFmt = this->config.get(Jupiter::ReferenceString::empty, "PlayerCommandFailFormat"_jrs);
+
 	RenX_LoggingPlugin::playerFmt = this->config.get(Jupiter::ReferenceString::empty, "PlayerFormat"_jrs,
 		Jupiter::StringS::Format(IRCCOLOR "12[Player]" IRCCOLOR " %.*s", RenX::tags->messageTag.size(), RenX::tags->messageTag.ptr()));
 
@@ -488,6 +494,8 @@ bool RenX_LoggingPlugin::initialize()
 	RenX::sanitizeTags(gameFmt);
 	RenX::sanitizeTags(executeFmt);
 	RenX::sanitizeTags(playerExecuteFmt);
+	RenX::sanitizeTags(playerCommandSuccessFmt);
+	RenX::sanitizeTags(playerCommandFailFmt);
 	RenX::sanitizeTags(devBotExecuteFmt);
 	RenX::sanitizeTags(hostChatFmt);
 	RenX::sanitizeTags(hostPageFmt);
@@ -669,6 +677,26 @@ void RenX_LoggingPlugin::RenX_OnExecute(RenX::Server *server, const RenX::Player
 	}
 }
 
+void RenX_LoggingPlugin::RenX_OnPlayerCommand(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &message, RenX::GameCommand *command)
+{
+	logFuncType func;
+	if (RenX_LoggingPlugin::playerCommandPublic)
+		func = &RenX::Server::sendLogChan;
+	else
+		func = &RenX::Server::sendAdmChan;
+
+	Jupiter::String msg = command != nullptr
+		? this->playerCommandSuccessFmt
+		: this->playerCommandFailFmt;
+
+	if (msg.isNotEmpty())
+	{
+		processTags(msg, server, player);
+		msg.replace(RenX::tags->INTERNAL_MESSAGE_TAG, message);
+		(server->*func)(msg);
+	}
+}
+
 void RenX_LoggingPlugin::RenX_OnPlayer(RenX::Server *server, const Jupiter::ReadableString &raw)
 {
 	logFuncType func;
diff --git a/RenX.Logging/RenX_Logging.h b/RenX.Logging/RenX_Logging.h
index 62bc01c..04e6a0b 100644
--- a/RenX.Logging/RenX_Logging.h
+++ b/RenX.Logging/RenX_Logging.h
@@ -33,6 +33,7 @@ public: // RenX::Plugin
 	void RenX_OnNameChange(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &newPlayerName) override;
 	void RenX_OnTeamChange(RenX::Server *server, const RenX::PlayerInfo *player, const RenX::TeamType &oldTeam) override;
 	void RenX_OnExecute(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &command) override;
+	void RenX_OnPlayerCommand(RenX::Server *server, const RenX::PlayerInfo *player, const Jupiter::ReadableString &message, RenX::GameCommand *command) override;
 	void RenX_OnSpeedHack(RenX::Server *server, const RenX::PlayerInfo *player) override;
 	void RenX_OnPlayer(RenX::Server *server, const Jupiter::ReadableString &raw) override;
 	
@@ -185,6 +186,7 @@ private:
 	unsigned int gameOverPublic : 1;
 	unsigned int gamePublic : 1;
 	unsigned int executePublic : 1;
+	unsigned int playerCommandPublic : 1;
 	unsigned int subscribePublic : 1;
 	unsigned int RCONPublic : 1;
 	unsigned int adminLoginPublic : 1;
@@ -277,6 +279,7 @@ private:
 	Jupiter::StringS gameOverScoreFmt;
 	Jupiter::StringS gameFmt;
 	Jupiter::StringS executeFmt, playerExecuteFmt, devBotExecuteFmt;
+	Jupiter::StringS playerCommandSuccessFmt, playerCommandFailFmt;
 	Jupiter::StringS subscribeFmt;
 	Jupiter::StringS rconFmt;
 	Jupiter::StringS adminLoginFmt;
