diff --git a/src/Components/Modules/Bans.cpp b/src/Components/Modules/Bans.cpp
index 8928494d..b84ed62f 100644
--- a/src/Components/Modules/Bans.cpp
+++ b/src/Components/Modules/Bans.cpp
@@ -5,6 +5,8 @@
 
 namespace Components
 {
+	const char* Bans::BanListFile = "userraw/bans.json";
+
 	// Have only one instance of IW4x read/write the file
 	std::unique_lock<Utils::NamedMutex> Bans::Lock()
 	{
@@ -89,7 +91,7 @@ namespace Components
 
 	void Bans::SaveBans(const BanList* list)
 	{
-		assert(list != nullptr);
+		assert(list);
 
 		const auto _ = Lock();
 
@@ -107,7 +109,8 @@ namespace Components
 				ipEntry.bytes[0] & 0xFF,
 				ipEntry.bytes[1] & 0xFF,
 				ipEntry.bytes[2] & 0xFF,
-				ipEntry.bytes[3] & 0xFF));
+				ipEntry.bytes[3] & 0xFF)
+			);
 		}
 
 		const nlohmann::json bans = nlohmann::json
@@ -116,18 +119,17 @@ namespace Components
 			{ "id", idVector },
 		};
 
-		FileSystem::FileWriter ("bans.json").write(bans.dump());
+		Utils::IO::WriteFile(BanListFile, bans.dump());
 	}
 
 	void Bans::LoadBans(BanList* list)
 	{
-		assert(list != nullptr);
+		assert(list);
 
 		const auto _ = Lock();
 
-		FileSystem::File bans("bans.json");
-
-		if (!bans.exists())
+		const auto bans = Utils::IO::ReadFile(BanListFile);
+		if (bans.empty())
 		{
 			Logger::Debug("bans.json does not exist");
 			return;
@@ -136,14 +138,20 @@ namespace Components
 		nlohmann::json banData;
 		try
 		{
-			banData = nlohmann::json::parse(bans.getBuffer());
+			banData = nlohmann::json::parse(bans);
 		}
-		catch (const nlohmann::json::parse_error& ex)
+		catch (const std::exception& ex)
 		{
 			Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what());
 			return;
 		}
 
+		if (!banData.contains("id") || !banData.contains("ip"))
+		{
+			Logger::PrintError(Game::CON_CHANNEL_ERROR, "bans.json contains invalid data\n");
+			return;
+		}
+
 		const auto& idList = banData["id"];
 		const auto& ipList = banData["ip"];
 
diff --git a/src/Components/Modules/Bans.hpp b/src/Components/Modules/Bans.hpp
index 0ba36d12..322a4a12 100644
--- a/src/Components/Modules/Bans.hpp
+++ b/src/Components/Modules/Bans.hpp
@@ -9,8 +9,6 @@ namespace Components
 
 		Bans();
 
-		static std::unique_lock<Utils::NamedMutex> Lock();
-
 		static void BanClient(Game::client_t* cl, const std::string& reason);
 		static void UnbanClient(SteamID id);
 		static void UnbanClient(Game::netIP_t ip);
@@ -25,6 +23,10 @@ namespace Components
 			std::vector<Game::netIP_t> ipList;
 		};
 
+		static const char* BanListFile;
+
+		static std::unique_lock<Utils::NamedMutex> Lock();
+
 		static void LoadBans(BanList* list);
 		static void SaveBans(const BanList* list);
 	};
diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp
index cff5b9d5..cd9c2c40 100644
--- a/src/Components/Modules/Chat.cpp
+++ b/src/Components/Modules/Chat.cpp
@@ -5,6 +5,8 @@
 
 #include "GSC/Script.hpp"
 
