From 73a099c5ea07993a7e9b1c5d06e9a67221abc46f Mon Sep 17 00:00:00 2001
From: Alex King <89047260+Kinglykrab@users.noreply.github.com>
Date: Sat, 13 Jan 2024 01:03:36 -0500
Subject: [PATCH] [Character] Character EXP Modifiers in Memory (#3934)

* Cleanup

* Final push.

* Update zonedb.cpp

* Update zone.h

* Update exp.cpp

* Update zonedb.cpp

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
---
 .../base_character_exp_modifiers_repository.h |   1 +
 .../character_exp_modifiers_repository.h      |  54 +++++
 zone/client.cpp                               |  48 +++++
 zone/client.h                                 |  13 +-
 zone/client_packet.cpp                        |   1 +
 zone/exp.cpp                                  |   6 +-
 zone/lua_client.cpp                           |  56 +++--
 zone/lua_client.h                             |  20 +-
 zone/lua_general.cpp                          |  48 ++---
 zone/perl_client.cpp                          |  56 +++--
 zone/questmgr.cpp                             |  16 +-
 zone/questmgr.h                               |   8 +-
 zone/zone.cpp                                 |  70 ++++++
 zone/zone.h                                   |  14 ++
 zone/zonedb.cpp                               | 199 ++++++++++--------
 zone/zonedb.h                                 |  12 +-
 16 files changed, 443 insertions(+), 179 deletions(-)

diff --git a/common/repositories/base/base_character_exp_modifiers_repository.h b/common/repositories/base/base_character_exp_modifiers_repository.h
index 5a18cd9e39..eb10deb5b0 100644
--- a/common/repositories/base/base_character_exp_modifiers_repository.h
+++ b/common/repositories/base/base_character_exp_modifiers_repository.h
@@ -16,6 +16,7 @@
 #include "../../strings.h"
 #include <ctime>
 
+
 class BaseCharacterExpModifiersRepository {
 public:
 	struct CharacterExpModifiers {
diff --git a/common/repositories/character_exp_modifiers_repository.h b/common/repositories/character_exp_modifiers_repository.h
index d94fe79ff9..437806cc6b 100644
--- a/common/repositories/character_exp_modifiers_repository.h
+++ b/common/repositories/character_exp_modifiers_repository.h
@@ -44,7 +44,61 @@ class CharacterExpModifiersRepository: public BaseCharacterExpModifiersRepositor
      */
 
 	// Custom extended repository methods here
+	static EXPModifier GetEXPModifier(
+		Database& db,
+		uint32 character_id,
+		uint32 zone_id,
+		int16 instance_version
+	)
+	{
+		const auto& l = CharacterExpModifiersRepository::GetWhere(
+			db,
+			fmt::format(
+				SQL(
+					`character_id` = {} AND
+					(`zone_id` = {} OR `zone_id` = 0) AND
+					(`instance_version` = {} OR `instance_version` = -1)
+					ORDER BY `zone_id`, `instance_version` DESC
+					LIMIT 1
+				),
+				character_id,
+				zone_id,
+				instance_version
+			)
+		);
 
+		if (l.empty()) {
+			return EXPModifier{
+				.aa_modifier = 1.0f,
+				.exp_modifier = 1.0f
+			};
+		}
+
+		return EXPModifier{
+			.aa_modifier = l[0].aa_modifier,
+			.exp_modifier = l[0].exp_modifier
+		};
+	}
+
+	static void SetEXPModifier(
+		Database& db,
+		uint32 character_id,
+		uint32 zone_id,
+		int16 instance_version,
+		EXPModifier m
+	)
+	{
+		CharacterExpModifiersRepository::ReplaceOne(
+			db,
+			CharacterExpModifiersRepository::CharacterExpModifiers{
+				.character_id = static_cast<int32_t>(character_id),
+				.zone_id = static_cast<int32_t>(zone_id),
+				.instance_version = instance_version,
+				.aa_modifier = m.aa_modifier,
+				.exp_modifier = m.exp_modifier
+			}
+		);
+	}
 };
 
 #endif //EQEMU_CHARACTER_EXP_MODIFIERS_REPOSITORY_H
diff --git a/zone/client.cpp b/zone/client.cpp
index f8418f7677..61b65baa88 100644
--- a/zone/client.cpp
+++ b/zone/client.cpp
@@ -388,6 +388,10 @@ Client::~Client() {
 		Bot::ProcessBotOwnerRefDelete(this);
 	}
 
+	if (zone) {
+		zone->ClearEXPModifier(this);
+	}
+
 	if(IsInAGuild())
 		guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
 
@@ -742,6 +746,8 @@ bool Client::Save(uint8 iCommitNow) {
 
 	database.SaveCharacterData(this, &m_pp, &m_epp); /* Save Character Data */
 
+	database.SaveCharacterEXPModifier(this);
+
 	return true;
 }
 
@@ -11951,3 +11957,45 @@ void Client::ClearXTargets()
 		}
 	}
 }
+
+float Client::GetAAEXPModifier(uint32 zone_id, int16 instance_version)
+{
+	return database.GetAAEXPModifierByCharID(
+		CharacterID(),
+		zone_id,
+		instance_version
+	);
+}
+
+float Client::GetEXPModifier(uint32 zone_id, int16 instance_version)
+{
+	return database.GetEXPModifierByCharID(
+		CharacterID(),
+		zone_id,
+		instance_version
+	);
+}
+
+void Client::SetAAEXPModifier(uint32 zone_id, float aa_modifier, int16 instance_version)
+{
+	database.SetAAEXPModifierByCharID(
+		CharacterID(),
+		zone_id,
+		aa_modifier,
+		instance_version
+	);
+
+	database.LoadCharacterEXPModifier(this);
+}
+
+void Client::SetEXPModifier(uint32 zone_id, float exp_modifier, int16 instance_version)
+{
+	database.SetEXPModifierByCharID(
+		CharacterID(),
+		zone_id,
+		exp_modifier,
+		instance_version
+	);
+
+	database.LoadCharacterEXPModifier(this);
+}
diff --git a/zone/client.h b/zone/client.h
index f5416fecc7..921952c841 100644
--- a/zone/client.h
+++ b/zone/client.h
@@ -248,6 +248,9 @@ class Client : public Mob
 	bool IsEXPEnabled() const;
 	void SetEXPEnabled(bool is_exp_enabled);
 
