From af2ba5b287ad572e4e5c56d28bea0db1cae8c3d7 Mon Sep 17 00:00:00 2001
From: David Markowitz <EmosewaMC@gmail.com>
Date: Mon, 20 Jan 2025 02:53:21 -0800
Subject: [PATCH] Add FlagComponent and msg handlers

---
 dCommon/dEnums/MessageType/Game.h             |   9 +-
 dCommon/dEnums/eReplicaComponentType.h        |   2 +-
 dGame/Character.cpp                           | 159 ++------------
 dGame/Character.h                             |  45 +---
 dGame/Entity.cpp                              |   3 +
 dGame/EntityManager.cpp                       |   7 +
 dGame/EntityManager.h                         |  10 +
 dGame/UserManager.cpp                         |   7 +-
 dGame/dComponents/CMakeLists.txt              |   1 +
 dGame/dComponents/FlagComponent.cpp           | 199 ++++++++++++++++++
 dGame/dComponents/FlagComponent.h             |  59 ++++++
 dGame/dComponents/PetComponent.cpp            |  12 +-
 dGame/dComponents/QuickBuildComponent.cpp     |  14 +-
 dGame/dGameMessages/GameMessageHandler.cpp    |  24 +--
 dGame/dGameMessages/GameMessages.cpp          |  45 ++--
 dGame/dGameMessages/GameMessages.h            |  25 ++-
 dGame/dMission/Mission.cpp                    |  11 +-
 dGame/dUtilities/Preconditions.cpp            |  17 +-
 .../SlashCommands/DEVGMCommands.cpp           |  37 +++-
 .../02_server/Map/AG/AgCagedBricksServer.cpp  |  15 +-
 .../02_server/Map/AG/RemoveRentalGear.cpp     |  12 +-
 dScripts/02_server/Map/AM/AmBlueX.cpp         |  10 +-
 dScripts/02_server/Map/General/Binoculars.cpp |  21 +-
 .../Ninjago/NjRailActivatorsServer.cpp        |  10 +-
 .../02_server/Map/General/PetDigServer.cpp    |  32 ++-
 .../Map/General/StoryBoxInteractServer.cpp    |  16 +-
 .../Map/General/TokenConsoleServer.cpp        |  18 +-
 .../02_server/Map/NS/NsTokenConsoleServer.cpp |  15 +-
 .../Map/NT/NTPipeVisibilityServer.cpp         |  12 +-
 .../Map/NT/NtImagimeterVisibility.cpp         |   8 +-
 .../02_server/Map/NT/NtParadoxPanelServer.cpp |   9 +-
 .../Map/Property/AG_Small/ZoneAgProperty.cpp  |  85 +++++---
 dScripts/02_server/Map/VE/VeEpsilonServer.cpp |  12 +-
 .../02_server/Map/VE/VeMissionConsole.cpp     |  11 +-
 .../02_server/Map/njhub/CavePrisonCage.cpp    |  10 +-
 .../Map/njhub/NjDragonEmblemChestServer.cpp   |  11 +-
 .../Map/njhub/NjGarmadonCelebration.cpp       |  18 +-
 .../Map/njhub/NjNPCMissionSpinjitzuServer.cpp |  15 +-
 dScripts/02_server/Map/njhub/NjWuNPC.cpp      |  18 +-
 dScripts/BasePropertyServer.cpp               |  23 +-
 dScripts/Darkitect.cpp                        |  11 +-
 .../TrialFactionArmorServer.cpp               |  16 +-
 dScripts/NtFactionSpyServer.cpp               |  18 +-
 .../ai/ACT/FootRace/BaseFootRaceManager.cpp   |  34 +--
 dScripts/ai/FV/FvFreeGfNinjas.cpp             |  28 ++-
 dScripts/ai/FV/FvPandaServer.cpp              |  11 +-
 dScripts/ai/FV/FvPandaSpawnerServer.cpp       |   8 +-
 dScripts/ai/GF/GfJailkeepMission.cpp          |   9 +-
 dScripts/ai/GF/PirateRep.cpp                  |  11 +-
 dScripts/ai/NS/NsGetFactionMissionServer.cpp  |  14 +-
 dScripts/ai/PROPERTY/AG/AgPropGuard.cpp       |  22 +-
 dScripts/ai/PROPERTY/AgPropguards.cpp         |  12 +-
 dWorldServer/WorldServer.cpp                  |   6 +-
 53 files changed, 781 insertions(+), 486 deletions(-)
 create mode 100644 dGame/dComponents/FlagComponent.cpp
 create mode 100644 dGame/dComponents/FlagComponent.h

diff --git a/dCommon/dEnums/MessageType/Game.h b/dCommon/dEnums/MessageType/Game.h
index b4c44915..58bcd0c1 100644
--- a/dCommon/dEnums/MessageType/Game.h
+++ b/dCommon/dEnums/MessageType/Game.h
@@ -1603,12 +1603,17 @@ namespace MessageType {
 		UPDATE_FORGED_ITEM = 1768,
 		CAN_ITEMS_BE_REFORGED = 1769,
 		NOTIFY_CLIENT_RAIL_START_FAILED = 1771,
-		GET_IS_ON_RAIL = 1772
+		GET_IS_ON_RAIL = 1772,
+
+		// DLU CUSTOM GAME MESSAGES, DO NOT NETWORK OR SEND TO CLIENTS (it wont do anything bad but still dont do it >:( )
+		CLEAR_SESSION_FLAGS = 2000,
+		SET_RETROACTIVE_FLAGS = 2001,
+		GAME_MESSAGES_END,
 	};
 }
 
 template <>
 struct magic_enum::customize::enum_range<MessageType::Game> {
 	static constexpr int min = 0;
-	static constexpr int max = 1772;
+	static constexpr int max = static_cast<int>(MessageType::Game::GAME_MESSAGES_END) - 1;
 };
