From ebb657153aa741a8482ad6242327f7c11b7c9619 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 19 Nov 2024 21:06:53 -0800 Subject: [PATCH] Zoning works but saving and player movement packets dont so it's a little limited, wear change works. Chat is a work in progress but not yet working. --- common/patches/larion.cpp | 121 ++++++++++++++++++++++++++++++++ common/patches/larion_ops.h | 5 ++ common/patches/larion_structs.h | 25 +++++++ utils/patches/patch_Larion.conf | 8 +-- zone/client.cpp | 27 ++++--- 5 files changed, 173 insertions(+), 13 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 4ed644b4d..9c5db909a 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2407,6 +2407,70 @@ namespace Larion FINISH_ENCODE(); } + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + eq->wear_slot_id = emu->wear_slot_id; + eq->armor_id = emu->material; + eq->variation = emu->unknown06; + eq->material = emu->elite_material; + eq->new_armor_id = emu->hero_forge_model; + eq->new_armor_type = emu->unknown18; + eq->color = emu->color.Color; + + FINISH_ENCODE(); + } + + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id + + std::string name; + in->ReadString(name); // NPC names max out at 63 chars + + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; + std::string new_message; + + in->ReadString(old_message); + + //ServerToRoF2SayLink(new_message, old_message); + + buf.WriteString(new_message); + + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_DeleteSpawn) + { + ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); + + OUT(spawn_id); + eq->unknown04 = 1; // Observed + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -2469,5 +2533,62 @@ namespace Larion FINISH_DIRECT_DECODE(); } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + emu->wear_slot_id = eq->wear_slot_id; + emu->material = eq->armor_id; + emu->unknown06 = eq->variation; + emu->elite_material = eq->material; + emu->hero_forge_model = eq->new_armor_id; + emu->unknown18 = eq->new_armor_type; + emu->color.Color = eq->color; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ChannelMessage) + { + unsigned char* __eq_buffer = __packet->pBuffer; + + char* InBuffer = (char*)__eq_buffer; + + char Sender[64]; + char Target[64]; + + VARSTRUCT_DECODE_STRING(Sender, InBuffer); + VARSTRUCT_DECODE_STRING(Target, InBuffer); + + //packet seems the same as rof2 with 5 more empty bytes before language + InBuffer += 9; + + uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + + InBuffer += 5; + + uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + + std::string old_message = InBuffer; + std::string new_message; + //RoF2ToServerSayLink(new_message, old_message); + + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + ChannelMessage_Struct* emu = (ChannelMessage_Struct*)__packet->pBuffer; + + strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); + strn0cpy(emu->sender, Target, sizeof(emu->sender)); + emu->language = Language; + emu->chan_num = Channel; + emu->skill_in_language = Skill; + strcpy(emu->message, new_message.c_str()); + + delete[] __eq_buffer; + } } /*Larion*/ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 7c801405e..1567272d8 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -19,11 +19,16 @@ E(OP_SendZonepoints) E(OP_RequestClientZoneChange) E(OP_ZoneChange) E(OP_ClientUpdate) +E(OP_WearChange) +E(OP_SpecialMesg) +E(OP_DeleteSpawn) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) D(OP_ZoneChange) D(OP_ClientUpdate) +D(OP_WearChange) +D(OP_ChannelMessage) #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 8b238062f..1afe6cb82 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -385,6 +385,31 @@ namespace Larion { /*176*/ }; + struct WearChange_Struct { + /*000*/ uint32 spawn_id; + /*004*/ uint32 wear_slot_id; + /*008*/ uint32 armor_id; + /*012*/ uint32 variation; + /*016*/ uint32 material; + /*020*/ uint32 new_armor_id; + /*024*/ uint32 new_armor_type; + /*028*/ uint32 color; + /*032*/ + }; + + struct ExpUpdate_Struct + { + /*000*/ uint64 exp; //This is exp % / 1000 now; eg 69250 = 69.25% + /*008*/ uint64 unknown; //unclear, I didn't see the client actually read this value but i might have missed it + }; + + struct DeleteSpawn_Struct + { + /*00*/ uint32 spawn_id; // Spawn ID to delete + /*04*/ uint8 unknown04; // Seen 1 + /*05*/ + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Larion.conf index d88718599..89e62fbab 100644 --- a/utils/patches/patch_Larion.conf +++ b/utils/patches/patch_Larion.conf @@ -105,11 +105,11 @@ OP_ExpUpdate=0x0000 #0x611d OP_HPUpdate=0x0000 #0x775c OP_ManaChange=0x0000 #0x0606 OP_TGB=0x0000 -OP_SpecialMesg=0x0000 #0x7d93 +OP_SpecialMesg=0x7d93 OP_GuildMemberList=0x0000 OP_GuildMOTD=0x0000 OP_CharInventory=0x21d6 -OP_WearChange=0x0000 #0x44c0 +OP_WearChange=0x44c0 OP_ClientUpdate=0x3a4b OP_ClientReady=0x0831 OP_SetServerFilter=0x0000 #0x6b7f @@ -218,7 +218,7 @@ OP_Bug=0x0000 OP_Feedback=0x0000 OP_Report=0x0000 OP_Damage=0x0000 -OP_ChannelMessage=0x0000 +OP_ChannelMessage=0x6adc OP_Assist=0x0000 OP_AssistGroup=0x0000 OP_MoveCoin=0x0000 @@ -249,7 +249,7 @@ OP_XTargetOpen=0x0000 OP_XTargetOpenResponse=0x0000 OP_BuffCreate=0x0000 OP_BuffRemoveRequest=0x0000 -OP_DeleteSpawn=0x0000 +OP_DeleteSpawn=0x7712 OP_AutoAttack=0x0000 OP_AutoAttack2=0x0000 OP_Consume=0x0000 diff --git a/zone/client.cpp b/zone/client.cpp index d0a8d97b0..268d37c8a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -534,20 +534,29 @@ void Client::SendZoneInPackets() if (GetLevel() >= 51) SendAlternateAdvancementStats(); - // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + auto tmpxp2 = GetEXPForLevel(GetLevel() + 1); + auto tmpxp1 = GetEXPForLevel(GetLevel()); + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); + + //Larion uses a more granular exp bar than other clients + if (m_ClientVersion >= EQ::versions::ClientVersion::Larion) { + eu->exp = (uint32)(100000.0f * tmpxp); + } + else { + eu->exp = (uint32)(330.0f * tmpxp); + } + + FastQueuePacket(&outapp); + } + else { + eu->exp = 0; + FastQueuePacket(&outapp); } - safe_delete(outapp); SendAlternateAdvancementTimers();