+	std::vector<EXPModifier> GetEXPModifiers();
+	void SetEXPModifiers(std::vector<EXPModifier> exp_modifiers);
+
 	void SetPrimaryWeaponOrnamentation(uint32 model_id);
 	void SetSecondaryWeaponOrnamentation(uint32 model_id);
 
@@ -602,10 +605,10 @@ class Client : public Mob
 
 	inline uint32 GetEXP() const { return m_pp.exp; }
 
-	inline double GetAAEXPModifier(uint32 zone_id, int16 instance_version = -1) const { return database.GetAAEXPModifier(CharacterID(), zone_id, instance_version); };
-	inline double GetEXPModifier(uint32 zone_id, int16 instance_version = -1) const { return database.GetEXPModifier(CharacterID(), zone_id, instance_version); };
-	inline void SetAAEXPModifier(uint32 zone_id, double aa_modifier, int16 instance_version = -1) { database.SetAAEXPModifier(CharacterID(), zone_id, aa_modifier, instance_version); };
-	inline void SetEXPModifier(uint32 zone_id, double exp_modifier, int16 instance_version = -1) { database.SetEXPModifier(CharacterID(), zone_id, exp_modifier, instance_version); };
+	float GetAAEXPModifier(uint32 zone_id, int16 instance_version = -1);
+	float GetEXPModifier(uint32 zone_id, int16 instance_version = -1);
+	void SetAAEXPModifier(uint32 zone_id, float aa_modifier, int16 instance_version = -1);
+	void SetEXPModifier(uint32 zone_id, float exp_modifier, int16 instance_version = -1);
 
 	bool UpdateLDoNPoints(uint32 theme_id, int points);
 	void SetLDoNPoints(uint32 theme_id, uint32 points);
@@ -1953,6 +1956,8 @@ class Client : public Mob
 
 	bool m_exp_enabled;
 
+	std::vector<EXPModifier> m_exp_modifiers;
+
 	//Anti Spam Stuff
 	Timer *KarmaUpdateTimer;
 	uint32 TotalKarma;
diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp
index 54e4574d1f..da11daf514 100644
--- a/zone/client_packet.cpp
+++ b/zone/client_packet.cpp
@@ -1284,6 +1284,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
 	database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */
 	database.LoadCharacterLeadershipAbilities(cid, &m_pp); /* Load Character Leadership AA's */
 	database.LoadCharacterTribute(this); /* Load CharacterTribute */
+	database.LoadCharacterEXPModifier(this); /* Load Character EXP Modifier */
 
 	// this pattern is strange
 	// this is remnants of the old way of doing things
diff --git a/zone/exp.cpp b/zone/exp.cpp
index 1bf1664d1c..65d65e217f 100644
--- a/zone/exp.cpp
+++ b/zone/exp.cpp
@@ -183,7 +183,7 @@ uint64 Client::CalcEXP(uint8 consider_level, bool ignore_modifiers) {
 		}
 
 		if (RuleB(Character, EnableCharacterEXPMods)) {
-			in_add_exp *= GetEXPModifier(zone->GetZoneID(), zone->GetInstanceVersion());
+			in_add_exp *= zone->GetEXPModifier(this);
 		}
 	}
 
@@ -317,7 +317,7 @@ void Client::CalculateStandardAAExp(uint64 &add_aaxp, uint8 conlevel, bool resex
 	}
 
 	if (RuleB(Character, EnableCharacterEXPMods)) {
-		add_aaxp *= GetAAEXPModifier(zone->GetZoneID(), zone->GetInstanceVersion());
+		add_aaxp *= zone->GetAAEXPModifier(this);
 	}
 
 	add_aaxp = (uint64)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod);
@@ -480,7 +480,7 @@ void Client::CalculateExp(uint64 in_add_exp, uint64 &add_exp, uint64 &add_aaxp,
 	}
 
 	if (RuleB(Character, EnableCharacterEXPMods)) {
-		add_exp *= GetEXPModifier(zone->GetZoneID(), zone->GetInstanceVersion());
+		add_exp *= zone->GetEXPModifier(this);
 	}
 
 	//Enforce Percent XP Cap per kill, if rule is enabled
diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp
index b4445c62e1..f0bcbffc86 100644
--- a/zone/lua_client.cpp
+++ b/zone/lua_client.cpp
@@ -2164,42 +2164,62 @@ void Lua_Client::Fling(float value, float target_x, float target_y, float target
 	self->Fling(value, target_x, target_y, target_z, ignore_los, clip_through_walls);
 }
 