+#include <json.hpp>
+
 namespace Components
 {
 	Dvar::Var Chat::cg_chatWidth;
@@ -14,10 +16,19 @@ namespace Components
 	bool Chat::SendChat;
 
 	Utils::Concurrency::Container<Chat::muteList> Chat::MutedList;
+	const char* Chat::MutedListFile = "userraw/muted-users.json";
 
 	bool Chat::CanAddCallback = true;
 	std::vector<Scripting::Function> Chat::SayCallbacks;
 
+	// Have only one instance of IW4x read/write the file
+	std::unique_lock<Utils::NamedMutex> Chat::Lock()
+	{
+		static Utils::NamedMutex mutex{"iw4x-mute-list-lock"};
+		std::unique_lock lock{mutex};
+		return lock;
+	}
+
 	const char* Chat::EvaluateSay(char* text, Game::gentity_t* player, int mode)
 	{
 		SendChat = true;
@@ -236,7 +247,9 @@ namespace Components
 
 		Game::cgsArray[0].teamChatPos++;
 		if (Game::cgsArray[0].teamChatPos - Game::cgsArray[0].teamLastChatPos > chatHeight)
+		{
 			Game::cgsArray[0].teamLastChatPos = Game::cgsArray[0].teamChatPos + 1 - chatHeight;
+		}
 	}
 
 	__declspec(naked) void Chat::CG_AddToTeamChat_Stub()
@@ -259,7 +272,20 @@ namespace Components
 		const auto clientNum = ent - Game::g_entities;
 		const auto xuid = Game::svs_clients[clientNum].steamID;
 
-		const auto result = MutedList.access<bool>([&](muteList& clients)
+		const auto result = MutedList.access<bool>([&](const muteList& clients)
+		{
+			return clients.contains(xuid);
+		});
+
+		return result;
+	}
+
+	bool Chat::IsMuted(const Game::client_t* cl)
+	{
+		const auto clientNum = cl - Game::svs_clients;
+		const auto xuid = Game::svs_clients[clientNum].steamID;
+
+		const auto result = MutedList.access<bool>([&](const muteList& clients)
 		{
 			return clients.contains(xuid);
 		});
@@ -273,6 +299,7 @@ namespace Components
 		MutedList.access([&](muteList& clients)
 		{
 			clients.insert(xuid);
+			SaveMutedList(clients);
 		});
 
 		Logger::Print("{} was muted\n", client->name);
@@ -295,6 +322,67 @@ namespace Components
 				clients.clear();
 			else
 				clients.erase(id);
+
+			SaveMutedList(clients);
+		});
+	}
+
+	void Chat::SaveMutedList(const muteList& list)
+	{
+		const auto _ = Lock();
+
+		const nlohmann::json mutedUsers = nlohmann::json
+		{
+			{ "SteamID", list },
+		};
+
+		Utils::IO::WriteFile(MutedListFile, mutedUsers.dump());
+	}
+
+	void Chat::LoadMutedList()
+	{
+		const auto _ = Lock();
+
+		const auto mutedUsers = Utils::IO::ReadFile(MutedListFile);
+		if (mutedUsers.empty())
+		{
+			Logger::Debug("muted-users.json does not exist");
+			return;
+		}
+
+		nlohmann::json mutedUsersData;
+		try
+		{
+			mutedUsersData = nlohmann::json::parse(mutedUsers);
+		}
+		catch (const std::exception& ex)
+		{
+			Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what());
+			return;
+		}
+
+		if (!mutedUsersData.contains("SteamID"))
+		{
+			Logger::PrintError(Game::CON_CHANNEL_ERROR, "muted-users.json contains invalid data\n");
+			return;
+		}
+
+		const auto& list = mutedUsersData["SteamID"];
+		if (!list.is_array())
+		{
+			return;
+		}
+
+		MutedList.access([&](muteList& clients)
+		{
+			const nlohmann::json::array_t arr = list;
+			for (auto& entry : arr)
+			{
+				if (entry.is_number_unsigned())
+				{
+					clients.insert(entry.get<std::uint64_t>());
+				}
+			}
 		});
 	}
 
@@ -522,6 +610,8 @@ namespace Components
 		sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients");
 		Events::OnSVInit(AddChatCommands);
 
+		LoadMutedList();
+
 		// Intercept chat sending
 		Utils::Hook(0x4D000B, PreSayStub, HOOK_CALL).install()->quick();
 		Utils::Hook(0x4D00D4, PostSayStub, HOOK_CALL).install()->quick();