diff --git a/dCommon/dEnums/eReplicaComponentType.h b/dCommon/dEnums/eReplicaComponentType.h
index 2b991dfb..81c34bae 100644
--- a/dCommon/dEnums/eReplicaComponentType.h
+++ b/dCommon/dEnums/eReplicaComponentType.h
@@ -62,7 +62,7 @@ enum class eReplicaComponentType : uint32_t {
 	SOUND_AMBIENT_2D,
 	SOUND_AMBIENT_3D,
 	PRECONDITION,
-	PLAYER_FLAG,
+	FLAG,
 	CUSTOM_BUILD_ASSEMBLY,
 	BASE_COMBAT_AI,
 	MODULE_ASSEMBLY,
diff --git a/dGame/Character.cpp b/dGame/Character.cpp
index a1538e4d..77aa3469 100644
--- a/dGame/Character.cpp
+++ b/dGame/Character.cpp
@@ -22,6 +22,7 @@
 #include "eGameMasterLevel.h"
 #include "ePlayerFlag.h"
 #include "CDPlayerFlagsTable.h"
+#include "FlagComponent.h"
 
 Character::Character(uint32_t id, User* parentUser) {
 	//First load the name, etc:
@@ -37,7 +38,7 @@ Character::~Character() {
 	m_ParentUser = nullptr;
 }
 
-void Character::UpdateInfoFromDatabase() {
+void Character::UpdateInfoFromDatabase(bool clearSessionFlags) {
 	auto charInfo = Database::Get()->GetCharacterInfo(m_ID);
 
 	if (charInfo) {
@@ -65,10 +66,17 @@ void Character::UpdateInfoFromDatabase() {
 
 	m_OurEntity = nullptr;
 	m_BuildMode = false;
+
+	// This is not done through a game message because our current implementation does this at a point
+	// in time where an Entity does not exist, so there is no FlagComponent to handle the msg.
+	if (clearSessionFlags) {
+		FlagComponent::ClearSessionFlags(m_Doc);
+		WriteToDatabase();
+	}
 }
 
-void Character::UpdateFromDatabase() {
-	UpdateInfoFromDatabase();
+void Character::UpdateFromDatabase(bool clearSessionFlags) {
+	UpdateInfoFromDatabase(clearSessionFlags);
 }
 
 void Character::DoQuickXMLDataParse() {
@@ -197,25 +205,6 @@ void Character::DoQuickXMLDataParse() {
 		character->QueryAttribute("lzrz", &m_OriginalRotation.z);
 		character->QueryAttribute("lzrw", &m_OriginalRotation.w);
 	}
-
-	auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
-	if (flags) {
-		auto* currentChild = flags->FirstChildElement();
-		while (currentChild) {
-			const auto* temp = currentChild->Attribute("v");
-			const auto* id = currentChild->Attribute("id");
-			if (temp && id) {
-				uint32_t index = 0;
-				uint64_t value = 0;
-
-				index = std::stoul(id);
-				value = std::stoull(temp);
-
-				m_PlayerFlags.insert(std::make_pair(index, value));
-			}
-			currentChild = currentChild->NextSiblingElement();
-		}
-	}
 }
 
 void Character::UnlockEmote(int emoteID) {
@@ -276,25 +265,6 @@ void Character::SaveXMLToDatabase() {
 		character->LinkEndChild(emotes);
 	}
 
-	//Export our flags:
-	auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
-	if (!flags) {
-		flags = m_Doc.NewElement("flag"); //Create a flags tag if we don't have one
-		m_Doc.FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time
-	}
-
-	flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
-	for (const auto& [index, flagBucket] : m_PlayerFlags) {
-		auto* f = flags->InsertNewChildElement("f");
-		f->SetAttribute("id", index);
-		f->SetAttribute("v", flagBucket);
-	}
-
-	for (const auto& sessionFlag : m_SessionFlags) {
-		auto* s = flags->InsertNewChildElement("s");
-		s->SetAttribute("si", sessionFlag);
-	}
-
 	SaveXmlRespawnCheckpoints();
 
 	m_OurEntity->UpdateXMLDoc(m_Doc);
@@ -307,23 +277,6 @@ void Character::SaveXMLToDatabase() {
 	LOG("%i:%s Saved character to Database in: %fs", this->GetID(), this->GetName().c_str(), elapsed.count());
 }
 
-void Character::SetIsNewLogin() {
-	// If we dont have a flag element, then we cannot have a s element as a child of flag.
-	auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
-	if (!flags) return;
-
-	auto* currentChild = flags->FirstChildElement();
-	while (currentChild) {
-		auto* nextChild = currentChild->NextSiblingElement();
-		if (currentChild->Attribute("si")) {
-			LOG("Removed session flag (%s) from character %i:%s, saving character to database", currentChild->Attribute("si"), GetID(), GetName().c_str());
-			flags->DeleteChild(currentChild);
-			WriteToDatabase();
-		}
-		currentChild = nextChild;
-	}
-}
-
 void Character::WriteToDatabase() {
 	//Dump our xml into m_XMLData:
 	tinyxml2::XMLPrinter printer(0, true, 0);
@@ -333,90 +286,6 @@ void Character::WriteToDatabase() {
 	Database::Get()->UpdateCharacterXml(m_ID, printer.CStr());
 }
 
-void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
-	// If the flag is already set, we don't have to recalculate it
-	if (GetPlayerFlag(flagId) == value) return;
-
-	if (value) {
-		// Update the mission component:
-		auto* player = Game::entityManager->GetEntity(m_ObjectID);
-
-		if (player != nullptr) {
-			auto* missionComponent = player->GetComponent<MissionComponent>();
-
-			if (missionComponent != nullptr) {
-				missionComponent->Progress(eMissionTaskType::PLAYER_FLAG, flagId);
-			}
-		}
-	}
-
-	const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);	
-
-	if (flagEntry && flagEntry->sessionOnly) {
-		if (value) m_SessionFlags.insert(flagId);
-		else m_SessionFlags.erase(flagId);
-	} else {
-		// Calculate the index first
-		auto flagIndex = uint32_t(std::floor(flagId / 64));
-
-		const auto shiftedValue = 1ULL << flagId % 64;
-
-		auto it = m_PlayerFlags.find(flagIndex);
-
-		// Check if flag index exists
-		if (it != m_PlayerFlags.end()) {
-			// Update the value
-			if (value) {
-				it->second |= shiftedValue;
-			} else {
-				it->second &= ~shiftedValue;
-			}
-		} else {
-			if (value) {
-				// Otherwise, insert the value
-				uint64_t flagValue = 0;
-
-				flagValue |= shiftedValue;
-
-				m_PlayerFlags.insert(std::make_pair(flagIndex, flagValue));
-			}
-		}
-	}
-	// Notify the client that a flag has changed server-side
-	GameMessages::SendNotifyClientFlagChange(m_ObjectID, flagId, value, m_ParentUser->GetSystemAddress());
-}
-
-bool Character::GetPlayerFlag(const uint32_t flagId) const {
-	using enum ePlayerFlag;
-
-	bool toReturn = false; //by def, return false.
-
-	const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);
-	if (flagEntry && flagEntry->sessionOnly) {
-		toReturn = m_SessionFlags.contains(flagId);
-	} else {
-		// Calculate the index first
-		const auto flagIndex = uint32_t(std::floor(flagId / 64));
-
-		const auto shiftedValue = 1ULL << flagId % 64;
-
-		auto it = m_PlayerFlags.find(flagIndex);
-		if (it != m_PlayerFlags.end()) {
-			// Don't set the data if we don't have to
-			toReturn = (it->second & shiftedValue) != 0;
-		}
-	}
-
-	return toReturn;
-}
-
-void Character::SetRetroactiveFlags() {
-	// Retroactive check for if player has joined a faction to set their 'joined a faction' flag to true.
-	if (GetPlayerFlag(ePlayerFlag::VENTURE_FACTION) || GetPlayerFlag(ePlayerFlag::ASSEMBLY_FACTION) || GetPlayerFlag(ePlayerFlag::PARADOX_FACTION) || GetPlayerFlag(ePlayerFlag::SENTINEL_FACTION)) {
-		SetPlayerFlag(ePlayerFlag::JOINED_A_FACTION, true);
-	}
-}
-
 void Character::SaveXmlRespawnCheckpoints() {
 	//Export our respawn points:
 	auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
@@ -473,7 +342,11 @@ void Character::OnZoneLoad() {
 	if (missionComponent != nullptr) {
 		// Fix the monument race flag
 		if (missionComponent->GetMissionState(319) >= eMissionState::READY_TO_COMPLETE) {
-			SetPlayerFlag(ePlayerFlag::AG_FINISH_LINE_BUILT, true);
+			GameMessages::SetFlag setFlag{};
+			setFlag.target = m_ObjectID;
+			setFlag.iFlagId = ePlayerFlag::AG_FINISH_LINE_BUILT;
+			setFlag.bFlag = true;
+			SEND_ENTITY_MSG(setFlag);
 		}
 	}
 
diff --git a/dGame/Character.h b/dGame/Character.h
index 99ed9855..024199c9 100644
--- a/dGame/Character.h
+++ b/dGame/Character.h
@@ -31,7 +31,7 @@ public:
 	 */
 	void WriteToDatabase();
 	void SaveXMLToDatabase();
-	void UpdateFromDatabase();
+	void UpdateFromDatabase(bool clearSessionFlags = false);
 
 	void SaveXmlRespawnCheckpoints();
 	void LoadXmlRespawnCheckpoints();
@@ -40,15 +40,6 @@ public:
 	const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; }
 	void _setXmlDoc(tinyxml2::XMLDocument& doc) { doc.DeepCopy(&m_Doc); }
 
-	/**
-	 * Out of abundance of safety and clarity of what this saves, this is its own function.
-	 *
-	 * Clears the s element from the flag element and saves the xml to the database.  Used to prevent the news
-	 * feed from showing up on world transfers.
-	 *
-	 */
-	void SetIsNewLogin();
-
 	/**
 	 * Gets the database ID of the character
 	 * @return the database ID of the character
@@ -410,32 +401,11 @@ public:
 	 */
 	void UnlockEmote(int emoteID);
 
-	/**
-	 * Sets a flag for the character, indicating certain parts of the game that have been interacted with. Not to be
-	 * confused with the permissions
-	 * @param flagId the ID of the flag to set
-	 * @param value the value to set for the flag
-	 */
-	void SetPlayerFlag(uint32_t flagId, bool value);
-
-	/**
-	 * Gets the value for a certain character flag
-	 * @param flagId the ID of the flag to get a value for
-	 * @return the value of the flag given the ID (the default is false, obviously)
-	 */
-	bool GetPlayerFlag(uint32_t flagId) const;
-
 	/**
 	 * Notifies the character that they're now muted
 	 */
 	void SendMuteNotice() const;
 
-	/**
-	 * Sets any flags that are meant to have been set that may not have been set due to them being
-	 * missing in a previous patch.
-	 */
-	void SetRetroactiveFlags();
-
 	/**
 	 * Get the equipped items for this character, only used for character creation
 	 * @return the equipped items for this character on world load
@@ -465,7 +435,7 @@ public:
 	void _setXmlData(const std::string& xmlData) { m_XMLData = xmlData; }
 
 private:
-	void UpdateInfoFromDatabase();
+	void UpdateInfoFromDatabase(bool clearSessionFlags);
 	/**
 	 * The ID of this character. First 32 bits of the ObjectID.
 	 */
@@ -620,17 +590,6 @@ private:
 	 */
 	uint64_t m_LastLogin{};
 
-	/**
-	 * Flags only set for the duration of a session
-	 * 
-	 */
-	std::set<uint32_t> m_SessionFlags;
-
-	/**
-	 * The gameplay flags this character has (not just true values)
-	 */
-	std::unordered_map<uint32_t, uint64_t> m_PlayerFlags;
-
 	/**
 	 * The character XML belonging to this character
 	 */
diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp
index 90bf7e76..d11a2089 100644
--- a/dGame/Entity.cpp
+++ b/dGame/Entity.cpp
@@ -84,6 +84,7 @@
 #include "GhostComponent.h"
 #include "AchievementVendorComponent.h"
 #include "VanityUtilities.h"
+#include "FlagComponent.h"
 
 // Table includes
 #include "CDComponentsRegistryTable.h"
@@ -482,6 +483,8 @@ void Entity::Initialize() {
 		AddComponent<CharacterComponent>(m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
 
 		AddComponent<GhostComponent>();
+
+		AddComponent<FlagComponent>()->LoadFromXml(m_Character->GetXMLDoc());
 	}
 
 	if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) {
diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp
index c95af3d7..718d9024 100644
--- a/dGame/EntityManager.cpp
+++ b/dGame/EntityManager.cpp
@@ -604,3 +604,10 @@ void EntityManager::FireEventServerSide(Entity* origin, std::string args) {
 bool EntityManager::IsExcludedFromGhosting(LOT lot) {
 	return std::find(m_GhostingExcludedLOTs.begin(), m_GhostingExcludedLOTs.end(), lot) != m_GhostingExcludedLOTs.end();
 }
+
+bool EntityManager::SendMsg(GameMessages::GameMsg& msg) {
+	bool bRet = false;
+	auto* entity = GetEntity(msg.target);
+	if (entity) bRet = entity->HandleMsg(msg);
+	return bRet;
+}
diff --git a/dGame/EntityManager.h b/dGame/EntityManager.h
index fdbb1a55..831de0ca 100644
--- a/dGame/EntityManager.h
+++ b/dGame/EntityManager.h
@@ -8,6 +8,13 @@
 
 #include "dCommonVars.h"
 
+// Convenience macro to send a message to the entity manager
+#define SEND_ENTITY_MSG(msg) Game::entityManager->SendMsg(msg)
+
+namespace GameMessages {
+	struct GameMsg;	
+};
+
 class Entity;
 class EntityInfo;
 class Player;
@@ -72,6 +79,9 @@ public:
 	const bool GetHardcoreDropinventoryOnDeath() { return m_HardcoreDropinventoryOnDeath; };
 	const uint32_t GetHardcoreUscoreEnemiesMultiplier() { return m_HardcoreUscoreEnemiesMultiplier; };
 
+	// Sends a message to be handled by the receiving entity
+	bool SendMsg(GameMessages::GameMsg& msg);
+
 private:
 	void SerializeEntities();
 	void KillEntities();
diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp
index e0e37e7c..13e1f30f 100644
--- a/dGame/UserManager.cpp
+++ b/dGame/UserManager.cpp
@@ -197,6 +197,10 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
 			skillComponent->Reset();
 		}
 
+		GameMessages::ClearSessionFlags msg{};
+		msg.target = chars[i]->GetObjectID();
+		SEND_ENTITY_MSG(msg);
+
 		Game::entityManager->DestroyEntity(chars[i]->GetEntity());
 
 		chars[i]->SaveXMLToDatabase();
@@ -210,8 +214,7 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
 
 	for (const auto& characterId : Database::Get()->GetAccountCharacterIds(u->GetAccountID())) {
 		Character* character = new Character(characterId, u);
-		character->UpdateFromDatabase();
-		character->SetIsNewLogin();
+		character->UpdateFromDatabase(true);
 		chars.push_back(character);
 	}
 
diff --git a/dGame/dComponents/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt
index e1116895..fc0cf982 100644
--- a/dGame/dComponents/CMakeLists.txt
+++ b/dGame/dComponents/CMakeLists.txt
@@ -10,6 +10,7 @@ set(DGAME_DCOMPONENTS_SOURCES
 	"ControllablePhysicsComponent.cpp"
 	"DestroyableComponent.cpp"
 	"DonationVendorComponent.cpp"
+	"FlagComponent.cpp"
 	"GhostComponent.cpp"
 	"InventoryComponent.cpp"
 	"ItemComponent.cpp"
diff --git a/dGame/dComponents/FlagComponent.cpp b/dGame/dComponents/FlagComponent.cpp
new file mode 100644
index 00000000..1de06d4a
--- /dev/null
+++ b/dGame/dComponents/FlagComponent.cpp
@@ -0,0 +1,199 @@
+#include "FlagComponent.h"
+
+#include "CDPlayerFlagsTable.h"
+
+#include "eMissionTaskType.h"
+#include "ePlayerFlag.h"
+
+#include "MissionComponent.h"
+
+FlagComponent::FlagComponent(Entity* parent) : Component(parent) {
+	RegisterMsg(MessageType::Game::SET_FLAG, this, &FlagComponent::OnSetFlag);
+	RegisterMsg(MessageType::Game::GET_FLAG, this, &FlagComponent::OnGetFlag);
+	RegisterMsg(MessageType::Game::CLEAR_SESSION_FLAGS, this, &FlagComponent::OnClearSessionFlags);
+	RegisterMsg(MessageType::Game::SET_RETROACTIVE_FLAGS, this, &FlagComponent::OnSetRetroactiveFlags);
+}
+
+bool FlagComponent::OnSetFlag(GameMessages::GameMsg& msg) {
+	auto& setFlag = static_cast<GameMessages::SetFlag&>(msg);
+	LOG("Set %i", setFlag.iFlagId);
+	SetPlayerFlag(setFlag.iFlagId, setFlag.bFlag);
+
+	// This is always set the first time a player loads into a world from character select
+	// and is used to know when to refresh the players inventory items so they show up.
+	if (setFlag.iFlagId == ePlayerFlag::IS_NEWS_SCREEN_VISIBLE && setFlag.bFlag) {
+		m_Parent->SetVar<bool>(u"dlu_first_time_load", true);
+	}
+
+	return true;
+}
+
+bool FlagComponent::OnGetFlag(GameMessages::GameMsg& msg) {
+	auto& getFlag = static_cast<GameMessages::GetFlag&>(msg);
+	LOG("Get %i", getFlag.iFlagId);
+
+	getFlag.bFlag = GetPlayerFlag(getFlag.iFlagId);
+	return true;
+}
+
+void FlagComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
+	if (!doc.FirstChildElement("obj")) return;
+	auto& obj = *doc.FirstChildElement("obj");
+
+	if (!obj.FirstChildElement("flag")) {
+		obj.InsertNewChildElement("flag");
+	}
+
+	auto& flags = *obj.FirstChildElement("flag");
+
+	flags.DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
+
+	// Save our flags
+	for (const auto& [index, flagBucket] : m_PlayerFlags) {
+		auto& f = *flags.InsertNewChildElement("f");
+		f.SetAttribute("id", index);
+		f.SetAttribute("v", flagBucket);
+	}
+
+	// Save our session flags
+	for (const auto& sessionFlag : m_SessionFlags) {
+		auto& s = *flags.InsertNewChildElement("s");
+		LOG("Saving session flag %i", sessionFlag);
+		s.SetAttribute("si", sessionFlag);
+	}
+}
+
+void FlagComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
+	if (!doc.FirstChildElement("obj")) return;
+	auto& obj = *doc.FirstChildElement("obj");
+
+	if (!obj.FirstChildElement("flag")) return;
+	auto& flags = *obj.FirstChildElement("flag");
+
+	const auto* currentChild = flags.FirstChildElement("f");
+	while (currentChild) {
+		const auto* temp = currentChild->Attribute("v");
+		const auto* id = currentChild->Attribute("id");
+		if (temp && id) {
+			uint32_t index = 0;
+			uint64_t value = 0;
+
+			index = std::stoul(id);
+			value = std::stoull(temp);
+
+			m_PlayerFlags.insert(std::make_pair(index, value));
+		}
+		currentChild = currentChild->NextSiblingElement("f");
+	}
+
+	// Now load our session flags
+	currentChild = flags.FirstChildElement("s");
+	while (currentChild) {
+		const auto* temp = currentChild->Attribute("si");
+		if (temp) {
+			uint32_t sessionIndex = 0;
+			sessionIndex = std::stoul(temp);
+			m_SessionFlags.insert(sessionIndex);
+		}
+		currentChild = currentChild->NextSiblingElement("s");
+	}
+}
+
+void FlagComponent::SetPlayerFlag(const uint32_t flagId, const bool value) {
+	// If the flag is already set, we don't have to recalculate it
+	if (GetPlayerFlag(flagId) == value) return;
+
+	if (value) {
+		// Update the mission component:
+		auto* missionComponent = m_Parent->GetComponent<MissionComponent>();
+
+		if (missionComponent != nullptr) {
+			missionComponent->Progress(eMissionTaskType::PLAYER_FLAG, flagId);
+		}
+	}
+
+	const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);
+
+	if (flagEntry && flagEntry->sessionOnly) {
+		if (value) m_SessionFlags.insert(flagId);
+		else m_SessionFlags.erase(flagId);
+	} else {
+		// Calculate the index first
+		auto flagIndex = uint32_t(std::floor(flagId / 64));
+
+		const auto shiftedValue = 1ULL << flagId % 64;
+
+		auto it = m_PlayerFlags.find(flagIndex);
+
+		// Check if flag index exists
+		if (it != m_PlayerFlags.end()) {
+			// Update the value
+			if (value) {
+				it->second |= shiftedValue;
+			} else {
+				it->second &= ~shiftedValue;
+			}
+		} else {
+			if (value) {
+				// Otherwise, insert the value
+				uint64_t flagValue = 0;
+
+				flagValue |= shiftedValue;
+
+				m_PlayerFlags.insert(std::make_pair(flagIndex, flagValue));
+			}
+		}
+	}
+	// Notify the client that a flag has changed server-side
+	GameMessages::SendNotifyClientFlagChange(m_Parent->GetObjectID(), flagId, value, m_Parent->GetSystemAddress());
+}
+
+bool FlagComponent::GetPlayerFlag(const uint32_t flagId) const {
+	bool toReturn = false; //by def, return false.
+
+	const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);
+	if (flagEntry && flagEntry->sessionOnly) {
+		toReturn = m_SessionFlags.contains(flagId);
+	} else {
+		// Calculate the index first
+		const auto flagIndex = uint32_t(std::floor(flagId / 64));
+
+		const auto shiftedValue = 1ULL << flagId % 64;
+
+		auto it = m_PlayerFlags.find(flagIndex);
+		if (it != m_PlayerFlags.end()) {
+			// Don't set the data if we don't have to
+			toReturn = (it->second & shiftedValue) != 0;
+		}
+	}
+
+	return toReturn;
+}
+
+bool FlagComponent::OnSetRetroactiveFlags(GameMessages::GameMsg& msg) {
+	// Retroactive check for if player has joined a faction to set their 'joined a faction' flag to true.
+	if (GetPlayerFlag(ePlayerFlag::VENTURE_FACTION) ||
+		GetPlayerFlag(ePlayerFlag::ASSEMBLY_FACTION) ||
+		GetPlayerFlag(ePlayerFlag::PARADOX_FACTION) ||
+		GetPlayerFlag(ePlayerFlag::SENTINEL_FACTION)) {
+		SetPlayerFlag(ePlayerFlag::JOINED_A_FACTION, true);
+	}
+	return true;
+}
+
+void FlagComponent::ClearSessionFlags(tinyxml2::XMLDocument& doc) {
+	if (!doc.FirstChildElement("obj")) return;
+	auto& obj = *doc.FirstChildElement("obj");
+
+	if (!obj.FirstChildElement("flag")) return;
+	auto& flags = *obj.FirstChildElement("flag");
+
+	auto* currentChild = flags.FirstChildElement();
+	while (currentChild) {
+		auto* nextChild = currentChild->NextSiblingElement();
+		if (currentChild->Attribute("si")) {
+			flags.DeleteChild(currentChild);
+		}
+		currentChild = nextChild;
+	}
+}
diff --git a/dGame/dComponents/FlagComponent.h b/dGame/dComponents/FlagComponent.h
new file mode 100644
index 00000000..b2fffc3b
--- /dev/null
+++ b/dGame/dComponents/FlagComponent.h
@@ -0,0 +1,59 @@
+#ifndef FLAGCOMPONENT_H
+#define FLAGCOMPONENT_H
+
+#include <cstdint>
+#include <set>
+#include <unordered_map>
+
+#include "Component.h"
+#include "eReplicaComponentType.h"
+
+class FlagComponent final : public Component {
+public:
+	static const inline eReplicaComponentType ComponentType = eReplicaComponentType::FLAG;
+	FlagComponent(Entity* parent);
+
+	void UpdateXml(tinyxml2::XMLDocument& doc) override;
+	void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
+
+	// Used to clear the save data from a static context where you only have a doc (switching characters)
+	static void ClearSessionFlags(tinyxml2::XMLDocument& doc);
+private:
+
+	/**
+	 * Sets a flag for the character, indicating certain parts of the game that have been interacted with. Not to be
+	 * confused with the permissions
+	 * @param flagId the ID of the flag to set
+	 * @param value the value to set for the flag
+	 */
+	bool OnSetFlag(GameMessages::GameMsg& msg);
+	void SetPlayerFlag(const uint32_t flagId, const bool value);
+
+	/**
+	 * Gets the value for a certain character flag
+	 * @param flagId the ID of the flag to get a value for
+	 * @return the value of the flag given the ID (the default is false, obviously)
+	 */
+	bool OnGetFlag(GameMessages::GameMsg& msg);
+	bool GetPlayerFlag(const uint32_t flagId) const;
+
+	bool OnClearSessionFlags(GameMessages::GameMsg& msg) { m_SessionFlags.clear(); return true; }
+
+	/**
+	 * Sets any flags that are meant to have been set that may not have been set due to them being
+	 * missing in a previous patch.
+	 */
+	bool OnSetRetroactiveFlags(GameMessages::GameMsg& msg);
+	/**
+	 * Flags only set for the duration of a session
+	 *
+	 */
+	std::set<uint32_t> m_SessionFlags;
+
+	/**
+	 * The gameplay flags this character has (not just true values)
+	 */
+	std::unordered_map<uint32_t, uint64_t> m_PlayerFlags;
+};
+
+#endif  //!FLAGCOMPONENT_H
diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp
index 7121dd44..304a9a54 100644
--- a/dGame/dComponents/PetComponent.cpp
+++ b/dGame/dComponents/PetComponent.cpp
@@ -557,7 +557,11 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
 
 	// Triggers the catch a pet missions
 	if (petFlags.find(m_Parent->GetLOT()) != petFlags.end()) {
-		tamer->GetCharacter()->SetPlayerFlag(petFlags.at(m_Parent->GetLOT()), true);
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = tamer->GetObjectID();
+		setFlag.iFlagId = petFlags.at(m_Parent->GetLOT());
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 	}
 
 	auto* missionComponent = tamer->GetComponent<MissionComponent>();
@@ -847,7 +851,11 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
 
 	Game::entityManager->SerializeEntity(m_Parent);
 
-	owner->GetCharacter()->SetPlayerFlag(ePlayerFlag::FIRST_MANUAL_PET_HIBERNATE, true);
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = owner->GetObjectID();
+	setFlag.iFlagId = ePlayerFlag::FIRST_MANUAL_PET_HIBERNATE;
+	setFlag.bFlag = true;
+	SEND_ENTITY_MSG(setFlag);
 
 	if (registerPet) {
 		GameMessages::SendAddPetToPlayer(m_Owner, 0, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, m_Parent->GetLOT(), owner->GetSystemAddress());
diff --git a/dGame/dComponents/QuickBuildComponent.cpp b/dGame/dComponents/QuickBuildComponent.cpp
index 7a5cd8a0..0d504a84 100644
--- a/dGame/dComponents/QuickBuildComponent.cpp
+++ b/dGame/dComponents/QuickBuildComponent.cpp
@@ -481,14 +481,12 @@ void QuickBuildComponent::CompleteQuickBuild(Entity* const user) {
 	}
 
 	// Set flag
-	auto* character = user->GetCharacter();
-
-	if (character != nullptr) {
-		const auto flagNumber = m_Parent->GetVar<int32_t>(u"quickbuild_single_build_player_flag");
-
-		if (flagNumber != 0) {
-			character->SetPlayerFlag(flagNumber, true);
-		}
+	if (m_Parent->HasVar(u"quickbuild_single_build_player_flag")) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = user->GetObjectID();
+		setFlag.iFlagId = m_Parent->GetVar<int32_t>(u"quickbuild_single_build_player_flag");
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 	}
 	RenderComponent::PlayAnimation(user, u"rebuild-celebrate", 1.09f);
 }
diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp
index ef890401..d3de0af5 100644
--- a/dGame/dGameMessages/GameMessageHandler.cpp
+++ b/dGame/dGameMessages/GameMessageHandler.cpp
@@ -47,6 +47,7 @@ namespace {
 	std::map<MessageType::Game, MessageCreator> g_MessageHandlers = {
 		{ REQUEST_SERVER_OBJECT_INFO, []() { return std::make_unique<RequestServerObjectInfo>(); } },
 		{ SHOOTING_GALLERY_FIRE, []() { return std::make_unique<ShootingGalleryFire>(); } },
+		{ SET_FLAG, []() { return std::make_unique<SetFlag>(); } },
 	};
 };
 
@@ -123,11 +124,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
 		break;
 	}
 
-	case MessageType::Game::SET_FLAG: {
-		GameMessages::HandleSetFlag(inStream, entity);
-		break;
-	}
-
 	case MessageType::Game::HAS_BEEN_COLLECTED: {
 		GameMessages::HandleHasBeenCollected(inStream, entity);
 		break;
@@ -215,13 +211,17 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
 		// After we've done our thing, tell the client they're ready
 		GameMessages::SendPlayerReady(Game::zoneManager->GetZoneControlObject(), sysAddr);
 
-		if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1"
-			|| !entity->GetCharacter()
-			|| !entity->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return;
-		entity->AddCallbackTimer(0.5f, [entity, sysAddr]() {
-			if (!entity) return;
-			GameMessages::SendEndCinematic(entity->GetObjectID(), u"", sysAddr);
-			});
+		GameMessages::GetFlag getFlag{};
+		getFlag.target = entity->GetObjectID();
+		getFlag.iFlagId = ePlayerFlag::DLU_SKIP_CINEMATICS;
+		SEND_ENTITY_MSG(getFlag);
+
+		if (Game::config->GetValue("allow_players_to_skip_cinematics") == "1" && getFlag.bFlag) {
+			entity->AddCallbackTimer(0.5f, [entity, sysAddr]() {
+				if (!entity) return;
+				GameMessages::SendEndCinematic(entity->GetObjectID(), u"", sysAddr);
+				});
+		}
 		break;
 	}
 
diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp
index 6fd7576f..ad214b36 100644
--- a/dGame/dGameMessages/GameMessages.cpp
+++ b/dGame/dGameMessages/GameMessages.cpp
@@ -5076,23 +5076,6 @@ void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream& inStream, E
 	item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::QUICKBUILD);
 }
 
-void GameMessages::HandleSetFlag(RakNet::BitStream& inStream, Entity* entity) {
-	bool bFlag{};
-	int32_t iFlagID{};
-
-	inStream.Read(bFlag);
-	inStream.Read(iFlagID);
-
-	auto character = entity->GetCharacter();
-	if (character) character->SetPlayerFlag(iFlagID, bFlag);
-
-	// This is always set the first time a player loads into a world from character select
-	// and is used to know when to refresh the players inventory items so they show up.
-	if (iFlagID == ePlayerFlag::IS_NEWS_SCREEN_VISIBLE && bFlag) {
-		entity->SetVar<bool>(u"dlu_first_time_load", true);
-	}
-}
-
 void GameMessages::HandleRespondToMission(RakNet::BitStream& inStream, Entity* entity) {
 	int missionID{};
 	LWOOBJID playerID{};
@@ -5157,13 +5140,17 @@ void GameMessages::HandleMissionDialogOK(RakNet::BitStream& inStream, Entity* en
 		missionComponent->CompleteMission(missionID);
 	}
 
-	if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1"
-		|| !player->GetCharacter()
-		|| !player->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return;
-	player->AddCallbackTimer(0.5f, [player]() {
-		if (!player) return;
-		GameMessages::SendEndCinematic(player->GetObjectID(), u"", player->GetSystemAddress());
-		});
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = entity->GetObjectID();
+	getFlag.iFlagId = ePlayerFlag::DLU_SKIP_CINEMATICS;
+	SEND_ENTITY_MSG(getFlag);
+
+	if (Game::config->GetValue("allow_players_to_skip_cinematics") == "1" && getFlag.bFlag) {
+		player->AddCallbackTimer(0.5f, [player]() {
+			if (!player) return;
+			GameMessages::SendEndCinematic(player->GetObjectID(), u"", player->GetSystemAddress());
+			});
+	}
 }
 
 void GameMessages::HandleRequestLinkedMission(RakNet::BitStream& inStream, Entity* entity) {
@@ -6443,4 +6430,14 @@ namespace GameMessages {
 		auto* handlingEntity = Game::entityManager->GetEntity(targetForReport);
 		if (handlingEntity) handlingEntity->HandleMsg(*this);
 	}
+
+	bool SetFlag::Deserialize(RakNet::BitStream& bitStream) {
+		if (!bitStream.Read(bFlag)) return false;
+		if (!bitStream.Read(iFlagId)) return false;
+		return true;
+	}
+
+	void SetFlag::Handle(Entity& entity, const SystemAddress& sysAddr) {
+		entity.HandleMsg(*this);
+	}
 }
diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h
index c3889674..b870e9e3 100644
--- a/dGame/dGameMessages/GameMessages.h
+++ b/dGame/dGameMessages/GameMessages.h
@@ -634,7 +634,6 @@ namespace GameMessages {
 	void HandleRequestUse(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
 	void HandlePlayEmote(RakNet::BitStream& inStream, Entity* entity);
 	void HandleModularBuildConvertModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
-	void HandleSetFlag(RakNet::BitStream& inStream, Entity* entity);
 	void HandleRespondToMission(RakNet::BitStream& inStream, Entity* entity);
 	void HandleMissionDialogOK(RakNet::BitStream& inStream, Entity* entity);
 	void HandleRequestLinkedMission(RakNet::BitStream& inStream, Entity* entity);
@@ -782,6 +781,30 @@ namespace GameMessages {
 		bool Deserialize(RakNet::BitStream& bitStream) override;
 		void Handle(Entity& entity, const SystemAddress& sysAddr) override;
 	};
+
+	struct SetFlag : public GameMsg {
+		SetFlag() : GameMsg(MessageType::Game::SET_FLAG) {}
+		bool Deserialize(RakNet::BitStream& bitStream) override;
+		void Handle(Entity& entity, const SystemAddress& sysAddr) override;
+
+		uint32_t iFlagId{};
+		bool bFlag{};
+	};
+
+	struct GetFlag : public GameMsg {
+		GetFlag() : GameMsg(MessageType::Game::GET_FLAG) {}
+
+		uint32_t iFlagId{};
+		bool bFlag{};
+	};
+
+	struct ClearSessionFlags : public GameMsg {
+		ClearSessionFlags() : GameMsg(MessageType::Game::CLEAR_SESSION_FLAGS) {}
+	};
+
+	struct SetRetroactiveFlags : public GameMsg {
+		SetRetroactiveFlags() : GameMsg(MessageType::Game::SET_RETROACTIVE_FLAGS) {}
+	};
 };
 
 #endif // GAMEMESSAGES_H
diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp
index 2a841e39..f847e8ac 100644
--- a/dGame/dMission/Mission.cpp
+++ b/dGame/dMission/Mission.cpp
@@ -390,10 +390,15 @@ void Mission::Catchup() {
 		}
 
 		if (type == eMissionTaskType::PLAYER_FLAG) {
-			for (int32_t target : task->GetAllTargets()) {
-				const auto flag = GetCharacter()->GetPlayerFlag(target);
+			GameMessages::GetFlag getFlag{};
+			getFlag.target = entity->GetObjectID();
 
-				if (!flag) {
+			for (int32_t target : task->GetAllTargets()) {
+				getFlag.iFlagId = target;
+				getFlag.bFlag = false;
+				SEND_ENTITY_MSG(getFlag);
+
+				if (!getFlag.bFlag) {
 					continue;
 				}
 
diff --git a/dGame/dUtilities/Preconditions.cpp b/dGame/dUtilities/Preconditions.cpp
index 118d9037..8797792d 100644
--- a/dGame/dUtilities/Preconditions.cpp
+++ b/dGame/dUtilities/Preconditions.cpp
@@ -146,17 +146,22 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
 		return missionComponent->GetMissionState(value) == eMissionState::AVAILABLE || missionComponent->GetMissionState(value) == eMissionState::COMPLETE_AVAILABLE;
 	case PreconditionType::OnMission:
 		if (missionComponent == nullptr) return false;
-		return  missionComponent->GetMissionState(value) == eMissionState::ACTIVE || 
-				missionComponent->GetMissionState(value) == eMissionState::COMPLETE_ACTIVE ||
-				missionComponent->GetMissionState(value) == eMissionState::READY_TO_COMPLETE ||
-				missionComponent->GetMissionState(value) == eMissionState::COMPLETE_READY_TO_COMPLETE;
+		return  missionComponent->GetMissionState(value) == eMissionState::ACTIVE ||
+			missionComponent->GetMissionState(value) == eMissionState::COMPLETE_ACTIVE ||
+			missionComponent->GetMissionState(value) == eMissionState::READY_TO_COMPLETE ||
+			missionComponent->GetMissionState(value) == eMissionState::COMPLETE_READY_TO_COMPLETE;
 	case PreconditionType::MissionComplete:
 		if (missionComponent == nullptr) return false;
 		return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
 	case PreconditionType::PetDeployed:
 		return false; // TODO
-	case PreconditionType::HasFlag:
-		return character->GetPlayerFlag(value);
+	case PreconditionType::HasFlag: {
+		GameMessages::GetFlag getFlag{};
+		getFlag.target = player->GetObjectID();
+		getFlag.iFlagId = value;
+		SEND_ENTITY_MSG(getFlag);
+		return getFlag.bFlag;
+	}
 	case PreconditionType::WithinShape:
 		return true; // Client checks this one
 	case PreconditionType::InBuild:
diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp
index 43f46746..1cbab3bc 100644
--- a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp
+++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp
@@ -108,11 +108,18 @@ namespace DEVGMCommands {
 
 	void ToggleSkipCinematics(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
 		if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" && entity->GetGMLevel() < eGameMasterLevel::DEVELOPER) return;
-		auto* character = entity->GetCharacter();
-		if (!character) return;
-		bool current = character->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS);
-		character->SetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS, !current);
-		if (!current) {
+		GameMessages::GetFlag current{};
+		current.target = entity->GetObjectID();
+		current.iFlagId = ePlayerFlag::DLU_SKIP_CINEMATICS;
+		SEND_ENTITY_MSG(current);
+
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = entity->GetObjectID();
+		setFlag.iFlagId = ePlayerFlag::DLU_SKIP_CINEMATICS;
+		setFlag.bFlag = !current.bFlag;
+		SEND_ENTITY_MSG(setFlag);
+
+		if (!current.bFlag) {
 			GameMessages::SendSlashCommandFeedbackText(entity, u"You have elected to skip cinematics. Note that not all cinematics can be skipped, but most will be skipped now.");
 		} else {
 			GameMessages::SendSlashCommandFeedbackText(entity, u"Cinematics will no longer be skipped.");
@@ -424,6 +431,9 @@ namespace DEVGMCommands {
 
 	void SetFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
 		const auto splitArgs = GeneralUtils::SplitString(args, ' ');
+
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = entity->GetObjectID();
 		if (splitArgs.size() == 1) {
 			const auto flagId = GeneralUtils::TryParse<int32_t>(splitArgs.at(0));
 
@@ -431,8 +441,9 @@ namespace DEVGMCommands {
 				ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag id.");
 				return;
 			}
-
-			entity->GetCharacter()->SetPlayerFlag(flagId.value(), true);
+			setFlag.iFlagId = flagId.value();
+			setFlag.bFlag = true;
+			SEND_ENTITY_MSG(setFlag);
 		} else if (splitArgs.size() >= 2) {
 			const auto flagId = GeneralUtils::TryParse<int32_t>(splitArgs.at(1));
 			std::string onOffFlag = splitArgs.at(0);
@@ -445,12 +456,17 @@ namespace DEVGMCommands {
 				ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag type.");
 				return;
 			}
-
-			entity->GetCharacter()->SetPlayerFlag(flagId.value(), onOffFlag == "on");
+			setFlag.iFlagId = flagId.value();
+			setFlag.bFlag = onOffFlag == "on";
+			SEND_ENTITY_MSG(setFlag);
 		}
 	}
 
 	void ClearFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = entity->GetObjectID();
+		setFlag.bFlag = false;
+
 		const auto splitArgs = GeneralUtils::SplitString(args, ' ');
 		if (splitArgs.empty()) return;
 
@@ -461,7 +477,8 @@ namespace DEVGMCommands {
 			return;
 		}
 
-		entity->GetCharacter()->SetPlayerFlag(flagId.value(), false);
+		setFlag.iFlagId = flagId.value();
+		SEND_ENTITY_MSG(setFlag);
 	}
 
 	void PlayEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
diff --git a/dScripts/02_server/Map/AG/AgCagedBricksServer.cpp b/dScripts/02_server/Map/AG/AgCagedBricksServer.cpp
index 4d7e8a64..dbbb60d5 100644
--- a/dScripts/02_server/Map/AG/AgCagedBricksServer.cpp
+++ b/dScripts/02_server/Map/AG/AgCagedBricksServer.cpp
@@ -1,9 +1,8 @@
 #include "AgCagedBricksServer.h"
+
 #include "InventoryComponent.h"
 #include "GameMessages.h"
-#include "Character.h"
 #include "EntityManager.h"
-#include "eReplicaComponentType.h"
 #include "ePlayerFlag.h"
 
 void AgCagedBricksServer::OnUse(Entity* self, Entity* user) {
@@ -14,14 +13,14 @@ void AgCagedBricksServer::OnUse(Entity* self, Entity* user) {
 	}
 
 	//Set the flag & mission status:
-	auto character = user->GetCharacter();
-
-	if (!character) return;
-
-	character->SetPlayerFlag(ePlayerFlag::CAGED_SPIDER, true);
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = user->GetObjectID();
+	setFlag.iFlagId = ePlayerFlag::CAGED_SPIDER;
+	setFlag.bFlag = true;
+	SEND_ENTITY_MSG(setFlag);
 
 	//Remove the maelstrom cube:
-	auto inv = static_cast<InventoryComponent*>(user->GetComponent(eReplicaComponentType::INVENTORY));
+	auto* inv = user->GetComponent<InventoryComponent>();
 
 	if (inv) {
 		inv->RemoveItem(14553, 1);
diff --git a/dScripts/02_server/Map/AG/RemoveRentalGear.cpp b/dScripts/02_server/Map/AG/RemoveRentalGear.cpp
index f9bdf1ce..150b1d85 100644
--- a/dScripts/02_server/Map/AG/RemoveRentalGear.cpp
+++ b/dScripts/02_server/Map/AG/RemoveRentalGear.cpp
@@ -1,9 +1,8 @@
 #include "RemoveRentalGear.h"
+
 #include "InventoryComponent.h"
 #include "Item.h"
 #include "eMissionState.h"
-#include "Character.h"
-#include "eReplicaComponentType.h"
 #include "ePlayerFlag.h"
 
 /*
@@ -23,7 +22,7 @@ void RemoveRentalGear::OnMissionDialogueOK(Entity* self, Entity* target, int mis
 	if (missionID != defaultMission && missionID != 313) return;
 
 	if (missionState == eMissionState::COMPLETE || missionState == eMissionState::READY_TO_COMPLETE) {
-		auto inv = static_cast<InventoryComponent*>(target->GetComponent(eReplicaComponentType::INVENTORY));
+		auto* inv = target->GetComponent<InventoryComponent>();
 		if (!inv) return;
 
 		//remove the inventory items
@@ -36,7 +35,10 @@ void RemoveRentalGear::OnMissionDialogueOK(Entity* self, Entity* target, int mis
 		}
 
 		//reset the equipment flag
-		auto character = target->GetCharacter();
-		if (character) character->SetPlayerFlag(ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR, false);
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = target->GetObjectID();
+		setFlag.iFlagId = ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR;
+		setFlag.bFlag = false;
+		SEND_ENTITY_MSG(setFlag);
 	}
 }
diff --git a/dScripts/02_server/Map/AM/AmBlueX.cpp b/dScripts/02_server/Map/AM/AmBlueX.cpp
index 180d4c54..f7a83708 100644
--- a/dScripts/02_server/Map/AM/AmBlueX.cpp
+++ b/dScripts/02_server/Map/AM/AmBlueX.cpp
@@ -2,7 +2,6 @@
 #include "SkillComponent.h"
 #include "EntityManager.h"
 #include "EntityInfo.h"
-#include "Character.h"
 
 void AmBlueX::OnUse(Entity* self, Entity* user) {
 	auto* skillComponent = user->GetComponent<SkillComponent>();
@@ -16,10 +15,11 @@ void AmBlueX::OnSkillEventFired(Entity* self, Entity* caster, const std::string&
 		self->SetNetworkVar<bool>(m_XUsedVariable, true);
 		self->SetNetworkVar<bool>(m_StartEffectVariable, true);
 
-		auto* character = caster->GetCharacter();
-		if (character != nullptr) {
-			character->SetPlayerFlag(self->GetVar<int32_t>(m_FlagVariable), true);
-		}
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = caster->GetObjectID();
+		setFlag.iFlagId = self->GetVar<int32_t>(m_FlagVariable);
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 
 		EntityInfo info{};
 		info.lot = m_FXObject;
diff --git a/dScripts/02_server/Map/General/Binoculars.cpp b/dScripts/02_server/Map/General/Binoculars.cpp
index 854ccf71..2ef02e63 100644
--- a/dScripts/02_server/Map/General/Binoculars.cpp
+++ b/dScripts/02_server/Map/General/Binoculars.cpp
@@ -1,15 +1,26 @@
 #include "Binoculars.h"
-#include "Character.h"
+
 #include "GameMessages.h"
 #include "Game.h"
-#include "dServer.h"
+#include "dZoneManager.h"
 
 void Binoculars::OnUse(Entity* self, Entity* user) {
 	const auto number = self->GetVarAsString(u"number");
 
-	int32_t flag = std::stoi(std::to_string(Game::server->GetZoneID()).substr(0, 2) + number);
-	if (user->GetCharacter()->GetPlayerFlag(flag) == false) {
-		user->GetCharacter()->SetPlayerFlag(flag, true);
+	int32_t flag = std::stoi(std::to_string(Game::zoneManager->GetZoneID().GetMapID()).substr(0, 2) + number);
+
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = user->GetObjectID();
+	getFlag.iFlagId = flag;
+	SEND_ENTITY_MSG(getFlag);
+
+	if (!getFlag.bFlag) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = user->GetObjectID();
+		setFlag.iFlagId = flag;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
+
 		GameMessages::SendFireEventClientSide(self->GetObjectID(), user->GetSystemAddress(), u"achieve", LWOOBJID_EMPTY, 0, -1, LWOOBJID_EMPTY);
 	}
 }
diff --git a/dScripts/02_server/Map/General/Ninjago/NjRailActivatorsServer.cpp b/dScripts/02_server/Map/General/Ninjago/NjRailActivatorsServer.cpp
index fee3cd42..088adae1 100644
--- a/dScripts/02_server/Map/General/Ninjago/NjRailActivatorsServer.cpp
+++ b/dScripts/02_server/Map/General/Ninjago/NjRailActivatorsServer.cpp
@@ -1,6 +1,5 @@
 #include "NjRailActivatorsServer.h"
 #include "QuickBuildComponent.h"
-#include "Character.h"
 
 void NjRailActivatorsServer::OnUse(Entity* self, Entity* user) {
 	const auto flag = self->GetVar<int32_t>(u"RailFlagNum");
@@ -8,9 +7,10 @@ void NjRailActivatorsServer::OnUse(Entity* self, Entity* user) {
 
 	// Only allow use if this is not a quick build or the quick build is built
 	if (quickBuildComponent == nullptr || quickBuildComponent->GetState() == eQuickBuildState::COMPLETED) {
-		auto* character = user->GetCharacter();
-		if (character != nullptr) {
-			character->SetPlayerFlag(flag, true);
-		}
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = user->GetObjectID();
+		setFlag.iFlagId = flag;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 	}
 }
diff --git a/dScripts/02_server/Map/General/PetDigServer.cpp b/dScripts/02_server/Map/General/PetDigServer.cpp
index 77a50e5a..7302e1e4 100644
--- a/dScripts/02_server/Map/General/PetDigServer.cpp
+++ b/dScripts/02_server/Map/General/PetDigServer.cpp
@@ -1,10 +1,9 @@
-#include "dZoneManager.h"
 #include "PetDigServer.h"
+
+#include "dZoneManager.h"
 #include "MissionComponent.h"
 #include "EntityManager.h"
-#include "Character.h"
 #include "PetComponent.h"
-#include "User.h"
 #include "eMissionState.h"
 
 std::vector<LWOOBJID> PetDigServer::treasures{};
@@ -110,7 +109,6 @@ void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pe
 	if (!playerEntity || !playerEntity->GetCharacter())
 		return;
 
-	auto* player = playerEntity->GetCharacter();
 	const auto groupID = self->GetVar<std::u16string>(u"groupID");
 	int32_t playerFlag = 0;
 
@@ -123,15 +121,22 @@ void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pe
 		playerFlag = 63;
 	}
 
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = playerEntity->GetObjectID();
+	getFlag.iFlagId = playerFlag;
+	SEND_ENTITY_MSG(getFlag);
 	// If the player doesn't have the flag yet
-	if (playerFlag != 0 && !player->GetPlayerFlag(playerFlag)) {
+	if (playerFlag != 0 && SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
 		auto* petComponent = pet->GetComponent<PetComponent>();
 		if (petComponent != nullptr) {
 			// TODO: Pet state = 9 ??
 		}
 
-		// Shows the flag object to the player
-		player->SetPlayerFlag(playerFlag, true);
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = playerEntity->GetObjectID();
+		setFlag.iFlagId = playerFlag;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 	}
 
 	auto* xObject = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"X"));
@@ -173,12 +178,17 @@ void PetDigServer::ProgressPetDigMissions(const Entity* owner, const Entity* che
 		if (excavatorMissionState == eMissionState::ACTIVE) {
 			if (chest->HasVar(u"PetDig")) {
 				int32_t playerFlag = 1260 + chest->GetVarAs<int32_t>(u"PetDig");
-				Character* player = owner->GetCharacter();
-
+				GameMessages::GetFlag getFlag{};
+				getFlag.target = owner->GetObjectID();
+				getFlag.iFlagId = playerFlag;
 				// check if player flag is set
-				if (!player->GetPlayerFlag(playerFlag)) {
+				if (SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
 					missionComponent->ForceProgress(505, 767, 1);
-					player->SetPlayerFlag(playerFlag, 1);
+					GameMessages::SetFlag setFlag{};
+					setFlag.target = owner->GetObjectID();
+					setFlag.iFlagId = playerFlag;
+					setFlag.bFlag = true;
+					SEND_ENTITY_MSG(setFlag);
 				}
 			}
 		}
diff --git a/dScripts/02_server/Map/General/StoryBoxInteractServer.cpp b/dScripts/02_server/Map/General/StoryBoxInteractServer.cpp
index 4ad78d6a..e49d675d 100644
--- a/dScripts/02_server/Map/General/StoryBoxInteractServer.cpp
+++ b/dScripts/02_server/Map/General/StoryBoxInteractServer.cpp
@@ -1,7 +1,7 @@
 #include "StoryBoxInteractServer.h"
 #include "Character.h"
 #include "GameMessages.h"
-#include "dServer.h"
+#include "dZoneManager.h"
 #include "Amf3.h"
 #include "Entity.h"
 
@@ -36,11 +36,19 @@ void StoryBoxInteractServer::OnUse(Entity* self, Entity* user) {
 		if(!storyValue) return;
 		int32_t boxFlag = self->GetVar<int32_t>(u"altFlagID");
 		if (boxFlag <= 0) {
-			boxFlag = (10000 + Game::server->GetZoneID() + storyValue.value());
+			boxFlag = (10000 + Game::zoneManager->GetZoneID().GetMapID() + storyValue.value());
 		}
 
-		if (user->GetCharacter()->GetPlayerFlag(boxFlag) == false) {
-			user->GetCharacter()->SetPlayerFlag(boxFlag, true);
+		GameMessages::GetFlag getFlag{};
+		getFlag.target = user->GetObjectID();
+		getFlag.iFlagId = boxFlag;
+		if (SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
+			GameMessages::SetFlag setFlag{};
+			setFlag.target = user->GetObjectID();
+			setFlag.iFlagId = boxFlag;
+			setFlag.bFlag = true;
+			SEND_ENTITY_MSG(setFlag);
+
 			GameMessages::SendFireEventClientSide(self->GetObjectID(), user->GetSystemAddress(), u"achieve", LWOOBJID_EMPTY, 0, -1, LWOOBJID_EMPTY);
 		}
 	}
diff --git a/dScripts/02_server/Map/General/TokenConsoleServer.cpp b/dScripts/02_server/Map/General/TokenConsoleServer.cpp
index 0a1f679c..e0de6922 100644
--- a/dScripts/02_server/Map/General/TokenConsoleServer.cpp
+++ b/dScripts/02_server/Map/General/TokenConsoleServer.cpp
@@ -1,15 +1,14 @@
 #include "TokenConsoleServer.h"
+
 #include "InventoryComponent.h"
 #include "GameMessages.h"
-#include "Character.h"
-#include "eReplicaComponentType.h"
 #include "eTerminateType.h"
 #include "ePlayerFlag.h"
 
 //2021-05-03 - max - added script, omitted some parts related to inheritance in lua which we don't need
 
 void TokenConsoleServer::OnUse(Entity* self, Entity* user) {
-	auto* inv = static_cast<InventoryComponent*>(user->GetComponent(eReplicaComponentType::INVENTORY));
+	auto* inv = user->GetComponent<InventoryComponent>();
 
 	//make sure the user has the required amount of infected bricks
 	if (inv && inv->GetLotCount(6194) >= bricksToTake) {
@@ -22,17 +21,18 @@ void TokenConsoleServer::OnUse(Entity* self, Entity* user) {
 		}
 
 		//figure out which faction the player belongs to:
-		auto character = user->GetCharacter();
-		if (!character) return;
 		// At this point the player has to be in a faction.
+
+		GameMessages::GetFlag getFlag{};
+		getFlag.target = user->GetObjectID();
 		LOT tokenLOT = 0;
-		if (character->GetPlayerFlag(ePlayerFlag::VENTURE_FACTION)) //venture
+		if (getFlag.iFlagId = ePlayerFlag::VENTURE_FACTION, SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) //venture
 			tokenLOT = 8321;
-		else if (character->GetPlayerFlag(ePlayerFlag::ASSEMBLY_FACTION)) //assembly
+		else if (getFlag.iFlagId = ePlayerFlag::ASSEMBLY_FACTION, SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) //assembly
 			tokenLOT = 8318;
-		else if (character->GetPlayerFlag(ePlayerFlag::PARADOX_FACTION)) //paradox
+		else if (getFlag.iFlagId = ePlayerFlag::PARADOX_FACTION, SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) //paradox
 			tokenLOT = 8320;
-		else if (character->GetPlayerFlag(ePlayerFlag::SENTINEL_FACTION)) //sentinel
+		else if (getFlag.iFlagId = ePlayerFlag::SENTINEL_FACTION, SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) //sentinel
 			tokenLOT = 8319;
 		inv->AddItem(tokenLOT, tokensToGive, eLootSourceType::NONE);
 	}
diff --git a/dScripts/02_server/Map/NS/NsTokenConsoleServer.cpp b/dScripts/02_server/Map/NS/NsTokenConsoleServer.cpp
index 7d825828..47744324 100644
--- a/dScripts/02_server/Map/NS/NsTokenConsoleServer.cpp
+++ b/dScripts/02_server/Map/NS/NsTokenConsoleServer.cpp
@@ -1,7 +1,6 @@
 #include "NsTokenConsoleServer.h"
 #include "InventoryComponent.h"
 #include "GameMessages.h"
-#include "Character.h"
 #include "MissionComponent.h"
 #include "QuickBuildComponent.h"
 #include "eTerminateType.h"
@@ -24,9 +23,8 @@ void NsTokenConsoleServer::OnUse(Entity* self, Entity* user) {
 
 	auto* inventoryComponent = user->GetComponent<InventoryComponent>();
 	auto* missionComponent = user->GetComponent<MissionComponent>();
-	auto* character = user->GetCharacter();
 
-	if (inventoryComponent == nullptr || missionComponent == nullptr || character == nullptr) {
+	if (inventoryComponent == nullptr || missionComponent == nullptr) {
 		return;
 	}
 
@@ -42,15 +40,18 @@ void NsTokenConsoleServer::OnUse(Entity* self, Entity* user) {
 		GameMessages::SendPlayNDAudioEmitter(self, user->GetSystemAddress(), useSound);
 	}
 
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = user->GetObjectID();
+
 	// Player must be in faction to interact with this entity.
 	LOT tokenLOT = 0;
-	if (character->GetPlayerFlag(ePlayerFlag::VENTURE_FACTION)) //venture
+	if (getFlag.iFlagId = ePlayerFlag::VENTURE_FACTION, SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) //venture
 		tokenLOT = 8321;
-	else if (character->GetPlayerFlag(ePlayerFlag::ASSEMBLY_FACTION)) //assembly
+	else if (getFlag.iFlagId = ePlayerFlag::ASSEMBLY_FACTION, SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) //assembly
 		tokenLOT = 8318;
-	else if (character->GetPlayerFlag(ePlayerFlag::PARADOX_FACTION)) //paradox
+	else if (getFlag.iFlagId = ePlayerFlag::PARADOX_FACTION, SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) //paradox
 		tokenLOT = 8320;
-	else if (character->GetPlayerFlag(ePlayerFlag::SENTINEL_FACTION)) //sentinel
+	else if (getFlag.iFlagId = ePlayerFlag::SENTINEL_FACTION, SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) //sentinel
 		tokenLOT = 8319;
 
 	inventoryComponent->AddItem(tokenLOT, 5, eLootSourceType::NONE);
diff --git a/dScripts/02_server/Map/NT/NTPipeVisibilityServer.cpp b/dScripts/02_server/Map/NT/NTPipeVisibilityServer.cpp
index 7d39e9d9..d4a4c4d6 100644
--- a/dScripts/02_server/Map/NT/NTPipeVisibilityServer.cpp
+++ b/dScripts/02_server/Map/NT/NTPipeVisibilityServer.cpp
@@ -1,15 +1,15 @@
 #include "NTPipeVisibilityServer.h"
+
 #include "Entity.h"
-#include "Character.h"
 
 void NTPipeVisibilityServer::OnQuickBuildComplete(Entity* self, Entity* target) {
 	const auto flag = self->GetVar<int32_t>(u"flag");
 	if (flag == 0) return;
-
-	auto* character = target->GetCharacter();
-	if (!character) return;
-
-	character->SetPlayerFlag(flag, true);
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = target->GetObjectID();
+	setFlag.iFlagId = flag;
+	setFlag.bFlag = true;
+	SEND_ENTITY_MSG(setFlag);
 
 	GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PipeBuilt");
 }
diff --git a/dScripts/02_server/Map/NT/NtImagimeterVisibility.cpp b/dScripts/02_server/Map/NT/NtImagimeterVisibility.cpp
index 8003f4df..03b60f40 100644
--- a/dScripts/02_server/Map/NT/NtImagimeterVisibility.cpp
+++ b/dScripts/02_server/Map/NT/NtImagimeterVisibility.cpp
@@ -1,12 +1,14 @@
 #include "NtImagimeterVisibility.h"
 #include "GameMessages.h"
 #include "Entity.h"
-#include "Character.h"
 #include "ePlayerFlag.h"
 
 void NTImagimeterVisibility::OnQuickBuildComplete(Entity* self, Entity* target) {
-	auto* character = target->GetCharacter();
-	if (character) character->SetPlayerFlag(ePlayerFlag::NT_PLINTH_REBUILD, true);
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = target->GetObjectID();
+	setFlag.iFlagId = ePlayerFlag::NT_PLINTH_REBUILD;
+	setFlag.bFlag = true;
+	SEND_ENTITY_MSG(setFlag);
 
 	GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlinthBuilt", 0, 0, LWOOBJID_EMPTY, "", target->GetSystemAddress());
 }
diff --git a/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp b/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp
index 0fe97a26..8570bf35 100644
--- a/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp
+++ b/dScripts/02_server/Map/NT/NtParadoxPanelServer.cpp
@@ -1,8 +1,8 @@
 #include "NtParadoxPanelServer.h"
+
 #include "GameMessages.h"
 #include "MissionComponent.h"
 #include "EntityManager.h"
-#include "Character.h"
 #include "eMissionState.h"
 #include "RenderComponent.h"
 #include "eTerminateType.h"
@@ -32,8 +32,11 @@ void NtParadoxPanelServer::OnUse(Entity* self, Entity* user) {
 			}
 
 			const auto flag = self->GetVar<int32_t>(u"flag");
-
-			player->GetCharacter()->SetPlayerFlag(flag, true);
+			GameMessages::SetFlag setFlag{};
+			setFlag.target = playerID;
+			setFlag.iFlagId = flag;
+			setFlag.bFlag = true;
+			SEND_ENTITY_MSG(setFlag);
 
 			RenderComponent::PlayAnimation(player, u"rebuild-celebrate");
 
diff --git a/dScripts/02_server/Map/Property/AG_Small/ZoneAgProperty.cpp b/dScripts/02_server/Map/Property/AG_Small/ZoneAgProperty.cpp
index 6f2f6d36..2c9ef8bb 100644
--- a/dScripts/02_server/Map/Property/AG_Small/ZoneAgProperty.cpp
+++ b/dScripts/02_server/Map/Property/AG_Small/ZoneAgProperty.cpp
@@ -292,34 +292,45 @@ void ZoneAgProperty::BaseTimerDone(Entity* self, const std::string& timerName) {
 void ZoneAgProperty::OnZonePropertyRented(Entity* self, Entity* player) {
 	BaseZonePropertyRented(self, player);
 
-	auto* character = player->GetCharacter();
-	if (character == nullptr)
-		return;
-
-	character->SetPlayerFlag(108, true);
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = player->GetObjectID();
+	setFlag.iFlagId = 108;
+	setFlag.bFlag = true;
+	SEND_ENTITY_MSG(setFlag);
 }
 
 void ZoneAgProperty::OnZonePropertyModelPlaced(Entity* self, Entity* player) {
-	auto* character = player->GetCharacter();
 	auto* missionComponent = player->GetComponent<MissionComponent>();
+	if (!missionComponent) return;
 
-	if (!character->GetPlayerFlag(101)) {
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = player->GetObjectID();
+
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = player->GetObjectID();
+	setFlag.bFlag = true;
+
+	if (getFlag.iFlagId = 101, SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
 		BaseZonePropertyModelPlaced(self, player);
-		character->SetPlayerFlag(101, true);
+		setFlag.iFlagId = 101;
+		SEND_ENTITY_MSG(setFlag);
 		if (missionComponent->GetMissionState(871) == eMissionState::ACTIVE) {
 			self->SetNetworkVar<std::u16string>(u"Tooltip", u"AnotherModel");
 		}
 
-	} else if (!character->GetPlayerFlag(102)) {
-		character->SetPlayerFlag(102, true);
+	} else if (getFlag.iFlagId = 102, SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
+		setFlag.iFlagId = 102;
+		SEND_ENTITY_MSG(setFlag);
 		if (missionComponent->GetMissionState(871) == eMissionState::ACTIVE) {
 			self->SetNetworkVar<std::u16string>(u"Tooltip", u"TwoMoreModels");
 		}
 
-	} else if (!character->GetPlayerFlag(103)) {
-		character->SetPlayerFlag(103, true);
-	} else if (!character->GetPlayerFlag(104)) {
-		character->SetPlayerFlag(104, true);
+	} else if (getFlag.iFlagId = 103, SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
+		setFlag.iFlagId = 103;
+		SEND_ENTITY_MSG(setFlag);
+	} else if (getFlag.iFlagId = 104, SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
+		setFlag.iFlagId = 104;
+		SEND_ENTITY_MSG(setFlag);
 		self->SetNetworkVar<std::u16string>(u"Tooltip", u"TwoMoreModelsOff");
 	} else if (self->GetVar<std::string>(u"tutorial") == "place_model") {
 		self->SetVar<std::string>(u"tutorial", "");
@@ -328,20 +339,34 @@ void ZoneAgProperty::OnZonePropertyModelPlaced(Entity* self, Entity* player) {
 }
 
 void ZoneAgProperty::OnZonePropertyModelPickedUp(Entity* self, Entity* player) {
-	auto* character = player->GetCharacter();
 	auto* missionComponent = player->GetComponent<MissionComponent>();
+	if (!missionComponent) return;
 
-	if (!character->GetPlayerFlag(109)) {
-		character->SetPlayerFlag(109, true);
-		if (missionComponent->GetMissionState(891) == eMissionState::ACTIVE && !character->GetPlayerFlag(110)) {
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = player->GetObjectID();
+	getFlag.iFlagId = 109;
+
+	if (SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = player->GetObjectID();
+		setFlag.iFlagId = 109;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
+
+		getFlag.iFlagId = 110;
+		getFlag.bFlag = false;
+		if (missionComponent->GetMissionState(891) == eMissionState::ACTIVE && SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
 			self->SetNetworkVar<std::u16string>(u"Tooltip", u"Rotate");
 		}
 	}
 }
 
 void ZoneAgProperty::OnZonePropertyModelRemoved(Entity* self, Entity* player) {
-	auto* character = player->GetCharacter();
-	character->SetPlayerFlag(111, true);
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = player->GetObjectID();
+	setFlag.iFlagId = 111;
+	setFlag.bFlag = true;
+	SEND_ENTITY_MSG(setFlag);
 }
 
 void ZoneAgProperty::OnZonePropertyModelRemovedWhileEquipped(Entity* self, Entity* player) {
@@ -349,11 +374,18 @@ void ZoneAgProperty::OnZonePropertyModelRemovedWhileEquipped(Entity* self, Entit
 }
 
 void ZoneAgProperty::OnZonePropertyModelRotated(Entity* self, Entity* player) {
-	auto* character = player->GetCharacter();
 	auto* missionComponent = player->GetComponent<MissionComponent>();
+	if (!missionComponent) return;
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = player->GetObjectID();
+	getFlag.iFlagId = 110;
 
-	if (!character->GetPlayerFlag(110)) {
-		character->SetPlayerFlag(110, true);
+	if (SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = player->GetObjectID();
+		setFlag.iFlagId = 110;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 
 		if (missionComponent->GetMissionState(891) == eMissionState::ACTIVE) {
 			self->SetNetworkVar<std::u16string>(u"Tooltip", u"PlaceModel");
@@ -413,7 +445,12 @@ void ZoneAgProperty::BaseOnFireEventServerSide(Entity* self, Entity* sender, std
 		if (player == nullptr)
 			return;
 
-		player->GetCharacter()->SetPlayerFlag(self->GetVar<int32_t>(defeatedProperyFlag), true);
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = player->GetObjectID();
+		setFlag.iFlagId = self->GetVar<int32_t>(defeatedProperyFlag);
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
+
 		GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0,
 			LWOOBJID_EMPTY, destroyedCinematic, UNASSIGNED_SYSTEM_ADDRESS);
 
diff --git a/dScripts/02_server/Map/VE/VeEpsilonServer.cpp b/dScripts/02_server/Map/VE/VeEpsilonServer.cpp
index 4e8cb4e9..ab182d7d 100644
--- a/dScripts/02_server/Map/VE/VeEpsilonServer.cpp
+++ b/dScripts/02_server/Map/VE/VeEpsilonServer.cpp
@@ -1,21 +1,21 @@
 #include "VeEpsilonServer.h"
-#include "Character.h"
+
 #include "EntityManager.h"
 #include "GameMessages.h"
 #include "eMissionState.h"
 #include "Entity.h"
 
 void VeEpsilonServer::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, eMissionState missionState) {
-	auto* character = target->GetCharacter();
-	if (character == nullptr)
-		return;
-
 	// Resets the player flags that track which consoles they've used
 	if ((missionID == m_ConsoleMissionID || missionID == m_ConsoleRepeatMissionID)
 		&& (missionState == eMissionState::AVAILABLE || missionState == eMissionState::COMPLETE_AVAILABLE)) {
 
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = target->GetObjectID();
 		for (auto i = 0; i < 10; i++) {
-			character->SetPlayerFlag(m_ConsoleBaseFlag + i, false);
+			setFlag.iFlagId = m_ConsoleBaseFlag + i;
+			setFlag.bFlag = false;
+			SEND_ENTITY_MSG(setFlag);
 		}
 	}
 
diff --git a/dScripts/02_server/Map/VE/VeMissionConsole.cpp b/dScripts/02_server/Map/VE/VeMissionConsole.cpp
index aa9abb93..341e2491 100644
--- a/dScripts/02_server/Map/VE/VeMissionConsole.cpp
+++ b/dScripts/02_server/Map/VE/VeMissionConsole.cpp
@@ -1,6 +1,6 @@
 #include "VeMissionConsole.h"
+
 #include "InventoryComponent.h"
-#include "Character.h"
 #include "GameMessages.h"
 #include "Loot.h"
 #include "eTerminateType.h"
@@ -17,10 +17,11 @@ void VeMissionConsole::OnUse(Entity* self, Entity* user) {
 	const auto flagNumber = self->GetVar<std::u16string>(m_NumberVariable);
 	const int32_t flag = std::stoi("101" + GeneralUtils::UTF16ToWTF8(flagNumber));
 
-	auto* character = user->GetCharacter();
-	if (character != nullptr) {
-		character->SetPlayerFlag(flag, true);
-	}
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = user->GetObjectID();
+	setFlag.iFlagId = flag;
+	setFlag.bFlag = true;
+	SEND_ENTITY_MSG(setFlag);
 
 	GameMessages::SendNotifyClientObject(self->GetObjectID(), u"");
 	GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID());
diff --git a/dScripts/02_server/Map/njhub/CavePrisonCage.cpp b/dScripts/02_server/Map/njhub/CavePrisonCage.cpp
index 8496ecfb..492540c3 100644
--- a/dScripts/02_server/Map/njhub/CavePrisonCage.cpp
+++ b/dScripts/02_server/Map/njhub/CavePrisonCage.cpp
@@ -1,8 +1,8 @@
 #include "CavePrisonCage.h"
+
 #include "EntityManager.h"
 #include "QuickBuildComponent.h"
 #include "GameMessages.h"
-#include "Character.h"
 #include "dZoneManager.h"
 #include "RenderComponent.h"
 
@@ -161,10 +161,14 @@ void CavePrisonCage::OnTimerDone(Entity* self, std::string timerName) {
 			return;
 		}
 
+		// Set the flag on the builder character
 		const auto flagNum = 2020 + self->GetVarAs<int32_t>(u"myNumber");
 
-		// Set the flag on the builder character
-		builder->GetCharacter()->SetPlayerFlag(flagNum, true);
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = builder->GetObjectID();
+		setFlag.iFlagId = flagNum;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 
 		// Setup a timer named 'VillagerEscape' to be triggered in 5 seconds
 		self->AddTimer("VillagerEscape", 5.0f);
diff --git a/dScripts/02_server/Map/njhub/NjDragonEmblemChestServer.cpp b/dScripts/02_server/Map/njhub/NjDragonEmblemChestServer.cpp
index 74e50b1c..f12aba1a 100644
--- a/dScripts/02_server/Map/njhub/NjDragonEmblemChestServer.cpp
+++ b/dScripts/02_server/Map/njhub/NjDragonEmblemChestServer.cpp
@@ -1,16 +1,15 @@
 #include "NjDragonEmblemChestServer.h"
-#include "Character.h"
-#include "EntityInfo.h"
 #include "Loot.h"
 #include "Entity.h"
 #include "DestroyableComponent.h"
 #include "ePlayerFlag.h"
 
 void NjDragonEmblemChestServer::OnUse(Entity* self, Entity* user) {
-	auto* character = user->GetCharacter();
-	if (character != nullptr) {
-		character->SetPlayerFlag(ePlayerFlag::NJ_WU_SHOW_DAILY_CHEST, false);
-	}
+	GameMessages::SetFlag setFlag{};
+	setFlag.target = user->GetObjectID();
+	setFlag.iFlagId = ePlayerFlag::NJ_WU_SHOW_DAILY_CHEST;
+	setFlag.bFlag = false;
+	SEND_ENTITY_MSG(setFlag);
 
 	auto* destroyable = self->GetComponent<DestroyableComponent>();
 	if (destroyable != nullptr) {
diff --git a/dScripts/02_server/Map/njhub/NjGarmadonCelebration.cpp b/dScripts/02_server/Map/njhub/NjGarmadonCelebration.cpp
index d3e54be1..6acc3be9 100644
--- a/dScripts/02_server/Map/njhub/NjGarmadonCelebration.cpp
+++ b/dScripts/02_server/Map/njhub/NjGarmadonCelebration.cpp
@@ -1,17 +1,19 @@
 #include "NjGarmadonCelebration.h"
-#include "Character.h"
+
 #include "GameMessages.h"
 #include "ePlayerFlag.h"
 
 void NjGarmadonCelebration::OnCollisionPhantom(Entity* self, Entity* target) {
-	auto* character = target->GetCharacter();
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = target->GetObjectID();
+	getFlag.iFlagId = ePlayerFlag::NJ_GARMADON_CINEMATIC_SEEN;
 
-	if (character == nullptr) {
-		return;
-	}
-
-	if (!character->GetPlayerFlag(ePlayerFlag::NJ_GARMADON_CINEMATIC_SEEN)) {
-		character->SetPlayerFlag(ePlayerFlag::NJ_GARMADON_CINEMATIC_SEEN, true);
+	if (SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = target->GetObjectID();
+		setFlag.iFlagId = ePlayerFlag::NJ_GARMADON_CINEMATIC_SEEN;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 
 		GameMessages::SendStartCelebrationEffect(target, target->GetSystemAddress(), GarmadonCelebrationID);
 	}
diff --git a/dScripts/02_server/Map/njhub/NjNPCMissionSpinjitzuServer.cpp b/dScripts/02_server/Map/njhub/NjNPCMissionSpinjitzuServer.cpp
index 37a5754c..ed1ed70b 100644
--- a/dScripts/02_server/Map/njhub/NjNPCMissionSpinjitzuServer.cpp
+++ b/dScripts/02_server/Map/njhub/NjNPCMissionSpinjitzuServer.cpp
@@ -1,6 +1,5 @@
 #include "NjNPCMissionSpinjitzuServer.h"
-#include "Character.h"
-#include "EntityManager.h"
+
 #include "eMissionState.h"
 
 void NjNPCMissionSpinjitzuServer::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, eMissionState missionState) {
@@ -12,13 +11,11 @@ void NjNPCMissionSpinjitzuServer::OnMissionDialogueOK(Entity* self, Entity* targ
 
 		// Wait for an animation to complete and flag that the player has learned spinjitzu
 		self->AddCallbackTimer(5.0f, [targetID, element]() {
-			auto* target = Game::entityManager->GetEntity(targetID);
-			if (target != nullptr) {
-				auto* character = target->GetCharacter();
-				if (character != nullptr) {
-					character->SetPlayerFlag(ElementFlags.at(element), true);
-				}
-			}
+			GameMessages::SetFlag setFlag{};
+			setFlag.target = targetID;
+			setFlag.iFlagId = ElementFlags.at(element);
+			setFlag.bFlag = true;
+			SEND_ENTITY_MSG(setFlag);
 			});
 	}
 }
diff --git a/dScripts/02_server/Map/njhub/NjWuNPC.cpp b/dScripts/02_server/Map/njhub/NjWuNPC.cpp
index 1a5d30fd..0c11e916 100644
--- a/dScripts/02_server/Map/njhub/NjWuNPC.cpp
+++ b/dScripts/02_server/Map/njhub/NjWuNPC.cpp
@@ -1,6 +1,5 @@
 #include "NjWuNPC.h"
 #include "MissionComponent.h"
-#include "Character.h"
 #include "EntityManager.h"
 #include "GameMessages.h"
 #include "eMissionState.h"
@@ -10,10 +9,8 @@ void NjWuNPC::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, e
 
 	// The Dragon statue daily mission
 	if (missionID == m_MainDragonMissionID) {
-		auto* character = target->GetCharacter();
 		auto* missionComponent = target->GetComponent<MissionComponent>();
-		if (character == nullptr || missionComponent == nullptr)
-			return;
+		if (!missionComponent) return;
 
 		switch (missionState) {
 		case eMissionState::AVAILABLE:
@@ -24,8 +21,11 @@ void NjWuNPC::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, e
 				missionComponent->RemoveMission(subMissionID);
 				missionComponent->AcceptMission(subMissionID);
 			}
-
-			character->SetPlayerFlag(ePlayerFlag::NJ_WU_SHOW_DAILY_CHEST, false);
+			GameMessages::SetFlag setFlag{};
+			setFlag.target = target->GetObjectID();
+			setFlag.iFlagId = ePlayerFlag::NJ_WU_SHOW_DAILY_CHEST;
+			setFlag.bFlag = false;
+			SEND_ENTITY_MSG(setFlag);
 
 			// Hide the chest
 			for (auto* chest : Game::entityManager->GetEntitiesInGroup(m_DragonChestGroup)) {
@@ -38,7 +38,11 @@ void NjWuNPC::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, e
 		case eMissionState::READY_TO_COMPLETE:
 		case eMissionState::COMPLETE_READY_TO_COMPLETE:
 		{
-			character->SetPlayerFlag(ePlayerFlag::NJ_WU_SHOW_DAILY_CHEST, true);
+			GameMessages::SetFlag setFlag{};
+			setFlag.target = target->GetObjectID();
+			setFlag.iFlagId = ePlayerFlag::NJ_WU_SHOW_DAILY_CHEST;
+			setFlag.bFlag = true;
+			SEND_ENTITY_MSG(setFlag);
 
 			// Show the chest
 			for (auto* chest : Game::entityManager->GetEntitiesInGroup(m_DragonChestGroup)) {
diff --git a/dScripts/BasePropertyServer.cpp b/dScripts/BasePropertyServer.cpp
index 9048adeb..e2ed5f5d 100644
--- a/dScripts/BasePropertyServer.cpp
+++ b/dScripts/BasePropertyServer.cpp
@@ -127,7 +127,10 @@ void BasePropertyServer::BasePlayerLoaded(Entity* self, Entity* player) {
 		if (player->GetObjectID() != propertyOwner)
 			return;
 	} else {
-		const auto defeatedFlag = player->GetCharacter()->GetPlayerFlag(self->GetVar<int32_t>(defeatedProperyFlag));
+		GameMessages::GetFlag getFlag{};
+		getFlag.target = player->GetObjectID();
+		getFlag.iFlagId = self->GetVar<int32_t>(defeatedProperyFlag);
+		const auto defeatedFlag = SEND_ENTITY_MSG(getFlag) && getFlag.bFlag;
 
 		self->SetNetworkVar(UnclaimedVariable, true);
 		self->SetVar<LWOOBJID>(PlayerIDVariable, player->GetObjectID());
@@ -184,8 +187,13 @@ void BasePropertyServer::BaseZonePropertyModelPlaced(Entity* self, Entity* playe
 		return;
 
 	auto flag = self->GetVar<int32_t>(placedModelFlag);
-	if (flag)
-		character->SetPlayerFlag(flag, true);
+	if (flag) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = player->GetObjectID();
+		setFlag.iFlagId = flag;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
+	}
 }
 
 void BasePropertyServer::KillClouds(Entity* self) {
@@ -462,10 +470,11 @@ void BasePropertyServer::HandleOrbsTimer(Entity* self) {
 				// Notifies the client that the property has been claimed with a flag, completes missions too
 				auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
 				if (player != nullptr) {
-					auto* character = player->GetCharacter();
-					if (character != nullptr) {
-						character->SetPlayerFlag(self->GetVar<int32_t>(defeatedProperyFlag), true);
-					}
+					GameMessages::SetFlag setFlag{};
+					setFlag.target = player->GetObjectID();
+					setFlag.iFlagId = self->GetVar<int32_t>(defeatedProperyFlag);
+					setFlag.bFlag = true;
+					SEND_ENTITY_MSG(setFlag);
 				}
 
 				self->AddTimer(TornadoOffTimer, 0.5f);
diff --git a/dScripts/Darkitect.cpp b/dScripts/Darkitect.cpp
index 2881bde8..b52439ba 100644
--- a/dScripts/Darkitect.cpp
+++ b/dScripts/Darkitect.cpp
@@ -1,9 +1,9 @@
 #include "Darkitect.h"
+
 #include "MissionComponent.h"
 #include "DestroyableComponent.h"
 #include "EntityManager.h"
 #include "GameMessages.h"
-#include "Character.h"
 #include "eMissionState.h"
 
 void Darkitect::Reveal(Entity* self, Entity* player) {
@@ -18,15 +18,18 @@ void Darkitect::Reveal(Entity* self, Entity* player) {
 
 		auto* destroyableComponent = player->GetComponent<DestroyableComponent>();
 		auto* missionComponent = player->GetComponent<MissionComponent>();
-		auto* character = player->GetCharacter();
 
-		if (destroyableComponent != nullptr && missionComponent != nullptr && character != nullptr) {
+		if (destroyableComponent != nullptr && missionComponent != nullptr) {
 			destroyableComponent->SetArmor(0);
 			destroyableComponent->SetHealth(1);
 			destroyableComponent->SetImagination(0);
 
 			if (missionComponent->GetMissionState(1295) == eMissionState::ACTIVE) {
-				character->SetPlayerFlag(1911, true);
+				GameMessages::SetFlag setFlag{};
+				setFlag.target = player->GetObjectID();
+				setFlag.iFlagId = 1911;
+				setFlag.bFlag = true;
+				SEND_ENTITY_MSG(setFlag);
 			}
 
 			Game::entityManager->SerializeEntity(player);
diff --git a/dScripts/EquipmentTriggers/TrialFactionArmorServer.cpp b/dScripts/EquipmentTriggers/TrialFactionArmorServer.cpp
index b59b24aa..99168e6d 100644
--- a/dScripts/EquipmentTriggers/TrialFactionArmorServer.cpp
+++ b/dScripts/EquipmentTriggers/TrialFactionArmorServer.cpp
@@ -1,16 +1,20 @@
 #include "TrialFactionArmorServer.h"
 
-#include "Character.h"
 #include "ePlayerFlag.h"
+#include "GameMessages.h"
+#include "EntityManager.h"
 #include "DestroyableComponent.h"
 
 void TrialFactionArmorServer::OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) {
-	auto* character = itemOwner->GetCharacter();
-	if (!character) return;
+	GameMessages::GetFlag flag{};
+	flag.target = itemOwner->GetObjectID();
+	flag.iFlagId = ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR;
 
-	auto flag = character->GetPlayerFlag(ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR);
-	if (!flag) {
-		character->SetPlayerFlag(ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR, true);
+	if (SEND_ENTITY_MSG(flag) && !flag.bFlag) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.iFlagId = ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 
 		// technically a TimerWithCancel but our current implementation doesnt support this.
 		itemOwner->AddCallbackTimer(1.0f, [itemOwner]() {
diff --git a/dScripts/NtFactionSpyServer.cpp b/dScripts/NtFactionSpyServer.cpp
index 2d88e4ea..0e6ac8b6 100644
--- a/dScripts/NtFactionSpyServer.cpp
+++ b/dScripts/NtFactionSpyServer.cpp
@@ -1,11 +1,10 @@
 #include "NtFactionSpyServer.h"
-#include "Character.h"
+
 #include "ProximityMonitorComponent.h"
 #include "InventoryComponent.h"
 #include "GameMessages.h"
 #include "MissionComponent.h"
 #include "eMissionState.h"
-#include "eReplicaComponentType.h"
 #include "eCinematicEvent.h"
 #include "ePlayerFlag.h"
 
@@ -54,12 +53,14 @@ bool NtFactionSpyServer::IsSpy(Entity* self, Entity* possibleSpy) {
 
 	auto* missionComponent = possibleSpy->GetComponent<MissionComponent>();
 	auto* inventoryComponent = possibleSpy->GetComponent<InventoryComponent>();
-	auto* character = possibleSpy->GetCharacter();
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = possibleSpy->GetObjectID();
+	getFlag.iFlagId = spyData.flagID;
 
 	// A player is a spy if they have the spy mission, have the spy equipment equipped and don't have the spy flag set yet
 	return missionComponent != nullptr && missionComponent->GetMissionState(spyData.missionID) == eMissionState::ACTIVE
 		&& inventoryComponent != nullptr && inventoryComponent->IsEquipped(spyData.itemID)
-		&& character != nullptr && !character->GetPlayerFlag(spyData.flagID);
+		&& SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag;
 }
 
 void NtFactionSpyServer::OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event,
@@ -87,10 +88,11 @@ void NtFactionSpyServer::OnCinematicUpdate(Entity* self, Entity* sender, eCinema
 
 			} else if (event == eCinematicEvent::ENDED && pathIndex >= dialogueTable.size() - 1) {
 				auto spyData = self->GetVar<SpyData>(m_SpyDataVariable);
-				auto* character = sender->GetCharacter();
-				if (character != nullptr) {
-					character->SetPlayerFlag(spyData.flagID, true);
-				}
+				GameMessages::SetFlag setFlag{};
+				setFlag.target = sender->GetObjectID();
+				setFlag.iFlagId = spyData.flagID;
+				setFlag.bFlag = true;
+				SEND_ENTITY_MSG(setFlag);
 			}
 		}
 	}
diff --git a/dScripts/ai/ACT/FootRace/BaseFootRaceManager.cpp b/dScripts/ai/ACT/FootRace/BaseFootRaceManager.cpp
index c5954c4c..4ce9f5ad 100644
--- a/dScripts/ai/ACT/FootRace/BaseFootRaceManager.cpp
+++ b/dScripts/ai/ACT/FootRace/BaseFootRaceManager.cpp
@@ -1,6 +1,6 @@
 #include "BaseFootRaceManager.h"
+
 #include "EntityManager.h"
-#include "Character.h"
 #include "Entity.h"
 
 void BaseFootRaceManager::OnStartup(Entity* self) {
@@ -21,31 +21,31 @@ void BaseFootRaceManager::OnFireEventServerSide(Entity* self, Entity* sender, st
 			if (eventName == "updatePlayer") {
 				UpdatePlayer(self, player->GetObjectID());
 			} else if (IsPlayerInActivity(self, player->GetObjectID())) {
+				GameMessages::SetFlag setFlag{};
+				setFlag.target = player->GetObjectID();
+				setFlag.iFlagId = 115;
 				if (eventName == "initialActivityScore") {
-					auto* character = player->GetCharacter();
-					if (character != nullptr) {
-						character->SetPlayerFlag(115, true);
-					}
+					setFlag.bFlag = true;
+					SEND_ENTITY_MSG(setFlag);
 
 					SetActivityScore(self, player->GetObjectID(), 1);
 				} else if (eventName == "updatePlayerTrue") {
-					auto* character = player->GetCharacter();
-					if (character != nullptr) {
-						character->SetPlayerFlag(115, false);
-					}
+					setFlag.bFlag = false;
+					SEND_ENTITY_MSG(setFlag);
 
 					UpdatePlayer(self, player->GetObjectID(), true);
 				} else if (eventName == "PlayerWon") {
-					auto* character = player->GetCharacter();
-					if (character != nullptr) {
-						character->SetPlayerFlag(115, false);
-						if (param2 != -1) // Certain footraces set a flag
-							character->SetPlayerFlag(param2, true);
+					setFlag.bFlag = false;
+					SEND_ENTITY_MSG(setFlag);
+					if (param2 != -1) {
+						setFlag.iFlagId = param2;
+						setFlag.bFlag = true;
+						SEND_ENTITY_MSG(setFlag);
 					}
-
-					StopActivity(self, player->GetObjectID(), 0, param1);
-					SaveScore(self, player->GetObjectID(), static_cast<float>(param1), static_cast<float>(param2), static_cast<float>(param3));
 				}
+
+				StopActivity(self, player->GetObjectID(), 0, param1);
+				SaveScore(self, player->GetObjectID(), static_cast<float>(param1), static_cast<float>(param2), static_cast<float>(param3));
 			}
 		}
 	}
diff --git a/dScripts/ai/FV/FvFreeGfNinjas.cpp b/dScripts/ai/FV/FvFreeGfNinjas.cpp
index d690a6f7..778f4ad2 100644
--- a/dScripts/ai/FV/FvFreeGfNinjas.cpp
+++ b/dScripts/ai/FV/FvFreeGfNinjas.cpp
@@ -1,7 +1,9 @@
 #include "FvFreeGfNinjas.h"
-#include "Character.h"
+
 #include "MissionComponent.h"
 #include "eMissionState.h"
+#include "Game.h"
+#include "EntityManager.h"
 
 void FvFreeGfNinjas::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, eMissionState missionState) {
 	if (missionID == 705 && missionState == eMissionState::AVAILABLE) {
@@ -14,13 +16,17 @@ void FvFreeGfNinjas::OnMissionDialogueOK(Entity* self, Entity* target, int missi
 		missionComponent->AcceptMission(703);
 		missionComponent->AcceptMission(704);
 
-		auto* character = target->GetCharacter();
-		if (character != nullptr)
-			character->SetPlayerFlag(68, true);
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = target->GetObjectID();
+		setFlag.iFlagId = 68;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 	} else if (missionID == 786) {
-		auto* character = target->GetCharacter();
-		if (character != nullptr)
-			character->SetPlayerFlag(81, true);
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = target->GetObjectID();
+		setFlag.iFlagId = 81;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 	}
 }
 
@@ -31,9 +37,11 @@ void FvFreeGfNinjas::OnUse(Entity* self, Entity* user) {
 		return;
 
 	if (missionComponent->GetMissionState(705) == eMissionState::ACTIVE) {
-		auto* character = user->GetCharacter();
-		if (character != nullptr)
-			character->SetPlayerFlag(68, true);
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = user->GetObjectID();
+		setFlag.iFlagId = 68;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 
 		missionComponent->AcceptMission(701, true);
 		missionComponent->AcceptMission(702, true);
diff --git a/dScripts/ai/FV/FvPandaServer.cpp b/dScripts/ai/FV/FvPandaServer.cpp
index f29f7f2e..8157ce17 100644
--- a/dScripts/ai/FV/FvPandaServer.cpp
+++ b/dScripts/ai/FV/FvPandaServer.cpp
@@ -1,6 +1,6 @@
 #include "FvPandaServer.h"
+
 #include "PetComponent.h"
-#include "Character.h"
 #include "ePetTamingNotifyType.h"
 
 void FvPandaServer::OnStartup(Entity* self) {
@@ -19,10 +19,11 @@ void FvPandaServer::OnNotifyPetTamingMinigame(Entity* self, Entity* tamer, ePetT
 	} else if (type == ePetTamingNotifyType::SUCCESS) {
 		// TODO: Remove from groups
 
-		auto* character = tamer->GetCharacter();
-		if (character != nullptr) {
-			character->SetPlayerFlag(82, true);
-		}
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = tamer->GetObjectID();
+		setFlag.iFlagId = 82;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 	}
 }
 
diff --git a/dScripts/ai/FV/FvPandaSpawnerServer.cpp b/dScripts/ai/FV/FvPandaSpawnerServer.cpp
index bc9f1c8a..31fe0dfa 100644
--- a/dScripts/ai/FV/FvPandaSpawnerServer.cpp
+++ b/dScripts/ai/FV/FvPandaSpawnerServer.cpp
@@ -1,13 +1,15 @@
 #include "FvPandaSpawnerServer.h"
-#include "Character.h"
+
 #include "EntityManager.h"
 #include "GameMessages.h"
 #include "EntityInfo.h"
 #include "ScriptedActivityComponent.h"
 
 void FvPandaSpawnerServer::OnCollisionPhantom(Entity* self, Entity* target) {
-	auto* character = target->GetCharacter();
-	if (character != nullptr && character->GetPlayerFlag(81)) {
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = target->GetObjectID();
+	getFlag.iFlagId = 81;
+	if (SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) {
 
 		auto raceObjects = Game::entityManager->GetEntitiesInGroup("PandaRaceObject");
 		if (raceObjects.empty())
diff --git a/dScripts/ai/GF/GfJailkeepMission.cpp b/dScripts/ai/GF/GfJailkeepMission.cpp
index b8d4cd30..e835d8f5 100644
--- a/dScripts/ai/GF/GfJailkeepMission.cpp
+++ b/dScripts/ai/GF/GfJailkeepMission.cpp
@@ -1,6 +1,6 @@
 #include "GfJailkeepMission.h"
+
 #include "MissionComponent.h"
-#include "Character.h"
 #include "eMissionState.h"
 
 void GfJailkeepMission::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, eMissionState missionState) {
@@ -14,8 +14,11 @@ void GfJailkeepMission::OnMissionDialogueOK(Entity* self, Entity* target, int mi
 		missionComponent->AcceptMission(388, true);
 		missionComponent->AcceptMission(390, true);
 	} else if (missionID == 385 && missionState == eMissionState::COMPLETE_READY_TO_COMPLETE) {
-		auto* character = target->GetCharacter();
-		if (character != nullptr && character->GetPlayerFlag(68)) {
+		GameMessages::GetFlag getFlag{};
+		getFlag.target = target->GetObjectID();
+		getFlag.iFlagId = 68;
+
+		if (SEND_ENTITY_MSG(getFlag) && getFlag.bFlag) {
 			missionComponent->AcceptMission(701);
 			missionComponent->AcceptMission(702);
 			missionComponent->AcceptMission(703);
diff --git a/dScripts/ai/GF/PirateRep.cpp b/dScripts/ai/GF/PirateRep.cpp
index 33d6ab63..8b0e26c7 100644
--- a/dScripts/ai/GF/PirateRep.cpp
+++ b/dScripts/ai/GF/PirateRep.cpp
@@ -1,14 +1,15 @@
 #include "PirateRep.h"
-#include "Character.h"
+
 #include "eMissionState.h"
 #include "Entity.h"
 #include "ePlayerFlag.h"
 
 void PirateRep::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, eMissionState missionState) {
 	if (missionID == m_PirateRepMissionID && missionState >= eMissionState::READY_TO_COMPLETE) {
-		auto* character = target->GetCharacter();
-		if (character) {
-			character->SetPlayerFlag(ePlayerFlag::GF_PIRATE_REP, true);
-		}
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = target->GetObjectID();
+		setFlag.iFlagId = ePlayerFlag::GF_PIRATE_REP;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 	}
 }
diff --git a/dScripts/ai/NS/NsGetFactionMissionServer.cpp b/dScripts/ai/NS/NsGetFactionMissionServer.cpp
index 185bd344..0b3f5ec1 100644
--- a/dScripts/ai/NS/NsGetFactionMissionServer.cpp
+++ b/dScripts/ai/NS/NsGetFactionMissionServer.cpp
@@ -1,7 +1,7 @@
 #include "NsGetFactionMissionServer.h"
+
 #include "GameMessages.h"
 #include "MissionComponent.h"
-#include "Character.h"
 #include "eReplicaComponentType.h"
 #include "ePlayerFlag.h"
 
@@ -42,11 +42,17 @@ void NsGetFactionMissionServer::OnRespondToMission(Entity* self, int missionID,
 		}
 
 		if (flagID != -1) {
-			player->GetCharacter()->SetPlayerFlag(ePlayerFlag::JOINED_A_FACTION, true);
-			player->GetCharacter()->SetPlayerFlag(flagID, true);
+			GameMessages::SetFlag setFlag{};
+			setFlag.target = player->GetObjectID();
+			setFlag.iFlagId = ePlayerFlag::JOINED_A_FACTION;
+			setFlag.bFlag = true;
+			SEND_ENTITY_MSG(setFlag);
+			setFlag.iFlagId = flagID;
+			setFlag.bFlag = true;
+			SEND_ENTITY_MSG(setFlag);
 		}
 
-		MissionComponent* mis = static_cast<MissionComponent*>(player->GetComponent(eReplicaComponentType::MISSION));
+		auto* mis = player->GetComponent<MissionComponent>();
 
 		for (int mission : factionMissions) {
 			mis->AcceptMission(mission);
diff --git a/dScripts/ai/PROPERTY/AG/AgPropGuard.cpp b/dScripts/ai/PROPERTY/AG/AgPropGuard.cpp
index e8e94b53..89e92fe2 100644
--- a/dScripts/ai/PROPERTY/AG/AgPropGuard.cpp
+++ b/dScripts/ai/PROPERTY/AG/AgPropGuard.cpp
@@ -1,6 +1,6 @@
 #include "AgPropGuard.h"
+
 #include "Entity.h"
-#include "Character.h"
 #include "EntityManager.h"
 #include "InventoryComponent.h"
 #include "MissionComponent.h"
@@ -8,13 +8,16 @@
 #include "eMissionState.h"
 
 void AgPropGuard::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, eMissionState missionState) {
-	auto* character = target->GetCharacter();
 	auto* missionComponent = target->GetComponent<MissionComponent>();
 	auto* inventoryComponent = target->GetComponent<InventoryComponent>();
+	if (!missionComponent || !inventoryComponent) return;
 
 	const auto state = missionComponent->GetMissionState(320);
 	if (missionID == 768 && missionState == eMissionState::AVAILABLE) {
-		if (!character->GetPlayerFlag(71)) {
+		GameMessages::GetFlag getFlag{};
+		getFlag.target = target->GetObjectID();
+		getFlag.iFlagId = 71;
+		if (SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
 			// TODO: Cinematic "MissionCam"
 		}
 	} else if (missionID == 768 && missionState >= eMissionState::READY_TO_COMPLETE) {
@@ -27,13 +30,12 @@ void AgPropGuard::OnMissionDialogueOK(Entity* self, Entity* target, int missionI
 				inventoryComponent->RemoveItem(id->GetLot(), id->GetCount());
 			}
 		}
-	} else if (
-		(missionID == 320 && state == eMissionState::AVAILABLE) /*||
-		(state == eMissionState::COMPLETE && missionID == 891 && missionState == eMissionState::READY_TO_COMPLETE)*/
-		) {
-		//GameMessages::SendNotifyClientObject(Game::entityManager->GetZoneControlEntity()->GetObjectID(), u"GuardChat", target->GetObjectID(), 0, target->GetObjectID(), "", target->GetSystemAddress());
-
-		target->GetCharacter()->SetPlayerFlag(113, true);
+	} else if (missionID == 320 && state == eMissionState::AVAILABLE) {
+		GameMessages::SetFlag setFlag{};
+		setFlag.target = target->GetObjectID();
+		setFlag.iFlagId = 113;
+		setFlag.bFlag = true;
+		SEND_ENTITY_MSG(setFlag);
 
 		Game::entityManager->GetZoneControlEntity()->AddTimer("GuardFlyAway", 1.0f);
 	}
diff --git a/dScripts/ai/PROPERTY/AgPropguards.cpp b/dScripts/ai/PROPERTY/AgPropguards.cpp
index ced7dd68..5aa978c3 100644
--- a/dScripts/ai/PROPERTY/AgPropguards.cpp
+++ b/dScripts/ai/PROPERTY/AgPropguards.cpp
@@ -1,21 +1,21 @@
 #include "AgPropguards.h"
-#include "Character.h"
+
 #include "GameMessages.h"
 #include "EntityManager.h"
 #include "dZoneManager.h"
 #include "eMissionState.h"
 
 void AgPropguards::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, eMissionState missionState) {
-	auto* character = target->GetCharacter();
-	if (character == nullptr)
-		return;
-
 	const auto flag = GetFlagForMission(missionID);
 	if (flag == 0)
 		return;
 
+	GameMessages::GetFlag getFlag{};
+	getFlag.target = target->GetObjectID();
+	getFlag.iFlagId = flag;
+
 	if ((missionState == eMissionState::AVAILABLE || missionState == eMissionState::ACTIVE)
-		&& !character->GetPlayerFlag(flag)) {
+		&& SEND_ENTITY_MSG(getFlag) && !getFlag.bFlag) {
 		// If the player just started the mission, play a cinematic highlighting the target
 		GameMessages::SendPlayCinematic(target->GetObjectID(), u"MissionCam", target->GetSystemAddress());
 	} else if (missionState == eMissionState::READY_TO_COMPLETE) {
diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp
index 0bf467de..2d4a58c8 100644
--- a/dWorldServer/WorldServer.cpp
+++ b/dWorldServer/WorldServer.cpp
@@ -1044,10 +1044,12 @@ void HandlePacket(Packet* packet) {
 				switch (version) {
 				case eCharacterVersion::RELEASE:
 					// TODO: Implement, super low priority
-				case eCharacterVersion::LIVE:
+				case eCharacterVersion::LIVE: {
 					LOG("Updating Character Flags");
-					c->SetRetroactiveFlags();
+					GameMessages::SetRetroactiveFlags flags{};
+					SEND_ENTITY_MSG(flags);
 					levelComponent->SetCharacterVersion(eCharacterVersion::PLAYER_FACTION_FLAGS);
+				}
 				case eCharacterVersion::PLAYER_FACTION_FLAGS:
 					LOG("Updating Vault Size");
 					player->RetroactiveVaultSize();