-double Lua_Client::GetAAEXPModifier(uint32 zone_id) {
+float Lua_Client::GetAAEXPModifier() {
+	Lua_Safe_Call_Real();
+	return zone->GetAAEXPModifier(self);
+}
+
+float Lua_Client::GetAAEXPModifier(uint32 zone_id) {
 	Lua_Safe_Call_Real();
 	return self->GetAAEXPModifier(zone_id);
 }
 
-double Lua_Client::GetAAEXPModifier(uint32 zone_id, int16 instance_version) {
+float Lua_Client::GetAAEXPModifier(uint32 zone_id, int16 instance_version) {
 	Lua_Safe_Call_Real();
 	return self->GetAAEXPModifier(zone_id, instance_version);
 }
 
-double Lua_Client::GetEXPModifier(uint32 zone_id) {
+float Lua_Client::GetEXPModifier() {
+	Lua_Safe_Call_Real();
+	return zone->GetEXPModifier(self);
+}
+
+float Lua_Client::GetEXPModifier(uint32 zone_id) {
 	Lua_Safe_Call_Real();
 	return self->GetEXPModifier(zone_id);
 }
 
-double Lua_Client::GetEXPModifier(uint32 zone_id, int16 instance_version) {
+float Lua_Client::GetEXPModifier(uint32 zone_id, int16 instance_version) {
 	Lua_Safe_Call_Real();
 	return self->GetEXPModifier(zone_id, instance_version);
 }
 
-void Lua_Client::SetAAEXPModifier(uint32 zone_id, double aa_modifier) {
+void Lua_Client::SetAAEXPModifier(float aa_modifier) {
+	Lua_Safe_Call_Void();
+	zone->SetAAEXPModifier(self, aa_modifier);
+}
+
+void Lua_Client::SetAAEXPModifier(uint32 zone_id, float aa_modifier) {
 	Lua_Safe_Call_Void();
 	self->SetAAEXPModifier(zone_id, aa_modifier);
 }
 
-void Lua_Client::SetAAEXPModifier(uint32 zone_id, double aa_modifier, int16 instance_version) {
+void Lua_Client::SetAAEXPModifier(uint32 zone_id, float aa_modifier, int16 instance_version) {
 	Lua_Safe_Call_Void();
 	self->SetAAEXPModifier(zone_id, aa_modifier, instance_version);
 }
 
-void Lua_Client::SetEXPModifier(uint32 zone_id, double exp_modifier) {
+void Lua_Client::SetEXPModifier(float exp_modifier) {
+	Lua_Safe_Call_Void();
+	zone->SetEXPModifier(self, exp_modifier);
+}
+
+void Lua_Client::SetEXPModifier(uint32 zone_id, float exp_modifier) {
 	Lua_Safe_Call_Void();
 	self->SetEXPModifier(zone_id, exp_modifier);
 }
 
-void Lua_Client::SetEXPModifier(uint32 zone_id, double exp_modifier, int16 instance_version) {
+void Lua_Client::SetEXPModifier(uint32 zone_id, float exp_modifier, int16 instance_version) {
 	Lua_Safe_Call_Void();
 	self->SetEXPModifier(zone_id, exp_modifier, instance_version);
 }
@@ -3377,8 +3397,9 @@ luabind::scope lua_register_client() {
 	.def("ForageItem", (void(Lua_Client::*)(bool))&Lua_Client::ForageItem)
 	.def("ForageItem", (void(Lua_Client::*)(void))&Lua_Client::ForageItem)
 	.def("Freeze", (void(Lua_Client::*)(void))&Lua_Client::Freeze)
-	.def("GetAAEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetAAEXPModifier)
-	.def("GetAAEXPModifier", (double(Lua_Client::*)(uint32,int16))&Lua_Client::GetAAEXPModifier)
+	.def("GetAAEXPModifier", (float(Lua_Client::*)(void))&Lua_Client::GetAAEXPModifier)
+	.def("GetAAEXPModifier", (float(Lua_Client::*)(uint32))&Lua_Client::GetAAEXPModifier)
+	.def("GetAAEXPModifier", (float(Lua_Client::*)(uint32,int16))&Lua_Client::GetAAEXPModifier)
 	.def("GetAAExp", (uint32(Lua_Client::*)(void))&Lua_Client::GetAAExp)
 	.def("GetAAPercent", (uint32(Lua_Client::*)(void))&Lua_Client::GetAAPercent)
 	.def("GetAAPoints", (int(Lua_Client::*)(void))&Lua_Client::GetAAPoints)
@@ -3435,8 +3456,9 @@ luabind::scope lua_register_client() {
 	.def("GetDuelTarget", (int(Lua_Client::*)(void))&Lua_Client::GetDuelTarget)
 	.def("GetEXP", (uint32(Lua_Client::*)(void))&Lua_Client::GetEXP)
 	.def("GetEXPForLevel", (uint32(Lua_Client::*)(uint16))&Lua_Client::GetEXPForLevel)
-	.def("GetEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetEXPModifier)
-	.def("GetEXPModifier", (double(Lua_Client::*)(uint32,int16))&Lua_Client::GetEXPModifier)
+	.def("GetEXPModifier", (float(Lua_Client::*)(void))&Lua_Client::GetEXPModifier)
+	.def("GetEXPModifier", (float(Lua_Client::*)(uint32))&Lua_Client::GetEXPModifier)
+	.def("GetEXPModifier", (float(Lua_Client::*)(uint32,int16))&Lua_Client::GetEXPModifier)
 	.def("GetEbonCrystals", (uint32(Lua_Client::*)(void))&Lua_Client::GetEbonCrystals)
 	.def("GetEndurance", (int(Lua_Client::*)(void))&Lua_Client::GetEndurance)
 	.def("GetEndurancePercent", (int(Lua_Client::*)(void))&Lua_Client::GetEndurancePercent)
@@ -3667,8 +3689,9 @@ luabind::scope lua_register_client() {
 	.def("SendPayload", (void(Lua_Client::*)(int,std::string))&Lua_Client::SendPayload)
 	.def("SendWebLink", (void(Lua_Client::*)(const char *))&Lua_Client::SendWebLink)
 	.def("SendZoneFlagInfo", (void(Lua_Client::*)(Lua_Client))&Lua_Client::SendZoneFlagInfo)
-	.def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetAAEXPModifier)
-	.def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,double,int16))&Lua_Client::SetAAEXPModifier)
+	.def("SetAAEXPModifier", (void(Lua_Client::*)(float))&Lua_Client::SetAAEXPModifier)
+	.def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,float))&Lua_Client::SetAAEXPModifier)
+	.def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,float,int16))&Lua_Client::SetAAEXPModifier)
 	.def("SetAAPoints", (void(Lua_Client::*)(int))&Lua_Client::SetAAPoints)
 	.def("SetAATitle", (void(Lua_Client::*)(std::string))&Lua_Client::SetAATitle)
 	.def("SetAATitle", (void(Lua_Client::*)(std::string,bool))&Lua_Client::SetAATitle)
@@ -3702,8 +3725,9 @@ luabind::scope lua_register_client() {
 	.def("SetEXP", (void(Lua_Client::*)(uint64,uint64))&Lua_Client::SetEXP)
 	.def("SetEXP", (void(Lua_Client::*)(uint64,uint64,bool))&Lua_Client::SetEXP)
 	.def("SetEXPEnabled", (void(Lua_Client::*)(bool))&Lua_Client::SetEXPEnabled)
-	.def("SetEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetEXPModifier)
-	.def("SetEXPModifier", (void(Lua_Client::*)(uint32,double,int16))&Lua_Client::SetEXPModifier)
+	.def("SetEXPModifier", (void(Lua_Client::*)(float))&Lua_Client::SetEXPModifier)
+	.def("SetEXPModifier", (void(Lua_Client::*)(uint32,float))&Lua_Client::SetEXPModifier)
+	.def("SetEXPModifier", (void(Lua_Client::*)(uint32,float,int16))&Lua_Client::SetEXPModifier)
 	.def("SetEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::SetEbonCrystals)
 	.def("SetEndurance", (void(Lua_Client::*)(int))&Lua_Client::SetEndurance)
 	.def("SetEnvironmentDamageModifier", (void(Lua_Client::*)(int))&Lua_Client::SetEnvironmentDamageModifier)
diff --git a/zone/lua_client.h b/zone/lua_client.h
index ff47ed478f..2696cea65c 100644
--- a/zone/lua_client.h
+++ b/zone/lua_client.h
@@ -71,14 +71,18 @@ class Lua_Client : public Lua_Mob
 	int GetBaseWIS();
 	int GetWeight();
 	uint32 GetEXP();
-	double GetEXPModifier(uint32 zone_id);
-	double GetEXPModifier(uint32 zone_id, int16 instance_version);
-	double GetAAEXPModifier(uint32 zone_id);
-	double GetAAEXPModifier(uint32 zone_id, int16 instance_version);
-	void SetAAEXPModifier(uint32 zone_id, double aa_modifier);
-	void SetAAEXPModifier(uint32 zone_id, double aa_modifier, int16 instance_version);
-	void SetEXPModifier(uint32 zone_id, double exp_modifier);
-	void SetEXPModifier(uint32 zone_id, double exp_modifier, int16 instance_version);
+	float GetEXPModifier();
+	float GetEXPModifier(uint32 zone_id);
+	float GetEXPModifier(uint32 zone_id, int16 instance_version);
+	float GetAAEXPModifier();
+	float GetAAEXPModifier(uint32 zone_id);
+	float GetAAEXPModifier(uint32 zone_id, int16 instance_version);
+	void SetAAEXPModifier(float aa_modifier);
+	void SetAAEXPModifier(uint32 zone_id, float aa_modifier);
+	void SetAAEXPModifier(uint32 zone_id, float aa_modifier, int16 instance_version);
+	void SetEXPModifier(float exp_modifier);
+	void SetEXPModifier(uint32 zone_id, float exp_modifier);
+	void SetEXPModifier(uint32 zone_id, float exp_modifier, int16 instance_version);
 	uint32 GetAAExp();
 	uint32 GetAAPercent();
 	uint32 GetTotalSecondsPlayed();
diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp
index fe9c923253..da3c9d4b9a 100644
--- a/zone/lua_general.cpp
+++ b/zone/lua_general.cpp
@@ -1946,36 +1946,36 @@ std::string lua_get_hex_color_code(std::string color_name) {
 	return quest_manager.gethexcolorcode(color_name);
 }
 
-double lua_get_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id) {
-	return database.GetAAEXPModifier(character_id, zone_id);
+float lua_get_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id) {
+	return quest_manager.GetAAEXPModifierByCharID(character_id, zone_id);
 }
 
-double lua_get_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, int16 instance_version) {
-	return database.GetAAEXPModifier(character_id, zone_id, instance_version);
+float lua_get_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, int16 instance_version) {
+	return quest_manager.GetAAEXPModifierByCharID(character_id, zone_id, instance_version);
 }
 
-double lua_get_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id) {
-	return database.GetEXPModifier(character_id, zone_id);
+float lua_get_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id) {
+	return quest_manager.GetEXPModifierByCharID(character_id, zone_id);
 }
 
-double lua_get_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, int16 instance_version) {
-	return database.GetEXPModifier(character_id, zone_id, instance_version);
+float lua_get_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, int16 instance_version) {
+	return quest_manager.GetEXPModifierByCharID(character_id, zone_id, instance_version);
 }
 
-void lua_set_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, double aa_modifier) {
-	database.SetAAEXPModifier(character_id, zone_id, aa_modifier);
+void lua_set_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, float aa_modifier) {
+	quest_manager.SetAAEXPModifierByCharID(character_id, zone_id, aa_modifier);
 }
 
-void lua_set_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, double aa_modifier, int16 instance_version) {
-	database.SetAAEXPModifier(character_id, zone_id, aa_modifier, instance_version);
+void lua_set_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, float aa_modifier, int16 instance_version) {
+	quest_manager.SetAAEXPModifierByCharID(character_id, zone_id, aa_modifier, instance_version);
 }
 