diff --git a/src/Components/Modules/Chat.hpp b/src/Components/Modules/Chat.hpp
index f96e29e4..34cbffea 100644
--- a/src/Components/Modules/Chat.hpp
+++ b/src/Components/Modules/Chat.hpp
@@ -8,6 +8,9 @@ namespace Components
 	public:
 		Chat();
 
+		static bool IsMuted(const Game::gentity_s* ent);
+		static bool IsMuted(const Game::client_t* cl);
+
 	private:
 		static Dvar::Var cg_chatWidth;
 		static Dvar::Var sv_disableChat;
@@ -17,10 +20,13 @@ namespace Components
 
 		using muteList = std::unordered_set<std::uint64_t>;
 		static Utils::Concurrency::Container<muteList> MutedList;
+		static const char* MutedListFile;
 
 		static bool CanAddCallback; // ClientCommand & GSC thread are the same
 		static std::vector<Scripting::Function> SayCallbacks;
 
+		static std::unique_lock<Utils::NamedMutex> Lock();
+
 		static const char* EvaluateSay(char* text, Game::gentity_t* player, int mode);
 
 		static void PreSayStub();
@@ -30,10 +36,12 @@ namespace Components
 		static void CG_AddToTeamChat(const char* text);
 		static void CG_AddToTeamChat_Stub();
 
-		static bool IsMuted(const Game::gentity_s* ent);
 		static void MuteClient(const Game::client_t* client);
 		static void UnmuteClient(const Game::client_t* client);
 		static void UnmuteInternal(std::uint64_t id, bool everyone = false);
+		static void SaveMutedList(const muteList& list);
+		static void LoadMutedList();
+
 		static void AddChatCommands();
 
 		static int GetCallbackReturn();
diff --git a/src/Components/Modules/Events.cpp b/src/Components/Modules/Events.cpp
index e03522ec..3c9d8038 100644
--- a/src/Components/Modules/Events.cpp
+++ b/src/Components/Modules/Events.cpp
@@ -3,6 +3,7 @@
 namespace Components
 {
 	Utils::Signal<Events::ClientCallback> Events::ClientDisconnectSignal;
+	Utils::Signal<Events::ClientConnectCallback> Events::ClientConnectSignal;
 	Utils::Signal<Events::Callback> Events::SteamDisconnectSignal;
 	Utils::Signal<Events::Callback> Events::ShutdownSystemSignal;
 	Utils::Signal<Events::Callback> Events::ClientInitSignal;
@@ -13,6 +14,11 @@ namespace Components
 		ClientDisconnectSignal.connect(callback);
 	}
 
+	void Events::OnClientConnect(const Utils::Slot<ClientConnectCallback>& callback)
+	{
+		ClientConnectSignal.connect(callback);
+	}
+
 	void Events::OnSteamDisconnect(const Utils::Slot<Callback>& callback)
 	{
 		SteamDisconnectSignal.connect(callback);
@@ -44,6 +50,13 @@ namespace Components
 		Utils::Hook::Call<void(int)>(0x4AA430)(clientNum); // ClientDisconnect
 	}
 