-void lua_set_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, double exp_modifier) {
-	database.SetEXPModifier(character_id, zone_id, exp_modifier);
+void lua_set_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, float exp_modifier) {
+	quest_manager.SetEXPModifierByCharID(character_id, zone_id, exp_modifier);
 }
 
-void lua_set_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, double exp_modifier, int16 instance_version) {
-	database.SetEXPModifier(character_id, zone_id, exp_modifier, instance_version);
+void lua_set_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, float exp_modifier, int16 instance_version) {
+	quest_manager.SetEXPModifierByCharID(character_id, zone_id, exp_modifier, instance_version);
 }
 
 void lua_add_ldon_loss(uint32 theme_id) {
@@ -5834,14 +5834,14 @@ luabind::scope lua_register_general() {
 		luabind::def("seconds_to_time", &lua_seconds_to_time),
 		luabind::def("time_to_seconds", &lua_time_to_seconds),
 		luabind::def("get_hex_color_code", &lua_get_hex_color_code),
-		luabind::def("get_aa_exp_modifier_by_char_id", (double(*)(uint32,uint32))&lua_get_aa_exp_modifier_by_char_id),
-		luabind::def("get_aa_exp_modifier_by_char_id", (double(*)(uint32,uint32,int16))&lua_get_aa_exp_modifier_by_char_id),
-		luabind::def("get_exp_modifier_by_char_id", (double(*)(uint32,uint32))&lua_get_exp_modifier_by_char_id),
-		luabind::def("get_exp_modifier_by_char_id", (double(*)(uint32,uint32,int16))&lua_get_exp_modifier_by_char_id),
-		luabind::def("set_aa_exp_modifier_by_char_id", (void(*)(uint32,uint32,double))&lua_set_aa_exp_modifier_by_char_id),
-		luabind::def("set_aa_exp_modifier_by_char_id", (void(*)(uint32,uint32,double,int16))&lua_set_aa_exp_modifier_by_char_id),
-		luabind::def("set_exp_modifier_by_char_id", (void(*)(uint32,uint32,double))&lua_set_exp_modifier_by_char_id),
-		luabind::def("set_exp_modifier_by_char_id", (void(*)(uint32,uint32,double,int16))&lua_set_exp_modifier_by_char_id),
+		luabind::def("get_aa_exp_modifier_by_char_id", (float(*)(uint32,uint32))&lua_get_aa_exp_modifier_by_char_id),
+		luabind::def("get_aa_exp_modifier_by_char_id", (float(*)(uint32,uint32,int16))&lua_get_aa_exp_modifier_by_char_id),
+		luabind::def("get_exp_modifier_by_char_id", (float(*)(uint32,uint32))&lua_get_exp_modifier_by_char_id),
+		luabind::def("get_exp_modifier_by_char_id", (float(*)(uint32,uint32,int16))&lua_get_exp_modifier_by_char_id),
+		luabind::def("set_aa_exp_modifier_by_char_id", (void(*)(uint32,uint32,float))&lua_set_aa_exp_modifier_by_char_id),
+		luabind::def("set_aa_exp_modifier_by_char_id", (void(*)(uint32,uint32,float,int16))&lua_set_aa_exp_modifier_by_char_id),
+		luabind::def("set_exp_modifier_by_char_id", (void(*)(uint32,uint32,float))&lua_set_exp_modifier_by_char_id),
+		luabind::def("set_exp_modifier_by_char_id", (void(*)(uint32,uint32,float,int16))&lua_set_exp_modifier_by_char_id),
 		luabind::def("add_ldon_loss", &lua_add_ldon_loss),
 		luabind::def("add_ldon_points", &lua_add_ldon_points),
 		luabind::def("add_ldon_win", &lua_add_ldon_win),
diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp
index bc270b59e4..38629295d7 100644
--- a/zone/perl_client.cpp
+++ b/zone/perl_client.cpp
@@ -2121,42 +2121,62 @@ EQ::InventoryProfile* Perl_Client_GetInventory(Client* self)
 	return &self->GetInv();
 }
 
-double Perl_Client_GetAAEXPModifier(Client* self, uint32 zone_id)
+float Perl_Client_GetAAEXPModifier(Client* self)
+{
+	return zone->GetAAEXPModifier(self);
+}
+
+float Perl_Client_GetAAEXPModifier(Client* self, uint32 zone_id)
 {
 	return self->GetAAEXPModifier(zone_id);
 }
 
-double Perl_Client_GetAAEXPModifier(Client* self, uint32 zone_id, int16 instance_version)
+float Perl_Client_GetAAEXPModifier(Client* self, uint32 zone_id, int16 instance_version)
 {
 	return self->GetAAEXPModifier(zone_id, instance_version);
 }
 
-double Perl_Client_GetEXPModifier(Client* self, uint32 zone_id)
+float Perl_Client_GetEXPModifier(Client* self)
+{
+	return zone->GetEXPModifier(self);
+}
+
+float Perl_Client_GetEXPModifier(Client* self, uint32 zone_id)
 {
 	return self->GetEXPModifier(zone_id);
 }
 
-double Perl_Client_GetEXPModifier(Client* self, uint32 zone_id, int16 instance_version)
+float Perl_Client_GetEXPModifier(Client* self, uint32 zone_id, int16 instance_version)
 {
 	return self->GetEXPModifier(zone_id, instance_version);
 }
 
-void Perl_Client_SetAAEXPModifier(Client* self, uint32 zone_id, double aa_modifier)
+void Perl_Client_SetAAEXPModifier(Client* self, float aa_modifier)
+{
+	zone->SetAAEXPModifier(self, aa_modifier);
+}
+
+void Perl_Client_SetAAEXPModifier(Client* self, uint32 zone_id, float aa_modifier)
 {
 	self->SetAAEXPModifier(zone_id, aa_modifier);
 }
 
-void Perl_Client_SetAAEXPModifier(Client* self, uint32 zone_id, double aa_modifier, int16 instance_version)
+void Perl_Client_SetAAEXPModifier(Client* self, uint32 zone_id, float aa_modifier, int16 instance_version)
 {
 	self->SetAAEXPModifier(zone_id, aa_modifier, instance_version);
 }
 
-void Perl_Client_SetEXPModifier(Client* self, uint32 zone_id, double exp_modifier)
+void Perl_Client_SetEXPModifier(Client* self, float exp_modifier)
+{
+	zone->SetEXPModifier(self, exp_modifier);
+}
+
+void Perl_Client_SetEXPModifier(Client* self, uint32 zone_id, float exp_modifier)
 {
 	self->SetEXPModifier(zone_id, exp_modifier);
 }
 
-void Perl_Client_SetEXPModifier(Client* self, uint32 zone_id, double exp_modifier, int16 instance_version)
+void Perl_Client_SetEXPModifier(Client* self, uint32 zone_id, float exp_modifier, int16 instance_version)
 {
 	self->SetEXPModifier(zone_id, exp_modifier, instance_version);
 }
@@ -3181,8 +3201,9 @@ void perl_register_client()
 	package.add("ForageItem", &Perl_Client_ForageItem);
 	package.add("Freeze", &Perl_Client_Freeze);
 	package.add("GMKill", &Perl_Client_GMKill);
-	package.add("GetAAEXPModifier", (double(*)(Client*, uint32))&Perl_Client_GetAAEXPModifier);
-	package.add("GetAAEXPModifier", (double(*)(Client*, uint32, int16))&Perl_Client_GetAAEXPModifier);
+	package.add("GetAAEXPModifier", (float(*)(Client*))&Perl_Client_GetAAEXPModifier);
+	package.add("GetAAEXPModifier", (float(*)(Client*, uint32))&Perl_Client_GetAAEXPModifier);
+	package.add("GetAAEXPModifier", (float(*)(Client*, uint32, int16))&Perl_Client_GetAAEXPModifier);
 	package.add("GetAAExp", &Perl_Client_GetAAExp);
 	package.add("GetAALevel", &Perl_Client_GetAALevel);
 	package.add("GetAAPercent", &Perl_Client_GetAAPercent);
@@ -3241,8 +3262,9 @@ void perl_register_client()
 	package.add("GetEnvironmentDamageModifier", &Perl_Client_GetEnvironmentDamageModifier);
 	package.add("GetEXP", &Perl_Client_GetEXP);
 	package.add("GetEXPForLevel", &Perl_Client_GetEXPForLevel);
-	package.add("GetEXPModifier", (double(*)(Client*, uint32))&Perl_Client_GetEXPModifier);
-	package.add("GetEXPModifier", (double(*)(Client*, uint32, int16))&Perl_Client_GetEXPModifier);
+	package.add("GetEXPModifier", (float(*)(Client*))&Perl_Client_GetEXPModifier);
+	package.add("GetEXPModifier", (float(*)(Client*, uint32))&Perl_Client_GetEXPModifier);
+	package.add("GetEXPModifier", (float(*)(Client*, uint32, int16))&Perl_Client_GetEXPModifier);
 	package.add("GetEbonCrystals", &Perl_Client_GetEbonCrystals);
 	package.add("GetEndurance", &Perl_Client_GetEndurance);
 	package.add("GetEnduranceRatio", &Perl_Client_GetEnduranceRatio);
@@ -3472,8 +3494,9 @@ void perl_register_client()
 	package.add("SendToInstance", &Perl_Client_SendToInstance);
 	package.add("SendWebLink", &Perl_Client_SendWebLink);
 	package.add("SendZoneFlagInfo", &Perl_Client_SendZoneFlagInfo);
-	package.add("SetAAEXPModifier", (void(*)(Client*, uint32, double))&Perl_Client_SetAAEXPModifier);
-	package.add("SetAAEXPModifier", (void(*)(Client*, uint32, double, int16))&Perl_Client_SetAAEXPModifier);
+	package.add("SetAAEXPModifier", (void(*)(Client*, float))&Perl_Client_SetAAEXPModifier);
+	package.add("SetAAEXPModifier", (void(*)(Client*, uint32, float))&Perl_Client_SetAAEXPModifier);
+	package.add("SetAAEXPModifier", (void(*)(Client*, uint32, float, int16))&Perl_Client_SetAAEXPModifier);
 	package.add("SetAAPoints", &Perl_Client_SetAAPoints);
 	package.add("SetAATitle", (void(*)(Client*, std::string))&Perl_Client_SetAATitle);
 	package.add("SetAATitle", (void(*)(Client*, std::string, bool))&Perl_Client_SetAATitle);
@@ -3507,8 +3530,9 @@ void perl_register_client()
 	package.add("SetDueling", &Perl_Client_SetDueling);
 	package.add("SetEXP", (void(*)(Client*, uint64, uint64))&Perl_Client_SetEXP);
 	package.add("SetEXP", (void(*)(Client*, uint64, uint64, bool))&Perl_Client_SetEXP);
-	package.add("SetEXPModifier", (void(*)(Client*, uint32, double))&Perl_Client_SetEXPModifier);
-	package.add("SetEXPModifier", (void(*)(Client*, uint32, double, int16))&Perl_Client_SetEXPModifier);
+	package.add("SetEXPModifier", (void(*)(Client*, float))&Perl_Client_SetEXPModifier);
+	package.add("SetEXPModifier", (void(*)(Client*, uint32, float))&Perl_Client_SetEXPModifier);
+	package.add("SetEXPModifier", (void(*)(Client*, uint32, float, int16))&Perl_Client_SetEXPModifier);
 	package.add("SetEbonCrystals", &Perl_Client_SetEbonCrystals);
 	package.add("SetEndurance", &Perl_Client_SetEndurance);
 	package.add("SetEnvironmentDamageModifier", &Perl_Client_SetEnvironmentDamageModifier);
diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp
index 24c3a728eb..b26ead772b 100644
--- a/zone/questmgr.cpp
+++ b/zone/questmgr.cpp
@@ -3910,20 +3910,20 @@ std::string QuestManager::gethexcolorcode(std::string color_name) {
 	return std::string();
 }
 
-double QuestManager::GetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version) const {
-	return database.GetAAEXPModifier(character_id, zone_id, instance_version);
+float QuestManager::GetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version) const {
+	return database.GetAAEXPModifierByCharID(character_id, zone_id, instance_version);
 }
 
-double QuestManager::GetEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version) const {
-	return database.GetEXPModifier(character_id, zone_id, instance_version);
+float QuestManager::GetEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version) const {
+	return database.GetEXPModifierByCharID(character_id, zone_id, instance_version);
 }
 
-void QuestManager::SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, double aa_modifier, int16 instance_version) {
-	database.SetAAEXPModifier(character_id, zone_id, aa_modifier, instance_version);
+void QuestManager::SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, float aa_modifier, int16 instance_version) {
+	database.SetAAEXPModifierByCharID(character_id, zone_id, aa_modifier, instance_version);
 }
 
-void QuestManager::SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, double exp_modifier, int16 instance_version) {
-	database.SetEXPModifier(character_id, zone_id, exp_modifier, instance_version);
+void QuestManager::SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, float exp_modifier, int16 instance_version) {
+	database.SetEXPModifierByCharID(character_id, zone_id, exp_modifier, instance_version);
 }
 
 std::string QuestManager::getgendername(uint32 gender_id) {
diff --git a/zone/questmgr.h b/zone/questmgr.h
index c16154e4ae..ff06783bd4 100644
--- a/zone/questmgr.h
+++ b/zone/questmgr.h
@@ -333,10 +333,10 @@ class QuestManager {
 	void ClearNPCTypeCache(int npctype_id);
 	void ReloadZoneStaticData();
 	std::string gethexcolorcode(std::string color_name);
-	double GetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version = -1) const;
-	double GetEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version = -1) const;
-	void SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, double aa_modifier, int16 instance_version = -1);
-	void SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, double exp_modifier, int16 instance_version = -1);
+	float GetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version = -1) const;
+	float GetEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version = -1) const;
+	void SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, float aa_modifier, int16 instance_version = -1);
+	void SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, float exp_modifier, int16 instance_version = -1);
 	std::string getgendername(uint32 gender_id);
 	std::string getdeityname(uint32 deity_id);
 	std::string getinventoryslotname(int16 slot_id);