+	void Events::SV_UserinfoChanged_Hk(Game::client_t* cl)
+	{
+		ClientConnectSignal(cl);
+
+		Utils::Hook::Call<void(Game::client_t*)>(0x401950)(cl); // SV_UserinfoChanged
+	}
+
 	void Events::SteamDisconnect_Hk()
 	{
 		SteamDisconnectSignal();
@@ -78,6 +91,8 @@ namespace Components
 	{
 		Utils::Hook(0x625235, ClientDisconnect_Hk, HOOK_CALL).install()->quick(); // SV_FreeClient
 
+		Utils::Hook(0x4612BD, SV_UserinfoChanged_Hk, HOOK_CALL).install()->quick(); // SV_DirectConnect
+
 		Utils::Hook(0x403582, SteamDisconnect_Hk, HOOK_CALL).install()->quick(); // CL_Disconnect
 
 		Utils::Hook(0x47548B, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_LoadGame
diff --git a/src/Components/Modules/Events.hpp b/src/Components/Modules/Events.hpp
index a47f6794..2b211441 100644
--- a/src/Components/Modules/Events.hpp
+++ b/src/Components/Modules/Events.hpp
@@ -6,6 +6,7 @@ namespace Components
 	{
 	public:
 		typedef void(ClientCallback)(int clientNum);
+		typedef void(ClientConnectCallback)(Game::client_t* cl);
 		typedef void(Callback)();
 
 		Events();
@@ -13,6 +14,9 @@ namespace Components
 		// Server side
 		static void OnClientDisconnect(const Utils::Slot<ClientCallback>& callback);
 
+		// Server side
+		static void OnClientConnect(const Utils::Slot<ClientConnectCallback>& callback);
+
 		// Client side
 		static void OnSteamDisconnect(const Utils::Slot<Callback>& callback);
 
@@ -25,12 +29,14 @@ namespace Components
 
 	private:
 		static Utils::Signal<ClientCallback> ClientDisconnectSignal;
+		static Utils::Signal<ClientConnectCallback> ClientConnectSignal;
 		static Utils::Signal<Callback> SteamDisconnectSignal;
 		static Utils::Signal<Callback> ShutdownSystemSignal;
 		static Utils::Signal<Callback> ClientInitSignal;
 		static Utils::Signal<Callback> ServerInitSignal;
 
 		static void ClientDisconnect_Hk(int clientNum);
+		static void SV_UserinfoChanged_Hk(Game::client_t* cl);
 		static void SteamDisconnect_Hk();
 		static void Scr_ShutdownSystem_Hk(unsigned char sys);
 		static void CL_InitOnceForAllClients_HK();
diff --git a/src/Components/Modules/Voice.cpp b/src/Components/Modules/Voice.cpp
index 10e40a21..83e22a31 100644
--- a/src/Components/Modules/Voice.cpp
+++ b/src/Components/Modules/Voice.cpp
@@ -1,4 +1,5 @@
 #include <STDInclude.hpp>
+#include "Chat.hpp"
 #include "Voice.hpp"
 
 namespace Components
@@ -381,6 +382,13 @@ namespace Components
 
 		Events::OnSteamDisconnect(CL_ClearMutedList);
 		Events::OnClientDisconnect(SV_UnmuteClient);
+		Events::OnClientConnect([](Game::client_t* cl) -> void
+		{
+			if (Chat::IsMuted(cl))
+			{
+				SV_MuteClient(cl - Game::svs_clients);
+			}
+		});
 
 		// Write voice packets to the server instead of other clients
 		Utils::Hook(0x487935, CL_WriteVoicePacket_Hk, HOOK_CALL).install()->quick();
diff --git a/src/Utils/IO.hpp b/src/Utils/IO.hpp
index c4e73825..a0d4e39c 100644
--- a/src/Utils/IO.hpp
+++ b/src/Utils/IO.hpp
@@ -2,14 +2,14 @@
 
 namespace Utils::IO
 {
-	bool FileExists(const std::string& file);
+	[[nodiscard]] bool FileExists(const std::string& file);
 	bool WriteFile(const std::string& file, const std::string& data, bool append = false);
 	bool ReadFile(const std::string& file, std::string* data);
-	std::string ReadFile(const std::string& file);
+	[[nodiscard]] std::string ReadFile(const std::string& file);
 	bool RemoveFile(const std::string& file);
-	std::size_t FileSize(const std::string& file);
+	[[nodiscard]] std::size_t FileSize(const std::string& file);
 	bool CreateDir(const std::string& dir);
-	bool DirectoryExists(const std::filesystem::path& directory);
-	bool DirectoryIsEmpty(const std::filesystem::path& directory);
-	std::vector<std::string> ListFiles(const std::filesystem::path& directory, bool recursive = false);
+	[[nodiscard]] bool DirectoryExists(const std::filesystem::path& directory);
+	[[nodiscard]] bool DirectoryIsEmpty(const std::filesystem::path& directory);
+	[[nodiscard]] std::vector<std::string> ListFiles(const std::filesystem::path& directory, bool recursive = false);
 }