diff --git a/zone/zone.cpp b/zone/zone.cpp
index 28a8a04350..3e206b909c 100644
--- a/zone/zone.cpp
+++ b/zone/zone.cpp
@@ -55,6 +55,7 @@
 #include "../common/data_verification.h"
 #include "zone_reload.h"
 #include "../common/repositories/criteria/content_filter_criteria.h"
+#include "../common/repositories/character_exp_modifiers_repository.h"
 #include "../common/repositories/merchantlist_repository.h"
 #include "../common/repositories/object_repository.h"
 #include "../common/repositories/rule_sets_repository.h"
@@ -3160,3 +3161,72 @@ void Zone::ReloadContentFlags()
 
 	safe_delete(pack);
 }
+
+void Zone::ClearEXPModifier(Client* c)
+{
+	exp_modifiers.erase(c->CharacterID());
+}
+
+float Zone::GetAAEXPModifier(Client* c)
+{
+	const auto& l = exp_modifiers.find(c->CharacterID());
+	if (l == exp_modifiers.end()) {
+		return 1.0f;
+	}
+
+	const auto& v = l->second;
+
+	return v.aa_modifier;
+}
+
+float Zone::GetEXPModifier(Client* c)
+{
+	const auto& l = exp_modifiers.find(c->CharacterID());
+	if (l == exp_modifiers.end()) {
+		return 1.0f;
+	}
+
+	const auto& v = l->second;
+
+	return v.exp_modifier;
+}
+
+void Zone::SetAAEXPModifier(Client* c, float aa_modifier)
+{
+	auto l = exp_modifiers.find(c->CharacterID());
+	if (l == exp_modifiers.end()) {
+		return;
+	}
+
+	auto& m = l->second;
+
+	m.aa_modifier = aa_modifier;
+
+	CharacterExpModifiersRepository::SetEXPModifier(
+		database,
+		c->CharacterID(),
+		GetZoneID(),
+		GetInstanceVersion(),
+		m
+	);
+}
+
+void Zone::SetEXPModifier(Client* c, float exp_modifier)
+{
+	auto l = exp_modifiers.find(c->CharacterID());
+	if (l == exp_modifiers.end()) {
+		return;
+	}
+
+	auto& m = l->second;
+
+	m.exp_modifier = exp_modifier;
+
+	CharacterExpModifiersRepository::SetEXPModifier(
+		database,
+		c->CharacterID(),
+		GetZoneID(),
+		GetInstanceVersion(),
+		m
+	);
+}
diff --git a/zone/zone.h b/zone/zone.h
index 3e7cd4f1d9..51af2a1a2e 100755
--- a/zone/zone.h
+++ b/zone/zone.h
@@ -39,6 +39,12 @@
 #include "../common/discord/discord.h"
 #include "../common/repositories/dynamic_zone_templates_repository.h"
 
+struct EXPModifier
+{
+	float aa_modifier;
+	float exp_modifier;
+};
+
 class DynamicZone;
 
 struct ZonePoint {
@@ -221,6 +227,8 @@ class Zone {
 	std::unordered_map<uint32, std::unique_ptr<Expedition>>  expedition_cache;
 	std::unordered_map<uint32, DynamicZoneTemplatesRepository::DynamicZoneTemplates> dz_template_cache;
 
+	std::unordered_map<uint32, EXPModifier> exp_modifiers;
+
 	time_t weather_timer;
 	Timer  spawn2_timer;
 	Timer  hot_reload_timer;
@@ -248,6 +256,12 @@ class Zone {
 	std::string GetZoneDescription();
 	void SendReloadMessage(std::string reload_type);
 
+	void ClearEXPModifier(Client* c);
+	float GetAAEXPModifier(Client* c);
+	float GetEXPModifier(Client* c);
+	void SetAAEXPModifier(Client* c, float aa_modifier);
+	void SetEXPModifier(Client* c, float exp_modifier);
+
 	void AddAggroMob() { aggroedmobs++; }
 	void AddAuth(ServerZoneIncomingClient_Struct *szic);
 	void ChangeWeather();
diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp
index 1b05c28c21..8e91c18c21 100755
--- a/zone/zonedb.cpp
+++ b/zone/zonedb.cpp
@@ -45,6 +45,7 @@
 #include "../common/repositories/merc_subtypes_repository.h"
 #include "../common/repositories/npc_types_tint_repository.h"
 #include "../common/repositories/merchantlist_temp_repository.h"
+#include "../common/repositories/character_exp_modifiers_repository.h"
 #include "../common/repositories/character_data_repository.h"
 #include "../common/repositories/character_corpses_repository.h"
 #include "../common/repositories/character_corpse_items_repository.h"
@@ -4233,98 +4234,6 @@ bool ZoneDatabase::DeleteCharacterCorpse(uint32 corpse_id)
 	return CharacterCorpsesRepository::DeleteOne(*this, corpse_id);
 }
 
-double ZoneDatabase::GetAAEXPModifier(uint32 character_id, uint32 zone_id, int16 instance_version) const {
-	const std::string query = fmt::format(
-		SQL(
-			SELECT
-			`aa_modifier`
-			FROM
-			`character_exp_modifiers`
-			WHERE
-			`character_id` = {}
-			AND
-			(`zone_id` = {} OR `zone_id` = 0) AND
-			(`instance_version` = {} OR `instance_version` = -1)
-			ORDER BY `zone_id`, `instance_version` DESC
-			LIMIT 1
-		),
-		character_id,
-		zone_id,
-		instance_version
-	);
-
-	auto results = database.QueryDatabase(query);
-	for (auto& row = results.begin(); row != results.end(); ++row) {
-		return Strings::ToFloat(row[0]);
-	}
-
-	return 1.0f;
-}
-
-double ZoneDatabase::GetEXPModifier(uint32 character_id, uint32 zone_id, int16 instance_version) const {
-	const std::string query = fmt::format(
-		SQL(
-			SELECT
-			`exp_modifier`
-			FROM
-			`character_exp_modifiers`
-			WHERE
-			`character_id` = {}
-			AND
-			(`zone_id` = {} OR `zone_id` = 0) AND
-			(`instance_version` = {} OR `instance_version` = -1)
-			ORDER BY `zone_id`, `instance_version` DESC
-			LIMIT 1
-		),
-		character_id,
-		zone_id,
-		instance_version
-	);
-
-	auto results = database.QueryDatabase(query);
-	for (auto& row = results.begin(); row != results.end(); ++row) {
-		return Strings::ToFloat(row[0]);
-	}
-
-	return 1.0f;
-}
-
-void ZoneDatabase::SetAAEXPModifier(uint32 character_id, uint32 zone_id, double aa_modifier, int16 instance_version) {
-	float exp_modifier = GetEXPModifier(character_id, zone_id, instance_version);
-	std::string query = fmt::format(
-		SQL(
-			REPLACE INTO
-			`character_exp_modifiers`
-			VALUES
-			({}, {}, {}, {}, {})
-		),
-		character_id,
-		zone_id,
-		instance_version,
-		aa_modifier,
-		exp_modifier
-	);
-	database.QueryDatabase(query);
-}
-
-void ZoneDatabase::SetEXPModifier(uint32 character_id, uint32 zone_id, double exp_modifier, int16 instance_version) {
-	float aa_modifier = GetAAEXPModifier(character_id, zone_id, instance_version);
-	std::string query = fmt::format(
-		SQL(
-			REPLACE INTO
-			`character_exp_modifiers`
-			VALUES
-			({}, {}, {}, {}, {})
-		),
-		character_id,
-		zone_id,
-		instance_version,
-		aa_modifier,
-		exp_modifier
-	);
-	database.QueryDatabase(query);
-}
-
 void ZoneDatabase::UpdateGMStatus(uint32 account_id, int new_status)
 {
 	auto e = AccountRepository::FindOne(*this, account_id);
@@ -4426,3 +4335,109 @@ void ZoneDatabase::ZeroPlayerProfileCurrency(PlayerProfile_Struct* pp)
 		pp->copper_cursor = 0;
 	}
 }
+
+float ZoneDatabase::GetAAEXPModifierByCharID(
+	uint32 character_id,
+	uint32 zone_id,
+	int16 instance_version
+)
+{
+	EXPModifier m = CharacterExpModifiersRepository::GetEXPModifier(
+		database,
+		character_id,
+		zone_id,
+		instance_version
+	);
+
+	return m.aa_modifier;
+}
+
+float ZoneDatabase::GetEXPModifierByCharID(
+	uint32 character_id,
+	uint32 zone_id,
+	int16 instance_version
+)
+{
+	EXPModifier m = CharacterExpModifiersRepository::GetEXPModifier(
+		database,
+		character_id,
+		zone_id,
+		instance_version
+	);
+
+	return m.exp_modifier;
+}
+
+void ZoneDatabase::SetAAEXPModifierByCharID(
+	uint32 character_id,
+	uint32 zone_id,
+	float aa_modifier,
+	int16 instance_version
+)
+{
+	CharacterExpModifiersRepository::SetEXPModifier(
+		database,
+		character_id,
+		zone_id,
+		instance_version,
+		EXPModifier{
+			.aa_modifier = aa_modifier,
+			.exp_modifier = -1.0f
+		}
+	);
+}
+
+void ZoneDatabase::SetEXPModifierByCharID(
+	uint32 character_id,
+	uint32 zone_id,
+	float exp_modifier,
+	int16 instance_version
+)
+{
+	CharacterExpModifiersRepository::SetEXPModifier(
+		database,
+		character_id,
+		zone_id,
+		instance_version,
+		EXPModifier{
+			.aa_modifier = -1.0f,
+			.exp_modifier = exp_modifier
+		}
+	);
+}
+
+void ZoneDatabase::LoadCharacterEXPModifier(Client* c)
+{
+	if (!zone) {
+		return;
+	}
+
+	EXPModifier m = CharacterExpModifiersRepository::GetEXPModifier(
+		*this,
+		c->CharacterID(),
+		zone->GetZoneID(),
+		zone->GetInstanceVersion()
+	);
+
+	zone->exp_modifiers[c->CharacterID()] = m;
+}
+
+void ZoneDatabase::SaveCharacterEXPModifier(Client* c)
+{
+	if (!zone) {
+		return;
+	}
+
+	EXPModifier m = zone->exp_modifiers[c->CharacterID()];
+
+	CharacterExpModifiersRepository::ReplaceOne(
+		*this,
+		CharacterExpModifiersRepository::CharacterExpModifiers{
+			.character_id = static_cast<int32_t>(c->CharacterID()),
+			.zone_id = static_cast<int32_t>(zone->GetZoneID()),
+			.instance_version = zone->GetInstanceVersion(),
+			.aa_modifier = m.aa_modifier,
+			.exp_modifier = m.exp_modifier
+		}
+	);
+}
diff --git a/zone/zonedb.h b/zone/zonedb.h
index 562a58671b..449990150f 100644
--- a/zone/zonedb.h
+++ b/zone/zonedb.h
@@ -458,10 +458,14 @@ class ZoneDatabase : public SharedDatabase {
 
 	void ZeroPlayerProfileCurrency(PlayerProfile_Struct* pp);
 
-	double GetAAEXPModifier(uint32 character_id, uint32 zone_id, int16 instance_version = -1) const;
-	double GetEXPModifier(uint32 character_id, uint32 zone_id, int16 instance_version = -1) const;
-	void SetAAEXPModifier(uint32 character_id, uint32 zone_id, double aa_modifier, int16 instance_version = -1);
-	void SetEXPModifier(uint32 character_id, uint32 zone_id, double exp_modifier, int16 instance_version = -1);
+	/* EXP Modifiers */
+	void LoadCharacterEXPModifier(Client* c);
+	void SaveCharacterEXPModifier(Client *c);
+
+	float GetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version = -1);
+	float GetEXPModifierByCharID(uint32 character_id, uint32 zone_id, int16 instance_version = -1);
+	void SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, float aa_modifier, int16 instance_version = -1);
+	void SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, float exp_modifier, int16 instance_version = -1);
 
 	/* Character Inventory  */
 	bool	NoRentExpired(const std::string& name);