mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
* Update Raid Functions Updated various raid features for: Titanium - Raid window now functional, including with BOTS - Raid delegate assist/mark work - Raid notes work - Raid /rmark, /clearmarks work Underfoot - Raid window was already functional - Raid delegate assist/mark work - Raid notes work - Raid /rmark, /clearmarks work * Updates to resolve feedback * Slight update for overlooked case in encode for RaidUpdate for clients above Ti. * Updates to further address feedback. Only updated translators for Ti/RoF2. Once ok, I will update the others. * Update linux-build.sh * Final updates for other translators and the strncpy_s issue. * Fix for strn0cpy in raids.cpp, translators, and defines. Updated all in raids.cpp as well. * Reveted defines change. * Reverted accidental change --------- Co-authored-by: Akkadius <akkadius1@gmail.com>
3661 lines
98 KiB
C++
3661 lines
98 KiB
C++
/* EQEMu: Everquest Server Emulator
|
|
|
|
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
|
are required to give you total support for your newly bought product;
|
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "../global_define.h"
|
|
#include "../eqemu_config.h"
|
|
#include "../eqemu_logsys.h"
|
|
#include "sof.h"
|
|
#include "../opcodemgr.h"
|
|
|
|
#include "../eq_stream_ident.h"
|
|
#include "../crc32.h"
|
|
|
|
#include "../eq_packet_structs.h"
|
|
#include "../misc_functions.h"
|
|
#include "../strings.h"
|
|
#include "../item_instance.h"
|
|
#include "sof_structs.h"
|
|
#include "../rulesys.h"
|
|
#include "../path_manager.h"
|
|
#include "../raid.h"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
|
|
namespace SoF
|
|
{
|
|
static const char *name = "SoF";
|
|
static OpcodeManager *opcodes = nullptr;
|
|
static Strategy struct_strategy;
|
|
|
|
void SerializeItem(EQ::OutBuffer& ob, const EQ::ItemInstance *inst, int16 slot_id, uint8 depth);
|
|
|
|
// server to client inventory location converters
|
|
static inline uint32 ServerToSoFSlot(uint32 server_slot);
|
|
static inline uint32 ServerToSoFCorpseSlot(uint32 server_corpse_slot);
|
|
|
|
// client to server inventory location converters
|
|
static inline uint32 SoFToServerSlot(uint32 sof_slot);
|
|
static inline uint32 SoFToServerCorpseSlot(uint32 sof_corpse_slot);
|
|
|
|
// server to client say link converter
|
|
static inline void ServerToSoFSayLink(std::string &sof_saylink, const std::string &server_saylink);
|
|
|
|
// client to server say link converter
|
|
static inline void SoFToServerSayLink(std::string &server_saylink, const std::string &sof_saylink);
|
|
|
|
static inline spells::CastingSlot ServerToSoFCastingSlot(EQ::spells::CastingSlot slot);
|
|
static inline EQ::spells::CastingSlot SoFToServerCastingSlot(spells::CastingSlot slot, uint32 item_location);
|
|
|
|
static inline int ServerToSoFBuffSlot(int index);
|
|
static inline int SoFToServerBuffSlot(int index);
|
|
|
|
void Register(EQStreamIdentifier &into)
|
|
{
|
|
//create our opcode manager if we havent already
|
|
if (opcodes == nullptr) {
|
|
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
|
//load up the opcode manager.
|
|
//TODO: figure out how to support shared memory with multiple patches...
|
|
opcodes = new RegularOpcodeManager();
|
|
if (!opcodes->LoadOpcodes(opfile.c_str())) {
|
|
LogNetcode("[OPCODES] Error loading opcodes file [{}]. Not registering patch [{}]", opfile.c_str(), name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//ok, now we have what we need to register.
|
|
|
|
EQStreamInterface::Signature signature;
|
|
std::string pname;
|
|
|
|
//register our world signature.
|
|
pname = std::string(name) + "_world";
|
|
signature.ignore_eq_opcode = 0;
|
|
signature.first_length = sizeof(structs::LoginInfo_Struct);
|
|
signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo);
|
|
into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy);
|
|
|
|
//register our zone signature.
|
|
pname = std::string(name) + "_zone";
|
|
signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket);
|
|
signature.first_length = sizeof(structs::ClientZoneEntry_Struct);
|
|
signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry);
|
|
into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy);
|
|
|
|
LogNetcode("[StreamIdentify] Registered patch [{}]", name);
|
|
}
|
|
|
|
void Reload()
|
|
{
|
|
//we have a big problem to solve here when we switch back to shared memory
|
|
//opcode managers because we need to change the manager pointer, which means
|
|
//we need to go to every stream and replace it's manager.
|
|
|
|
if (opcodes != nullptr) {
|
|
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
|
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
|
LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name);
|
|
return;
|
|
}
|
|
LogNetcode("[OPCODES] Reloaded opcodes for patch [{}]", name);
|
|
}
|
|
}
|
|
|
|
Strategy::Strategy() : StructStrategy()
|
|
{
|
|
//all opcodes default to passthrough.
|
|
#include "ss_register.h"
|
|
#include "sof_ops.h"
|
|
}
|
|
|
|
std::string Strategy::Describe() const
|
|
{
|
|
std::string r;
|
|
r += "Patch ";
|
|
r += name;
|
|
return(r);
|
|
}
|
|
|
|
const EQ::versions::ClientVersion Strategy::ClientVersion() const
|
|
{
|
|
return EQ::versions::ClientVersion::SoF;
|
|
}
|
|
|
|
#include "ss_define.h"
|
|
|
|
// ENCODE methods
|
|
ENCODE(OP_Action)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Action_Struct);
|
|
SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct);
|
|
|
|
OUT(target);
|
|
OUT(source);
|
|
OUT(level);
|
|
OUT(instrument_mod);
|
|
OUT(force);
|
|
OUT(hit_heading);
|
|
OUT(hit_pitch);
|
|
OUT(type);
|
|
//OUT(damage);
|
|
OUT(spell);
|
|
OUT(spell_level);
|
|
OUT(effect_flag); // if this is 4, a buff icon is made
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_AdventureMerchantSell)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Adventure_Sell_Struct);
|
|
SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct);
|
|
|
|
eq->unknown000 = 1;
|
|
OUT(npcid);
|
|
eq->slot = ServerToSoFSlot(emu->slot);
|
|
OUT(charges);
|
|
OUT(sell_price);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_AltCurrencySell)
|
|
{
|
|
ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct);
|
|
SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct);
|
|
|
|
OUT(merchant_entity_id);
|
|
eq->slot_id = ServerToSoFSlot(emu->slot_id);
|
|
OUT(charges);
|
|
OUT(cost);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ApplyPoison)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ApplyPoison_Struct);
|
|
SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct);
|
|
|
|
eq->inventorySlot = ServerToSoFSlot(emu->inventorySlot);
|
|
OUT(success);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_AugmentInfo)
|
|
{
|
|
ENCODE_LENGTH_EXACT(AugmentInfo_Struct);
|
|
SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct);
|
|
|
|
OUT(itemid);
|
|
OUT(window);
|
|
strn0cpy(eq->augment_info, emu->augment_info, 64);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_BazaarSearch)
|
|
{
|
|
if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) {
|
|
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
return;
|
|
}
|
|
|
|
//consume the packet
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer;
|
|
|
|
//determine and verify length
|
|
int entrycount = in->size / sizeof(BazaarSearchResults_Struct);
|
|
if (entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) {
|
|
Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d",
|
|
opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
|
|
//make the EQ struct.
|
|
in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount;
|
|
in->pBuffer = new unsigned char[in->size];
|
|
structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer;
|
|
|
|
//zero out the packet. We could avoid this memset by setting all fields (including unknowns)
|
|
//in the loop.
|
|
memset(in->pBuffer, 0, in->size);
|
|
|
|
for (int i = 0; i<entrycount; i++, eq++, emu++) {
|
|
eq->Beginning.Action = emu->Beginning.Action;
|
|
eq->Beginning.Unknown001 = emu->Beginning.Unknown001;
|
|
eq->Beginning.Unknown002 = emu->Beginning.Unknown002;
|
|
eq->NumItems = emu->NumItems;
|
|
eq->SerialNumber = emu->SerialNumber;
|
|
eq->SellerID = emu->SellerID;
|
|
eq->Cost = emu->Cost;
|
|
eq->ItemStat = emu->ItemStat;
|
|
strcpy(eq->ItemName, emu->ItemName);
|
|
}
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_BecomeTrader)
|
|
{
|
|
ENCODE_LENGTH_EXACT(BecomeTrader_Struct);
|
|
SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct);
|
|
|
|
OUT(ID);
|
|
OUT(Code);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_Buff)
|
|
{
|
|
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
|
|
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
|
|
|
OUT(entityid);
|
|
OUT(buff.effect_type);
|
|
OUT(buff.level);
|
|
OUT(buff.bard_modifier);
|
|
OUT(buff.spellid);
|
|
OUT(buff.duration);
|
|
OUT(buff.counters);
|
|
OUT(buff.player_id);
|
|
eq->slotid = ServerToSoFBuffSlot(emu->slotid);
|
|
OUT(bufffade);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_CancelTrade)
|
|
{
|
|
ENCODE_LENGTH_EXACT(CancelTrade_Struct);
|
|
SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct);
|
|
|
|
OUT(fromid);
|
|
OUT(action);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ChannelMessage)
|
|
{
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer;
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
std::string old_message = emu->message;
|
|
std::string new_message;
|
|
ServerToSoFSayLink(new_message, old_message);
|
|
|
|
in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1;
|
|
|
|
in->pBuffer = new unsigned char[in->size];
|
|
|
|
char *OutBuffer = (char *)in->pBuffer;
|
|
|
|
memcpy(OutBuffer, __emu_buffer, sizeof(ChannelMessage_Struct));
|
|
|
|
OutBuffer += sizeof(ChannelMessage_Struct);
|
|
|
|
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_CharInventory)
|
|
{
|
|
//consume the packet
|
|
EQApplicationPacket* in = *p;
|
|
*p = nullptr;
|
|
|
|
if (!in->size) {
|
|
in->size = 4;
|
|
in->pBuffer = new uchar[in->size];
|
|
memset(in->pBuffer, 0, in->size);
|
|
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
return;
|
|
}
|
|
|
|
//store away the emu struct
|
|
uchar* __emu_buffer = in->pBuffer;
|
|
|
|
int item_count = in->size / sizeof(EQ::InternalSerializedItem_Struct);
|
|
if (!item_count || (in->size % sizeof(EQ::InternalSerializedItem_Struct)) != 0) {
|
|
Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d",
|
|
opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(EQ::InternalSerializedItem_Struct));
|
|
|
|
delete in;
|
|
return;
|
|
}
|
|
|
|
EQ::InternalSerializedItem_Struct* eq = (EQ::InternalSerializedItem_Struct*)in->pBuffer;
|
|
|
|
EQ::OutBuffer ob;
|
|
EQ::OutBuffer::pos_type last_pos = ob.tellp();
|
|
|
|
ob.write((const char*)&item_count, sizeof(uint32));
|
|
|
|
for (int index = 0; index < item_count; ++index, ++eq) {
|
|
SerializeItem(ob, (const EQ::ItemInstance*)eq->inst, eq->slot_id, 0);
|
|
if (ob.tellp() == last_pos)
|
|
LogNetcode("SoF::ENCODE(OP_CharInventory) Serialization failed on item slot [{}] during OP_CharInventory. Item skipped", eq->slot_id);
|
|
|
|
last_pos = ob.tellp();
|
|
}
|
|
|
|
in->size = ob.size();
|
|
in->pBuffer = ob.detach();
|
|
|
|
delete[] __emu_buffer;
|
|
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_ClientUpdate)
|
|
{
|
|
ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct);
|
|
SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct);
|
|
|
|
OUT(spawn_id);
|
|
OUT(x_pos);
|
|
OUT(delta_x);
|
|
OUT(delta_y);
|
|
OUT(z_pos);
|
|
OUT(delta_heading);
|
|
OUT(y_pos);
|
|
OUT(delta_z);
|
|
OUT(animation);
|
|
OUT(heading);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_Consider)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Consider_Struct);
|
|
SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct);
|
|
|
|
OUT(playerid);
|
|
OUT(targetid);
|
|
OUT(faction);
|
|
OUT(level);
|
|
OUT(pvpcon);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_Damage)
|
|
{
|
|
ENCODE_LENGTH_EXACT(CombatDamage_Struct);
|
|
SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct);
|
|
|
|
OUT(target);
|
|
OUT(source);
|
|
OUT(type);
|
|
OUT(spellid);
|
|
OUT(damage);
|
|
OUT(force);
|
|
OUT(hit_heading);
|
|
OUT(hit_pitch);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DeleteCharge)
|
|
{
|
|
Log(Logs::Detail, Logs::Netcode, "SoF::ENCODE(OP_DeleteCharge)");
|
|
|
|
ENCODE_FORWARD(OP_MoveItem);
|
|
}
|
|
|
|
ENCODE(OP_DeleteItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(DeleteItem_Struct);
|
|
SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct);
|
|
|
|
eq->from_slot = ServerToSoFSlot(emu->from_slot);
|
|
eq->to_slot = ServerToSoFSlot(emu->to_slot);
|
|
OUT(number_in_stack);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DeleteSpawn)
|
|
{
|
|
SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct);
|
|
|
|
OUT(spawn_id);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzChooseZone)
|
|
{
|
|
SETUP_VAR_ENCODE(DynamicZoneChooseZone_Struct);
|
|
|
|
SerializeBuffer buf;
|
|
buf.WriteUInt32(emu->client_id);
|
|
buf.WriteUInt32(emu->count);
|
|
|
|
for (uint32 i = 0; i < emu->count; ++i)
|
|
{
|
|
buf.WriteUInt16(emu->choices[i].dz_zone_id);
|
|
buf.WriteUInt16(emu->choices[i].dz_instance_id);
|
|
buf.WriteUInt32(emu->choices[i].unknown_id1);
|
|
buf.WriteUInt32(emu->choices[i].dz_type);
|
|
buf.WriteUInt32(emu->choices[i].unknown_id2);
|
|
buf.WriteString(emu->choices[i].description);
|
|
buf.WriteString(emu->choices[i].leader_name);
|
|
}
|
|
|
|
__packet->size = buf.size();
|
|
__packet->pBuffer = new unsigned char[__packet->size];
|
|
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzExpeditionEndsWarning)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ExpeditionExpireWarning);
|
|
SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning);
|
|
|
|
OUT(minutes_remaining);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzExpeditionInfo)
|
|
{
|
|
ENCODE_LENGTH_EXACT(DynamicZoneInfo_Struct);
|
|
SETUP_DIRECT_ENCODE(DynamicZoneInfo_Struct, structs::DynamicZoneInfo_Struct);
|
|
|
|
OUT(client_id);
|
|
OUT(assigned);
|
|
OUT(max_players);
|
|
strn0cpy(eq->dz_name, emu->dz_name, sizeof(eq->dz_name));
|
|
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzExpeditionInvite)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ExpeditionInvite_Struct);
|
|
SETUP_DIRECT_ENCODE(ExpeditionInvite_Struct, structs::ExpeditionInvite_Struct);
|
|
|
|
OUT(client_id);
|
|
strn0cpy(eq->inviter_name, emu->inviter_name, sizeof(eq->inviter_name));
|
|
strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name));
|
|
OUT(swapping);
|
|
strn0cpy(eq->swap_name, emu->swap_name, sizeof(eq->swap_name));
|
|
OUT(dz_zone_id);
|
|
OUT(dz_instance_id);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzExpeditionLockoutTimers)
|
|
{
|
|
SETUP_VAR_ENCODE(ExpeditionLockoutTimers_Struct);
|
|
|
|
SerializeBuffer buf;
|
|
buf.WriteUInt32(emu->client_id);
|
|
buf.WriteUInt32(emu->count);
|
|
for (uint32 i = 0; i < emu->count; ++i)
|
|
{
|
|
buf.WriteString(emu->timers[i].expedition_name);
|
|
buf.WriteUInt32(emu->timers[i].seconds_remaining);
|
|
buf.WriteInt32(emu->timers[i].event_type);
|
|
buf.WriteString(emu->timers[i].event_name);
|
|
}
|
|
|
|
__packet->size = buf.size();
|
|
__packet->pBuffer = new unsigned char[__packet->size];
|
|
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzSetLeaderName)
|
|
{
|
|
ENCODE_LENGTH_EXACT(DynamicZoneLeaderName_Struct);
|
|
SETUP_DIRECT_ENCODE(DynamicZoneLeaderName_Struct, structs::DynamicZoneLeaderName_Struct);
|
|
|
|
OUT(client_id);
|
|
strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name));
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzMemberList)
|
|
{
|
|
SETUP_VAR_ENCODE(DynamicZoneMemberList_Struct);
|
|
|
|
SerializeBuffer buf;
|
|
buf.WriteUInt32(emu->client_id);
|
|
buf.WriteUInt32(emu->member_count);
|
|
for (uint32 i = 0; i < emu->member_count; ++i)
|
|
{
|
|
buf.WriteString(emu->members[i].name);
|
|
buf.WriteUInt8(emu->members[i].online_status);
|
|
}
|
|
|
|
__packet->size = buf.size();
|
|
__packet->pBuffer = new unsigned char[__packet->size];
|
|
memcpy(__packet->pBuffer, buf.buffer(), __packet->size);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzMemberListName)
|
|
{
|
|
ENCODE_LENGTH_EXACT(DynamicZoneMemberListName_Struct);
|
|
SETUP_DIRECT_ENCODE(DynamicZoneMemberListName_Struct, structs::DynamicZoneMemberListName_Struct);
|
|
|
|
OUT(client_id);
|
|
OUT(add_name);
|
|
strn0cpy(eq->name, emu->name, sizeof(eq->name));
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzMemberListStatus)
|
|
{
|
|
auto emu = reinterpret_cast<DynamicZoneMemberList_Struct*>((*p)->pBuffer);
|
|
if (emu->member_count == 1)
|
|
{
|
|
ENCODE_FORWARD(OP_DzMemberList);
|
|
}
|
|
}
|
|
|
|
ENCODE(OP_Emote)
|
|
{
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
Emote_Struct *emu = (Emote_Struct *)in->pBuffer;
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
std::string old_message = emu->message;
|
|
std::string new_message;
|
|
ServerToSoFSayLink(new_message, old_message);
|
|
|
|
//if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm)
|
|
// new_message = new_message.substr(0, 512);
|
|
|
|
in->size = new_message.length() + 5;
|
|
in->pBuffer = new unsigned char[in->size];
|
|
|
|
char *OutBuffer = (char *)in->pBuffer;
|
|
|
|
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->type);
|
|
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_ExpansionInfo)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ExpansionInfo_Struct);
|
|
SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct);
|
|
|
|
OUT(Expansions);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_FormattedMessage)
|
|
{
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
FormattedMessage_Struct *emu = (FormattedMessage_Struct *)in->pBuffer;
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
char *old_message_ptr = (char *)in->pBuffer;
|
|
old_message_ptr += sizeof(FormattedMessage_Struct);
|
|
|
|
std::string old_message_array[9];
|
|
|
|
for (int i = 0; i < 9; ++i) {
|
|
if (*old_message_ptr == 0) { break; }
|
|
old_message_array[i] = old_message_ptr;
|
|
old_message_ptr += old_message_array[i].length() + 1;
|
|
}
|
|
|
|
uint32 new_message_size = 0;
|
|
std::string new_message_array[9];
|
|
|
|
for (int i = 0; i < 9; ++i) {
|
|
if (old_message_array[i].length() == 0) { break; }
|
|
ServerToSoFSayLink(new_message_array[i], old_message_array[i]);
|
|
new_message_size += new_message_array[i].length() + 1;
|
|
}
|
|
|
|
in->size = sizeof(FormattedMessage_Struct) + new_message_size + 1;
|
|
in->pBuffer = new unsigned char[in->size];
|
|
|
|
char *OutBuffer = (char *)in->pBuffer;
|
|
|
|
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->unknown0);
|
|
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->string_id);
|
|
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->type);
|
|
|
|
for (int i = 0; i < 9; ++i) {
|
|
if (new_message_array[i].length() == 0) { break; }
|
|
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message_array[i].c_str());
|
|
}
|
|
|
|
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0);
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_GroundSpawn)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Object_Struct);
|
|
SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct);
|
|
|
|
OUT(drop_id);
|
|
OUT(zone_id);
|
|
OUT(zone_instance);
|
|
OUT(heading);
|
|
OUT(x);
|
|
OUT(y);
|
|
OUT(z);
|
|
OUT_str(object_name);
|
|
OUT(object_type);
|
|
OUT(spawn_id);
|
|
|
|
/*fill in some unknowns with observed values, hopefully it will help */
|
|
eq->unknown020 = 0;
|
|
eq->unknown024 = 0;
|
|
eq->size = 1; //This forces all objects to standard size for now
|
|
eq->unknown088 = 0;
|
|
memset(eq->unknown096, 0xFF, sizeof(eq->unknown096));
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_GuildMemberList)
|
|
{
|
|
//consume the packet
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer;
|
|
|
|
//make a new EQ buffer.
|
|
uint32 pnl = strlen(emu->player_name);
|
|
uint32 length = sizeof(structs::GuildMembers_Struct) + pnl +
|
|
emu->count*sizeof(structs::GuildMemberEntry_Struct)
|
|
+ emu->name_length + emu->note_length;
|
|
in->pBuffer = new uint8[length];
|
|
in->size = length;
|
|
//no memset since we fill every byte.
|
|
|
|
uint8 *buffer;
|
|
buffer = in->pBuffer;
|
|
|
|
//easier way to setup GuildMembers_Struct
|
|
//set prefix name
|
|
strcpy((char *)buffer, emu->player_name);
|
|
buffer += pnl;
|
|
*buffer = '\0';
|
|
buffer++;
|
|
|
|
//add member count.
|
|
*((uint32 *)buffer) = htonl(emu->count);
|
|
buffer += sizeof(uint32);
|
|
|
|
if (emu->count > 0) {
|
|
Internal_GuildMemberEntry_Struct *emu_e = emu->member;
|
|
const char *emu_name = (const char *)(__emu_buffer +
|
|
sizeof(Internal_GuildMembers_Struct)+ //skip header
|
|
emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data
|
|
);
|
|
const char *emu_note = (emu_name +
|
|
emu->name_length + //skip name contents
|
|
emu->count //skip string terminators
|
|
);
|
|
|
|
structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer;
|
|
|
|
uint32 r;
|
|
for (r = 0; r < emu->count; r++, emu_e++) {
|
|
|
|
//the order we set things here must match the struct
|
|
|
|
//nice helper macro
|
|
/*#define SlideStructString(field, str) \
|
|
strcpy(e->field, str.c_str()); \
|
|
e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/
|
|
#define SlideStructString(field, str) \
|
|
{ \
|
|
int sl = strlen(str); \
|
|
memcpy(e->field, str, sl+1); \
|
|
e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \
|
|
str += sl + 1; \
|
|
}
|
|
#define PutFieldN(field) e->field = htonl(emu_e->field)
|
|
|
|
SlideStructString(name, emu_name);
|
|
PutFieldN(level);
|
|
PutFieldN(banker);
|
|
PutFieldN(class_);
|
|
PutFieldN(rank);
|
|
PutFieldN(time_last_on);
|
|
PutFieldN(tribute_enable);
|
|
PutFieldN(total_tribute);
|
|
PutFieldN(last_tribute);
|
|
e->unknown_one = htonl(1);
|
|
SlideStructString(public_note, emu_note);
|
|
e->zoneinstance = 0;
|
|
e->zone_id = htons(emu_e->zone_id);
|
|
|
|
#undef SlideStructString
|
|
#undef PutFieldN
|
|
|
|
e++;
|
|
}
|
|
}
|
|
|
|
delete[] __emu_buffer;
|
|
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_Illusion)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Illusion_Struct);
|
|
SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct);
|
|
|
|
OUT(spawnid);
|
|
OUT_str(charname);
|
|
OUT(race);
|
|
OUT(unknown006[0]);
|
|
OUT(unknown006[1]);
|
|
OUT(gender);
|
|
OUT(texture);
|
|
OUT(helmtexture);
|
|
OUT(face);
|
|
OUT(hairstyle);
|
|
OUT(haircolor);
|
|
OUT(beard);
|
|
OUT(beardcolor);
|
|
OUT(size);
|
|
OUT(drakkin_heritage);
|
|
OUT(drakkin_tattoo);
|
|
OUT(drakkin_details);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_InspectRequest)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Inspect_Struct);
|
|
SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct);
|
|
|
|
OUT(TargetID);
|
|
OUT(PlayerID);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); }
|
|
|
|
ENCODE(OP_ItemPacket)
|
|
{
|
|
//consume the packet
|
|
EQApplicationPacket* in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
uchar* __emu_buffer = in->pBuffer;
|
|
|
|
EQ::InternalSerializedItem_Struct* int_struct = (EQ::InternalSerializedItem_Struct*)(&__emu_buffer[4]);
|
|
|
|
EQ::OutBuffer ob;
|
|
EQ::OutBuffer::pos_type last_pos = ob.tellp();
|
|
|
|
ob.write((const char*)__emu_buffer, 4);
|
|
|
|
SerializeItem(ob, (const EQ::ItemInstance*)int_struct->inst, int_struct->slot_id, 0);
|
|
if (ob.tellp() == last_pos) {
|
|
LogNetcode("SoF::ENCODE(OP_ItemPacket) Serialization failed on item slot [{}]", int_struct->slot_id);
|
|
delete in;
|
|
return;
|
|
}
|
|
|
|
in->size = ob.size();
|
|
in->pBuffer = ob.detach();
|
|
|
|
delete[] __emu_buffer;
|
|
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_ItemVerifyReply)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct);
|
|
SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct);
|
|
|
|
eq->slot = ServerToSoFSlot(emu->slot);
|
|
OUT(spell);
|
|
OUT(target);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_LeadershipExpUpdate)
|
|
{
|
|
SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct);
|
|
|
|
OUT(group_leadership_exp);
|
|
OUT(group_leadership_points);
|
|
OUT(raid_leadership_exp);
|
|
OUT(raid_leadership_points);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_LogServer)
|
|
{
|
|
ENCODE_LENGTH_EXACT(LogServer_Struct);
|
|
SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct);
|
|
|
|
strcpy(eq->worldshortname, emu->worldshortname);
|
|
|
|
OUT(enablevoicemacros);
|
|
OUT(enablemail);
|
|
OUT(enable_pvp);
|
|
OUT(enable_FV);
|
|
|
|
// These next two need to be set like this for the Tutorial Button to work.
|
|
eq->unknown263[0] = 0;
|
|
eq->unknown263[2] = 1;
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_LootItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(LootingItem_Struct);
|
|
SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct);
|
|
|
|
Log(Logs::Detail, Logs::Netcode, "SoF::ENCODE(OP_LootItem)");
|
|
|
|
OUT(lootee);
|
|
OUT(looter);
|
|
eq->slot_id = ServerToSoFCorpseSlot(emu->slot_id);
|
|
OUT(auto_loot);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_MemorizeSpell)
|
|
{
|
|
ENCODE_LENGTH_EXACT(MemorizeSpell_Struct);
|
|
SETUP_DIRECT_ENCODE(MemorizeSpell_Struct, structs::MemorizeSpell_Struct);
|
|
|
|
// Since HT/LoH are translated up, we need to translate down only for memSpellSpellbar case
|
|
if (emu->scribing == 3)
|
|
eq->slot = static_cast<uint32>(ServerToSoFCastingSlot(static_cast<EQ::spells::CastingSlot>(emu->slot)));
|
|
else
|
|
OUT(slot);
|
|
OUT(spell_id);
|
|
OUT(scribing);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_MoveItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(MoveItem_Struct);
|
|
SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct);
|
|
|
|
Log(Logs::Detail, Logs::Netcode, "SoF::ENCODE(OP_MoveItem)");
|
|
|
|
eq->from_slot = ServerToSoFSlot(emu->from_slot);
|
|
eq->to_slot = ServerToSoFSlot(emu->to_slot);
|
|
OUT(number_in_stack);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); }
|
|
|
|
ENCODE(OP_NewZone)
|
|
{
|
|
SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct);
|
|
|
|
OUT_str(char_name);
|
|
OUT_str(zone_short_name);
|
|
OUT_str(zone_long_name);
|
|
OUT(ztype);
|
|
int r;
|
|
for (r = 0; r < 4; r++) {
|
|
OUT(fog_red[r]);
|
|
OUT(fog_green[r]);
|
|
OUT(fog_blue[r]);
|
|
OUT(fog_minclip[r]);
|
|
OUT(fog_maxclip[r]);
|
|
}
|
|
OUT(gravity);
|
|
OUT(time_type);
|
|
for (r = 0; r < 4; r++) {
|
|
OUT(rain_chance[r]);
|
|
}
|
|
for (r = 0; r < 4; r++) {
|
|
OUT(rain_duration[r]);
|
|
}
|
|
for (r = 0; r < 4; r++) {
|
|
OUT(snow_chance[r]);
|
|
}
|
|
for (r = 0; r < 4; r++) {
|
|
OUT(snow_duration[r]);
|
|
}
|
|
for (r = 0; r < 32; r++) {
|
|
eq->unknown537[r] = 0xFF; //observed
|
|
}
|
|
OUT(sky);
|
|
OUT(zone_exp_multiplier);
|
|
OUT(safe_y);
|
|
OUT(safe_x);
|
|
OUT(safe_z);
|
|
OUT(max_z);
|
|
OUT(underworld);
|
|
OUT(minclip);
|
|
OUT(maxclip);
|
|
OUT_str(zone_short_name2);
|
|
OUT(zone_id);
|
|
OUT(zone_instance);
|
|
OUT(suspend_buffs);
|
|
OUT(fast_regen_hp);
|
|
OUT(fast_regen_mana);
|
|
OUT(fast_regen_endurance);
|
|
OUT(underworld_teleport_index);
|
|
|
|
/*fill in some unknowns with observed values, hopefully it will help */
|
|
eq->unknown796 = -1;
|
|
eq->unknown840 = 600;
|
|
OUT(lava_damage);
|
|
OUT(min_lava_damage);
|
|
eq->unknown884 = 1;
|
|
eq->unknown885 = 0;
|
|
eq->unknown886 = 1;
|
|
eq->unknown887 = 0;
|
|
eq->unknown888 = 0;
|
|
eq->unknown889 = 0;
|
|
eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off
|
|
eq->unknown891 = 0;
|
|
eq->unknown904 = 2;
|
|
eq->unknown908 = 2;
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_OnLevelMessage)
|
|
{
|
|
ENCODE_LENGTH_EXACT(OnLevelMessage_Struct);
|
|
SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct);
|
|
|
|
OUT_str(Title);
|
|
OUT_str(Text);
|
|
OUT(Buttons);
|
|
OUT(Duration);
|
|
OUT(PopupID);
|
|
|
|
eq->unknown4236 = 0x00000000;
|
|
eq->unknown4240 = 0xffffffff;
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_PetBuffWindow)
|
|
{
|
|
ENCODE_LENGTH_EXACT(PetBuff_Struct);
|
|
SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct);
|
|
|
|
OUT(petid);
|
|
OUT(buffcount);
|
|
|
|
int EQBuffSlot = 0; // do we really want to shuffle them around like this?
|
|
|
|
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < PET_BUFF_COUNT; ++EmuBuffSlot)
|
|
{
|
|
if (emu->spellid[EmuBuffSlot])
|
|
{
|
|
eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot];
|
|
eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot];
|
|
}
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_PlayerProfile)
|
|
{
|
|
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
|
|
|
|
uint32 r;
|
|
|
|
eq->available_slots = 0xffffffff;
|
|
memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184));
|
|
memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396));
|
|
|
|
// OUT(checksum);
|
|
OUT(gender);
|
|
OUT(race);
|
|
OUT(class_);
|
|
// OUT(unknown00016);
|
|
OUT(level);
|
|
eq->level1 = emu->level;
|
|
// OUT(unknown00022[2]);
|
|
for (r = 0; r < 5; r++) {
|
|
OUT(binds[r].zone_id);
|
|
OUT(binds[r].x);
|
|
OUT(binds[r].y);
|
|
OUT(binds[r].z);
|
|
OUT(binds[r].heading);
|
|
}
|
|
OUT(deity);
|
|
OUT(intoxication);
|
|
|
|
OUT_array(spellSlotRefresh, spells::SPELL_GEM_COUNT);
|
|
eq->spellSlotRefresh[9] = 0; // 10th slot is not valid in this release
|
|
|
|
OUT(abilitySlotRefresh);
|
|
OUT(points); // Relocation Test
|
|
// OUT(unknown0166[4]);
|
|
OUT(haircolor);
|
|
OUT(beardcolor);
|
|
OUT(eyecolor1);
|
|
OUT(eyecolor2);
|
|
OUT(hairstyle);
|
|
OUT(beard);
|
|
// OUT(unknown00178[10]);
|
|
for (r = EQ::textures::textureBegin; r < EQ::textures::materialCount; r++) {
|
|
eq->equipment.Slot[r].Material = emu->item_material.Slot[r].Material;
|
|
eq->equipment.Slot[r].Unknown1 = 0;
|
|
eq->equipment.Slot[r].EliteMaterial = 0;
|
|
//eq->colors[r].color = emu->colors[r].color;
|
|
}
|
|
for (r = 0; r < 7; r++) {
|
|
OUT(item_tint.Slot[r].Color);
|
|
}
|
|
// OUT(unknown00224[48]);
|
|
//NOTE: new client supports 300 AAs, our internal rep/PP
|
|
//only supports 240..
|
|
for (r = 0; r < MAX_PP_AA_ARRAY; r++) {
|
|
OUT(aa_array[r].AA);
|
|
OUT(aa_array[r].value);
|
|
OUT(aa_array[r].charges);
|
|
}
|
|
// OUT(unknown02220[4]);
|
|
OUT(mana);
|
|
OUT(cur_hp);
|
|
OUT(STR);
|
|
OUT(STA);
|
|
OUT(CHA);
|
|
OUT(AGI);
|
|
OUT(INT);
|
|
OUT(DEX);
|
|
OUT(WIS);
|
|
OUT(face);
|
|
// OUT(unknown02264[47]);
|
|
|
|
if (spells::SPELLBOOK_SIZE <= EQ::spells::SPELLBOOK_SIZE) {
|
|
for (uint32 r = 0; r < spells::SPELLBOOK_SIZE; r++) {
|
|
if (emu->spell_book[r] <= spells::SPELL_ID_MAX)
|
|
eq->spell_book[r] = emu->spell_book[r];
|
|
else
|
|
eq->spell_book[r] = 0xFFFFFFFFU;
|
|
}
|
|
}
|
|
else {
|
|
for (uint32 r = 0; r < EQ::spells::SPELLBOOK_SIZE; r++) {
|
|
if (emu->spell_book[r] <= spells::SPELL_ID_MAX)
|
|
eq->spell_book[r] = emu->spell_book[r];
|
|
else
|
|
eq->spell_book[r] = 0xFFFFFFFFU;
|
|
}
|
|
// invalidate the rest of the spellbook slots
|
|
memset(&eq->spell_book[EQ::spells::SPELLBOOK_SIZE], 0xFF, (sizeof(uint32) * (spells::SPELLBOOK_SIZE - EQ::spells::SPELLBOOK_SIZE)));
|
|
}
|
|
|
|
// OUT(unknown4184[128]);
|
|
|
|
OUT_array(mem_spells, spells::SPELL_GEM_COUNT);
|
|
eq->mem_spells[9] = 0xFFFFFFFFU; // 10th slot is not valid in this release
|
|
|
|
// OUT(unknown04396[32]);
|
|
OUT(platinum);
|
|
OUT(gold);
|
|
OUT(silver);
|
|
OUT(copper);
|
|
OUT(platinum_cursor);
|
|
OUT(gold_cursor);
|
|
OUT(silver_cursor);
|
|
OUT(copper_cursor);
|
|
|
|
OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword)
|
|
OUT_array(InnateSkills, structs::MAX_PP_INNATE_SKILL); // 1:1 direct copy (25 dword)
|
|
|
|
// OUT(unknown04760[236]);
|
|
OUT(toxicity);
|
|
OUT(thirst_level);
|
|
OUT(hunger_level);
|
|
for (r = 0; r < structs::BUFF_COUNT; r++) {
|
|
OUT(buffs[r].effect_type);
|
|
OUT(buffs[r].level);
|
|
OUT(buffs[r].bard_modifier);
|
|
OUT(buffs[r].unknown003);
|
|
OUT(buffs[r].spellid);
|
|
OUT(buffs[r].duration);
|
|
OUT(buffs[r].counters);
|
|
OUT(buffs[r].player_id);
|
|
}
|
|
for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) {
|
|
OUT(disciplines.values[r]);
|
|
}
|
|
OUT_array(recastTimers, structs::MAX_RECAST_TYPES);
|
|
// OUT(unknown08124[360]);
|
|
OUT(endurance);
|
|
OUT(aapoints_spent);
|
|
OUT(aapoints);
|
|
|
|
// OUT(unknown06160[4]);
|
|
|
|
// Copy bandoliers where server and client indices converge
|
|
for (r = 0; r < EQ::profile::BANDOLIERS_SIZE && r < profile::BANDOLIERS_SIZE; ++r) {
|
|
OUT_str(bandoliers[r].Name);
|
|
for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
|
|
OUT(bandoliers[r].Items[k].ID);
|
|
OUT(bandoliers[r].Items[k].Icon);
|
|
OUT_str(bandoliers[r].Items[k].Name);
|
|
}
|
|
}
|
|
// Nullify bandoliers where server and client indices diverge, with a client bias
|
|
for (r = EQ::profile::BANDOLIERS_SIZE; r < profile::BANDOLIERS_SIZE; ++r) {
|
|
eq->bandoliers[r].Name[0] = '\0';
|
|
for (uint32 k = 0; k < profile::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
|
|
eq->bandoliers[r].Items[k].ID = 0;
|
|
eq->bandoliers[r].Items[k].Icon = 0;
|
|
eq->bandoliers[r].Items[k].Name[0] = '\0';
|
|
}
|
|
}
|
|
|
|
// OUT(unknown07444[5120]);
|
|
|
|
// Copy potion belt where server and client indices converge
|
|
for (r = 0; r < EQ::profile::POTION_BELT_SIZE && r < profile::POTION_BELT_SIZE; ++r) {
|
|
OUT(potionbelt.Items[r].ID);
|
|
OUT(potionbelt.Items[r].Icon);
|
|
OUT_str(potionbelt.Items[r].Name);
|
|
}
|
|
// Nullify potion belt where server and client indices diverge, with a client bias
|
|
for (r = EQ::profile::POTION_BELT_SIZE; r < profile::POTION_BELT_SIZE; ++r) {
|
|
eq->potionbelt.Items[r].ID = 0;
|
|
eq->potionbelt.Items[r].Icon = 0;
|
|
eq->potionbelt.Items[r].Name[0] = '\0';
|
|
}
|
|
|
|
// OUT(unknown12852[8]);
|
|
// OUT(unknown12864[76]);
|
|
|
|
OUT_str(name);
|
|
OUT_str(last_name);
|
|
OUT(guild_id);
|
|
OUT(birthday);
|
|
OUT(lastlogin);
|
|
OUT(timePlayedMin);
|
|
OUT(pvp);
|
|
OUT(anon);
|
|
OUT(gm);
|
|
OUT(guildrank);
|
|
OUT(guildbanker);
|
|
// OUT(unknown13054[12]);
|
|
OUT(exp);
|
|
// OUT(unknown13072[8]);
|
|
OUT(timeentitledonaccount);
|
|
OUT_array(languages, structs::MAX_PP_LANGUAGE);
|
|
// OUT(unknown13109[7]);
|
|
OUT(y); //reversed x and y
|
|
OUT(x);
|
|
OUT(z);
|
|
OUT(heading);
|
|
// OUT(unknown13132[4]);
|
|
OUT(platinum_bank);
|
|
OUT(gold_bank);
|
|
OUT(silver_bank);
|
|
OUT(copper_bank);
|
|
OUT(platinum_shared);
|
|
// OUT(unknown13156[84]);
|
|
OUT(expansions);
|
|
//eq->expansions = 16383;
|
|
// OUT(unknown13244[12]);
|
|
OUT(autosplit);
|
|
// OUT(unknown13260[16]);
|
|
OUT(zone_id);
|
|
OUT(zoneInstance);
|
|
for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) {
|
|
OUT_str(groupMembers[r]);
|
|
}
|
|
strcpy(eq->groupLeader, emu->groupMembers[0]);
|
|
// OUT_str(groupLeader);
|
|
// OUT(unknown13728[660]);
|
|
OUT(entityid);
|
|
OUT(leadAAActive);
|
|
// OUT(unknown14392[4]);
|
|
OUT(ldon_points_guk);
|
|
OUT(ldon_points_mir);
|
|
OUT(ldon_points_mmc);
|
|
OUT(ldon_points_ruj);
|
|
OUT(ldon_points_tak);
|
|
OUT(ldon_points_available);
|
|
// OUT(unknown14420[132]);
|
|
OUT(tribute_time_remaining);
|
|
OUT(career_tribute_points);
|
|
// OUT(unknown7208);
|
|
OUT(tribute_points);
|
|
// OUT(unknown7216);
|
|
OUT(tribute_active);
|
|
for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) {
|
|
OUT(tributes[r].tribute);
|
|
OUT(tributes[r].tier);
|
|
}
|
|
// OUT(unknown14616[8]);
|
|
OUT(group_leadership_exp);
|
|
// OUT(unknown14628);
|
|
OUT(raid_leadership_exp);
|
|
OUT(group_leadership_points);
|
|
OUT(raid_leadership_points);
|
|
OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY);
|
|
// OUT(unknown14772[128]);
|
|
OUT(air_remaining);
|
|
OUT(PVPKills);
|
|
OUT(PVPDeaths);
|
|
OUT(PVPCurrentPoints);
|
|
OUT(PVPCareerPoints);
|
|
OUT(PVPBestKillStreak);
|
|
OUT(PVPWorstDeathStreak);
|
|
OUT(PVPCurrentKillStreak);
|
|
// OUT(unknown17892[4580]);
|
|
OUT(expAA);
|
|
// OUT(unknown19516[40]);
|
|
OUT(currentRadCrystals);
|
|
OUT(careerRadCrystals);
|
|
OUT(currentEbonCrystals);
|
|
OUT(careerEbonCrystals);
|
|
OUT(groupAutoconsent);
|
|
OUT(raidAutoconsent);
|
|
OUT(guildAutoconsent);
|
|
// OUT(unknown19575[5]);
|
|
eq->level3 = emu->level;
|
|
eq->showhelm = emu->showhelm;
|
|
OUT(RestTimer);
|
|
// OUT(unknown19584[4]);
|
|
// OUT(unknown19588);
|
|
|
|
const uint8 bytes[] = {
|
|
0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
|
|
0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
|
0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F,
|
|
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
memcpy(eq->unknown12864, bytes, sizeof(bytes));
|
|
|
|
//set the checksum...
|
|
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_RaidJoin)
|
|
{
|
|
EQApplicationPacket* inapp = *p;
|
|
*p = nullptr;
|
|
unsigned char* __emu_buffer = inapp->pBuffer;
|
|
RaidCreate_Struct* emu = (RaidCreate_Struct*)__emu_buffer;
|
|
|
|
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct));
|
|
structs::RaidGeneral_Struct* general = (structs::RaidGeneral_Struct*)outapp->pBuffer;
|
|
|
|
general->action = raidCreate;
|
|
general->parameter = RaidCommandAcceptInvite;
|
|
strn0cpy(general->leader_name, emu->leader_name, sizeof(emu->leader_name));
|
|
strn0cpy(general->player_name, emu->leader_name, sizeof(emu->leader_name));
|
|
|
|
dest->FastQueuePacket(&outapp);
|
|
|
|
safe_delete(inapp);
|
|
|
|
}
|
|
|
|
ENCODE(OP_RaidUpdate)
|
|
{
|
|
EQApplicationPacket* inapp = *p;
|
|
*p = nullptr;
|
|
unsigned char* __emu_buffer = inapp->pBuffer;
|
|
RaidGeneral_Struct* raid_gen = (RaidGeneral_Struct*)__emu_buffer;
|
|
|
|
switch (raid_gen->action)
|
|
{
|
|
case raidAdd:
|
|
{
|
|
RaidAddMember_Struct* emu = (RaidAddMember_Struct*)__emu_buffer;
|
|
|
|
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct));
|
|
structs::RaidAddMember_Struct* eq = (structs::RaidAddMember_Struct*)outapp->pBuffer;
|
|
|
|
OUT(raidGen.action);
|
|
OUT(raidGen.parameter);
|
|
OUT_str(raidGen.leader_name);
|
|
OUT_str(raidGen.player_name);
|
|
OUT(_class);
|
|
OUT(level);
|
|
OUT(isGroupLeader);
|
|
OUT(flags[0]);
|
|
OUT(flags[1]);
|
|
OUT(flags[2]);
|
|
OUT(flags[3]);
|
|
OUT(flags[4]);
|
|
|
|
dest->FastQueuePacket(&outapp);
|
|
break;
|
|
}
|
|
case raidSetMotd:
|
|
{
|
|
RaidMOTD_Struct* emu = (RaidMOTD_Struct*)__emu_buffer;
|
|
|
|
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct));
|
|
structs::RaidMOTD_Struct* eq = (structs::RaidMOTD_Struct*)outapp->pBuffer;
|
|
|
|
OUT(general.action);
|
|
OUT_str(general.player_name);
|
|
OUT_str(general.leader_name);
|
|
OUT_str(motd);
|
|
|
|
dest->FastQueuePacket(&outapp);
|
|
break;
|
|
}
|
|
case raidSetLeaderAbilities:
|
|
case raidMakeLeader:
|
|
{
|
|
RaidLeadershipUpdate_Struct* emu = (RaidLeadershipUpdate_Struct*)__emu_buffer;
|
|
|
|
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct));
|
|
structs::RaidLeadershipUpdate_Struct* eq = (structs::RaidLeadershipUpdate_Struct*)outapp->pBuffer;
|
|
|
|
OUT(action);
|
|
OUT_str(player_name);
|
|
OUT_str(leader_name);
|
|
memcpy(&eq->raid, &emu->raid, sizeof(RaidLeadershipAA_Struct));
|
|
|
|
dest->FastQueuePacket(&outapp);
|
|
break;
|
|
}
|
|
case raidSetNote:
|
|
{
|
|
auto emu = (RaidNote_Struct*)__emu_buffer;
|
|
|
|
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidNote_Struct));
|
|
auto eq = (structs::RaidNote_Struct*)outapp->pBuffer;
|
|
|
|
OUT(general.action);
|
|
OUT_str(general.leader_name);
|
|
OUT_str(general.player_name);
|
|
OUT_str(note);
|
|
|
|
dest->FastQueuePacket(&outapp);
|
|
break;
|
|
}
|
|
case raidNoRaid:
|
|
{
|
|
dest->QueuePacket(inapp);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
RaidGeneral_Struct* emu = (RaidGeneral_Struct*)__emu_buffer;
|
|
|
|
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct));
|
|
structs::RaidGeneral_Struct* eq = (structs::RaidGeneral_Struct*)outapp->pBuffer;
|
|
|
|
OUT(action);
|
|
OUT(parameter);
|
|
OUT_str(leader_name);
|
|
OUT_str(player_name);
|
|
|
|
dest->FastQueuePacket(&outapp);
|
|
break;
|
|
}
|
|
}
|
|
safe_delete(inapp);
|
|
}
|
|
|
|
ENCODE(OP_ReadBook)
|
|
{
|
|
ENCODE_LENGTH_ATLEAST(BookText_Struct);
|
|
SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct);
|
|
|
|
if (emu->window == 0xFF)
|
|
eq->window = 0xFFFFFFFF;
|
|
else
|
|
eq->window = emu->window;
|
|
OUT(type);
|
|
eq->invslot = ServerToSoFSlot(emu->invslot);
|
|
OUT(target_id);
|
|
OUT(can_cast);
|
|
OUT(can_scribe);
|
|
strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile));
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_SendAATable)
|
|
{
|
|
EQApplicationPacket *inapp = *p;
|
|
*p = nullptr;
|
|
AARankInfo_Struct *emu = (AARankInfo_Struct*)inapp->pBuffer;
|
|
|
|
auto outapp = new EQApplicationPacket(
|
|
OP_SendAATable, sizeof(structs::SendAA_Struct) + emu->total_effects * sizeof(structs::AA_Ability));
|
|
structs::SendAA_Struct *eq = (structs::SendAA_Struct*)outapp->pBuffer;
|
|
|
|
inapp->SetReadPosition(sizeof(AARankInfo_Struct));
|
|
outapp->SetWritePosition(sizeof(structs::SendAA_Struct));
|
|
|
|
eq->id = emu->id;
|
|
eq->unknown004 = 1;
|
|
eq->id = emu->id;
|
|
eq->hotkey_sid = emu->upper_hotkey_sid;
|
|
eq->hotkey_sid2 = emu->lower_hotkey_sid;
|
|
eq->desc_sid = emu->desc_sid;
|
|
eq->title_sid = emu->title_sid;
|
|
eq->class_type = emu->level_req;
|
|
eq->cost = emu->cost;
|
|
eq->seq = emu->seq;
|
|
eq->current_level = emu->current_level;
|
|
eq->type = emu->type;
|
|
eq->spellid = emu->spell;
|
|
eq->spell_type = emu->spell_type;
|
|
eq->spell_refresh = emu->spell_refresh;
|
|
eq->classes = emu->classes;
|
|
eq->max_level = emu->max_level;
|
|
eq->last_id = emu->prev_id;
|
|
eq->next_id = emu->next_id;
|
|
eq->cost2 = emu->total_cost;
|
|
eq->grant_only = emu->grant_only;
|
|
eq->expendable_charges = emu->charges;
|
|
eq->aa_expansion = emu->expansion;
|
|
eq->special_category = emu->category;
|
|
eq->total_abilities = emu->total_effects;
|
|
|
|
for(auto i = 0; i < eq->total_abilities; ++i) {
|
|
eq->abilities[i].skill_id = inapp->ReadUInt32();
|
|
eq->abilities[i].base_value = inapp->ReadUInt32();
|
|
eq->abilities[i].limit_value = inapp->ReadUInt32();
|
|
eq->abilities[i].slot = inapp->ReadUInt32();
|
|
}
|
|
|
|
if(emu->total_prereqs > 0) {
|
|
eq->prereq_skill = inapp->ReadUInt32();
|
|
eq->prereq_minpoints = inapp->ReadUInt32();
|
|
}
|
|
|
|
dest->FastQueuePacket(&outapp);
|
|
delete inapp;
|
|
}
|
|
|
|
ENCODE(OP_SendCharInfo)
|
|
{
|
|
ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
|
|
SETUP_VAR_ENCODE(CharacterSelect_Struct);
|
|
|
|
// Zero-character count shunt
|
|
if (emu->CharCount == 0) {
|
|
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
|
|
eq->CharCount = emu->CharCount;
|
|
eq->TotalChars = emu->TotalChars;
|
|
|
|
if (eq->TotalChars > constants::CHARACTER_CREATION_LIMIT)
|
|
eq->TotalChars = constants::CHARACTER_CREATION_LIMIT;
|
|
|
|
FINISH_ENCODE();
|
|
return;
|
|
}
|
|
|
|
unsigned char *emu_ptr = __emu_buffer;
|
|
emu_ptr += sizeof(CharacterSelect_Struct);
|
|
CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr;
|
|
|
|
size_t names_length = 0;
|
|
size_t character_count = 0;
|
|
for (; character_count < emu->CharCount && character_count < constants::CHARACTER_CREATION_LIMIT; ++character_count) {
|
|
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
|
|
names_length += strlen(emu_cse->Name);
|
|
emu_ptr += sizeof(CharacterSelectEntry_Struct);
|
|
}
|
|
|
|
size_t total_length = sizeof(structs::CharacterSelect_Struct)
|
|
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
|
|
+ names_length;
|
|
|
|
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length);
|
|
structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr;
|
|
|
|
eq->CharCount = character_count;
|
|
eq->TotalChars = emu->TotalChars;
|
|
|
|
if (eq->TotalChars > constants::CHARACTER_CREATION_LIMIT)
|
|
eq->TotalChars = constants::CHARACTER_CREATION_LIMIT;
|
|
|
|
emu_ptr = __emu_buffer;
|
|
emu_ptr += sizeof(CharacterSelect_Struct);
|
|
|
|
unsigned char *eq_ptr = __packet->pBuffer;
|
|
eq_ptr += sizeof(structs::CharacterSelect_Struct);
|
|
|
|
for (int counter = 0; counter < character_count; ++counter) {
|
|
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
|
|
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // base address
|
|
|
|
eq_cse->Level = emu_cse->Level;
|
|
eq_cse->HairStyle = emu_cse->HairStyle;
|
|
eq_cse->Gender = emu_cse->Gender;
|
|
|
|
strcpy(eq_cse->Name, emu_cse->Name);
|
|
eq_ptr += strlen(emu_cse->Name);
|
|
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // offset address (base + name length offset)
|
|
eq_cse->Name[0] = '\0'; // (offset)eq_cse->Name[0] = (base)eq_cse->Name[strlen(emu_cse->Name)]
|
|
|
|
eq_cse->Beard = emu_cse->Beard;
|
|
eq_cse->HairColor = emu_cse->HairColor;
|
|
eq_cse->Face = emu_cse->Face;
|
|
|
|
for (int equip_index = EQ::textures::textureBegin; equip_index < EQ::textures::materialCount; equip_index++) {
|
|
eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material;
|
|
eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1;
|
|
eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteModel;
|
|
eq_cse->Equip[equip_index].Color = emu_cse->Equip[equip_index].Color;
|
|
}
|
|
|
|
eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
|
|
eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
|
|
eq_cse->Tutorial = emu_cse->Tutorial;
|
|
eq_cse->Unknown15 = emu_cse->Unknown15;
|
|
eq_cse->Deity = emu_cse->Deity;
|
|
eq_cse->Zone = emu_cse->Zone;
|
|
eq_cse->Unknown19 = emu_cse->Unknown19;
|
|
eq_cse->Race = emu_cse->Race;
|
|
eq_cse->GoHome = emu_cse->GoHome;
|
|
eq_cse->Class = emu_cse->Class;
|
|
eq_cse->EyeColor1 = emu_cse->EyeColor1;
|
|
eq_cse->BeardColor = emu_cse->BeardColor;
|
|
eq_cse->EyeColor2 = emu_cse->EyeColor2;
|
|
eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
|
|
eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
|
|
eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
|
|
|
|
emu_ptr += sizeof(CharacterSelectEntry_Struct);
|
|
eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
//hack hack hack
|
|
ENCODE(OP_SendZonepoints)
|
|
{
|
|
ENCODE_LENGTH_ATLEAST(ZonePoints);
|
|
|
|
SETUP_VAR_ENCODE(ZonePoints);
|
|
ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size);
|
|
|
|
memcpy(eq, emu, __packet->size);
|
|
|
|
FINISH_ENCODE();
|
|
// unknown0xxx[24];
|
|
//this is utter crap... the client is waiting for this
|
|
//certain 0 length opcode to come after the reqclientspawn
|
|
//stuff... so this is a dirty way to put it in there.
|
|
// this needs to be done better
|
|
|
|
//EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0);
|
|
//dest->QueuePacket(&hack_test);
|
|
}
|
|
|
|
ENCODE(OP_ShopPlayerSell)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct);
|
|
SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct);
|
|
|
|
OUT(npcid);
|
|
eq->itemslot = ServerToSoFSlot(emu->itemslot);
|
|
OUT(quantity);
|
|
OUT(price);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_SomeItemPacketMaybe)
|
|
{
|
|
// This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow
|
|
// and flying to the target.
|
|
//
|
|
|
|
ENCODE_LENGTH_EXACT(Arrow_Struct);
|
|
SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct);
|
|
|
|
OUT(src_y);
|
|
OUT(src_x);
|
|
OUT(src_z);
|
|
OUT(velocity);
|
|
OUT(launch_angle);
|
|
OUT(tilt);
|
|
OUT(arc);
|
|
OUT(source_id);
|
|
OUT(target_id);
|
|
OUT(item_id);
|
|
|
|
eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged.
|
|
|
|
OUT(item_type);
|
|
OUT(skill);
|
|
|
|
strcpy(eq->model_name, emu->model_name);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_SpawnDoor)
|
|
{
|
|
SETUP_VAR_ENCODE(Door_Struct);
|
|
|
|
int door_count = __packet->size / sizeof(Door_Struct);
|
|
int total_length = door_count * sizeof(structs::Door_Struct);
|
|
ALLOC_VAR_ENCODE(structs::Door_Struct, total_length);
|
|
int r;
|
|
for (r = 0; r < door_count; r++) {
|
|
strcpy(eq[r].name, emu[r].name);
|
|
eq[r].xPos = emu[r].xPos;
|
|
eq[r].yPos = emu[r].yPos;
|
|
eq[r].zPos = emu[r].zPos;
|
|
eq[r].heading = emu[r].heading;
|
|
eq[r].incline = emu[r].incline;
|
|
eq[r].size = emu[r].size;
|
|
eq[r].doorId = emu[r].doorId;
|
|
eq[r].opentype = emu[r].opentype;
|
|
eq[r].state_at_spawn = emu[r].state_at_spawn;
|
|
eq[r].invert_state = emu[r].invert_state;
|
|
eq[r].door_param = emu[r].door_param;
|
|
eq[r].unknown0076 = 0;
|
|
eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors
|
|
eq[r].unknown0078 = 0;
|
|
eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors
|
|
eq[r].unknown0080 = 0;
|
|
eq[r].unknown0081 = 0;
|
|
eq[r].unknown0082 = 0;
|
|
}
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
ServerToSoFSayLink(new_message, old_message);
|
|
|
|
buf.WriteString(new_message);
|
|
|
|
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
|
|
|
dest->FastQueuePacket(&outapp, ack_req);
|
|
delete in;
|
|
}
|
|
|
|
ENCODE(OP_Stun)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Stun_Struct);
|
|
SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct);
|
|
|
|
OUT(duration);
|
|
eq->unknown005 = 163;
|
|
eq->unknown006 = 67;
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_TaskDescription)
|
|
{
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
char *InBuffer = (char *)in->pBuffer;
|
|
char *block_start = InBuffer;
|
|
|
|
InBuffer += sizeof(TaskDescriptionHeader_Struct);
|
|
uint32 title_size = strlen(InBuffer) + 1;
|
|
InBuffer += title_size;
|
|
InBuffer += sizeof(TaskDescriptionData1_Struct);
|
|
uint32 description_size = strlen(InBuffer) + 1;
|
|
InBuffer += description_size;
|
|
InBuffer += sizeof(TaskDescriptionData2_Struct);
|
|
|
|
uint32 reward_size = strlen(InBuffer) + 1;
|
|
InBuffer += reward_size;
|
|
|
|
std::string old_message = InBuffer; // start item link string
|
|
std::string new_message;
|
|
ServerToSoFSayLink(new_message, old_message);
|
|
|
|
in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+
|
|
sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+
|
|
title_size + description_size + reward_size + new_message.length() + 1;
|
|
|
|
in->pBuffer = new unsigned char[in->size];
|
|
|
|
char *OutBuffer = (char *)in->pBuffer;
|
|
|
|
memcpy(OutBuffer, block_start, (InBuffer - block_start));
|
|
OutBuffer += (InBuffer - block_start);
|
|
|
|
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
|
|
|
InBuffer += strlen(InBuffer) + 1;
|
|
|
|
memcpy(OutBuffer, InBuffer, sizeof(TaskDescriptionTrailer_Struct));
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_Track)
|
|
{
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
Track_Struct *emu = (Track_Struct *)__emu_buffer;
|
|
|
|
int EntryCount = in->size / sizeof(Track_Struct);
|
|
|
|
if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0)
|
|
{
|
|
LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
|
|
in->size = sizeof(structs::Track_Struct) * EntryCount;
|
|
in->pBuffer = new unsigned char[in->size];
|
|
structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer;
|
|
|
|
for (int i = 0; i < EntryCount; ++i, ++eq, ++emu)
|
|
{
|
|
OUT(entityid);
|
|
//OUT(padding002);
|
|
OUT(distance);
|
|
}
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_Trader)
|
|
{
|
|
if ((*p)->size != sizeof(TraderBuy_Struct)) {
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
return;
|
|
}
|
|
|
|
ENCODE_FORWARD(OP_TraderBuy);
|
|
}
|
|
|
|
ENCODE(OP_TraderBuy)
|
|
{
|
|
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
|
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
|
|
|
OUT(Action);
|
|
OUT(Price);
|
|
OUT(TraderID);
|
|
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
|
OUT(ItemID);
|
|
OUT(Quantity);
|
|
OUT(AlreadySold);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_TributeItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(TributeItem_Struct);
|
|
SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct);
|
|
|
|
eq->slot = ServerToSoFSlot(emu->slot);
|
|
OUT(quantity);
|
|
OUT(tribute_master_id);
|
|
OUT(tribute_points);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_VetRewardsAvaliable)
|
|
{
|
|
EQApplicationPacket *inapp = *p;
|
|
unsigned char * __emu_buffer = inapp->pBuffer;
|
|
|
|
uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward));
|
|
*p = nullptr;
|
|
|
|
auto outapp_create =
|
|
new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward) * count));
|
|
uchar *old_data = __emu_buffer;
|
|
uchar *data = outapp_create->pBuffer;
|
|
for (uint32 i = 0; i < count; ++i)
|
|
{
|
|
structs::VeteranReward *vr = (structs::VeteranReward*)data;
|
|
InternalVeteranReward *ivr = (InternalVeteranReward*)old_data;
|
|
|
|
vr->claim_count = ivr->claim_count;
|
|
vr->claim_id = ivr->claim_id;
|
|
vr->number_available = ivr->number_available;
|
|
for (int x = 0; x < 8; ++x)
|
|
{
|
|
vr->items[x].item_id = ivr->items[x].item_id;
|
|
strcpy(vr->items[x].item_name, ivr->items[x].item_name);
|
|
vr->items[x].charges = ivr->items[x].charges;
|
|
}
|
|
|
|
old_data += sizeof(InternalVeteranReward);
|
|
data += sizeof(structs::VeteranReward);
|
|
}
|
|
|
|
dest->FastQueuePacket(&outapp_create);
|
|
delete inapp;
|
|
}
|
|
|
|
ENCODE(OP_WearChange)
|
|
{
|
|
ENCODE_LENGTH_EXACT(WearChange_Struct);
|
|
SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct);
|
|
|
|
OUT(spawn_id);
|
|
OUT(material);
|
|
OUT(unknown06);
|
|
OUT(elite_material);
|
|
OUT(color.Color);
|
|
OUT(wear_slot_id);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); }
|
|
|
|
ENCODE(OP_ZonePlayerToBind)
|
|
{
|
|
SETUP_VAR_ENCODE(ZonePlayerToBind_Struct);
|
|
ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name));
|
|
|
|
__packet->SetWritePosition(0);
|
|
__packet->WriteUInt16(emu->bind_zone_id);
|
|
__packet->WriteUInt16(emu->bind_instance_id);
|
|
__packet->WriteFloat(emu->x);
|
|
__packet->WriteFloat(emu->y);
|
|
__packet->WriteFloat(emu->z);
|
|
__packet->WriteFloat(emu->heading);
|
|
__packet->WriteString(emu->zone_name);
|
|
__packet->WriteUInt8(1); // save items
|
|
__packet->WriteUInt32(0); // hp
|
|
__packet->WriteUInt32(0); // mana
|
|
__packet->WriteUInt32(0); // endurance
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ZoneServerInfo)
|
|
{
|
|
SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct);
|
|
|
|
OUT_str(ip);
|
|
OUT(port);
|
|
|
|
FINISH_ENCODE();
|
|
|
|
//this is SUCH bullshit to be doing from down here. but the
|
|
// new client requires us to close immediately following this
|
|
// packet, so do it.
|
|
//dest->Close();
|
|
}
|
|
|
|
ENCODE(OP_ZoneSpawns)
|
|
{
|
|
//consume the packet
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer;
|
|
|
|
//determine and verify length
|
|
int entrycount = in->size / sizeof(Spawn_Struct);
|
|
if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) {
|
|
LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
|
|
//make the EQ struct.
|
|
in->size = sizeof(structs::Spawn_Struct)*entrycount;
|
|
in->pBuffer = new unsigned char[in->size];
|
|
structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer;
|
|
|
|
//zero out the packet. We could avoid this memset by setting all fields (including unknowns)
|
|
//in the loop.
|
|
memset(in->pBuffer, 0, in->size);
|
|
|
|
//do the transform...
|
|
int r;
|
|
int k;
|
|
for (r = 0; r < entrycount; r++, eq++, emu++) {
|
|
|
|
eq->showname = emu->show_name ? 1 : 0; //New Field - Toggles Name Display on or off - 0 = off, 1 = on
|
|
eq->linkdead = 0; //New Field - Toggles LD on or off after name - 0 = off, 1 = on
|
|
eq->statue = 0; //New Field - 1 freezes animation
|
|
eq->showhelm = emu->showhelm;
|
|
eq->deity = emu->deity;
|
|
eq->drakkin_heritage = emu->drakkin_heritage;
|
|
eq->gender = emu->gender;
|
|
for (k = EQ::textures::textureBegin; k < EQ::textures::materialCount; k++) {
|
|
eq->equipment.Slot[k].Material = emu->equipment.Slot[k].Material;
|
|
eq->equipment.Slot[k].Unknown1 = emu->equipment.Slot[k].Unknown1;
|
|
eq->equipment.Slot[k].EliteMaterial = emu->equipment.Slot[k].EliteModel;
|
|
eq->equipment_tint.Slot[k].Color = emu->equipment_tint.Slot[k].Color;
|
|
}
|
|
eq->StandState = emu->StandState;
|
|
eq->guildID = emu->guildID;
|
|
eq->spelleffect = 0;
|
|
eq->spelleffect2 = 0;
|
|
eq->spelleffect3 = 0;
|
|
eq->spelleffect4 = 0;
|
|
eq->spelleffect5 = 0;
|
|
eq->spelleffect6 = 0;
|
|
eq->class_ = emu->class_;
|
|
eq->flymode = emu->flymode;
|
|
eq->gm = emu->gm;
|
|
eq->helm = emu->helm;
|
|
eq->drakkin_tattoo = emu->drakkin_tattoo;
|
|
eq->beardcolor = emu->beardcolor;
|
|
eq->runspeed = emu->runspeed;
|
|
eq->light = emu->light;
|
|
eq->level = emu->level;
|
|
eq->PlayerState = emu->PlayerState;
|
|
eq->lfg = emu->lfg;
|
|
eq->hairstyle = emu->hairstyle;
|
|
eq->haircolor = emu->haircolor;
|
|
eq->race = emu->race;
|
|
strcpy(eq->suffix, emu->suffix);
|
|
eq->findable = emu->findable;
|
|
if (emu->bodytype >= 66)
|
|
{
|
|
eq->bodytype = 11; //non-targetable
|
|
eq->showname = 0; //no visible name
|
|
eq->race = 127; //invisible man
|
|
eq->gender = 0; //invisible men are gender 0
|
|
}
|
|
else
|
|
{
|
|
eq->bodytype = emu->bodytype;
|
|
}
|
|
//eq->bodytype2 = 0;
|
|
eq->equip_chest2 = emu->equip_chest2;
|
|
eq->curHp = emu->curHp;
|
|
eq->invis = emu->invis;
|
|
strcpy(eq->lastName, emu->lastName);
|
|
eq->eyecolor1 = emu->eyecolor1;
|
|
strcpy(eq->title, emu->title);
|
|
eq->beard = emu->beard;
|
|
eq->targetable = 1; //New Field - Toggle Targetable on or off - 0 = off, 1 = on
|
|
eq->NPC = emu->NPC;
|
|
eq->targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0; //New Field - Toggle Targetable on or off - 0 = off, 1 = on
|
|
eq->x = emu->x;
|
|
eq->deltaX = emu->deltaX;
|
|
eq->deltaY = emu->deltaY;
|
|
eq->z = emu->z;
|
|
eq->deltaHeading = emu->deltaHeading;
|
|
eq->y = emu->y;
|
|
eq->deltaZ = emu->deltaZ;
|
|
eq->animation = emu->animation;
|
|
eq->heading = emu->heading;
|
|
eq->spawnId = emu->spawnId;
|
|
eq->nonvisible = 0;
|
|
strcpy(eq->name, emu->name);
|
|
eq->petOwnerId = emu->petOwnerId;
|
|
eq->pvp = 0; // 0 = non-pvp colored name, 1 = red pvp name
|
|
for (k = EQ::textures::textureBegin; k < EQ::textures::materialCount; k++) {
|
|
eq->equipment_tint.Slot[k].Color = emu->equipment_tint.Slot[k].Color;
|
|
}
|
|
eq->anon = emu->anon;
|
|
eq->face = emu->face;
|
|
eq->drakkin_details = emu->drakkin_details;
|
|
eq->size = emu->size;
|
|
eq->walkspeed = emu->walkspeed;
|
|
/*
|
|
//Uncomment this section to use this hack test with NPC last names
|
|
//Hack Test for finding more fields in the Struct:
|
|
if (emu->lastName[0] == '*') // Test NPC!
|
|
{
|
|
char code = emu->lastName[1];
|
|
size_t len = strlen(emu->lastName);
|
|
char* sep = (char*)memchr(&emu->lastName[2], '=', len - 2);
|
|
|
|
uint32 ofs;
|
|
uint32 val;
|
|
uint8 rnd = rand() & 0x0F;
|
|
if (sep == nullptr)
|
|
{
|
|
ofs = 0;
|
|
if ((emu->lastName[2] < '0') || (emu->lastName[2] > '9'))
|
|
{
|
|
val = rnd;
|
|
}
|
|
else
|
|
{
|
|
val = Strings::ToInt(&emu->lastName[2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sep[0] = nullptr;
|
|
ofs = Strings::ToInt(&emu->lastName[2]);
|
|
sep[0] = '=';
|
|
if ((sep[1] < '0') || (sep[1] > '9'))
|
|
{
|
|
val = rnd;
|
|
}
|
|
else
|
|
{
|
|
val = Strings::ToInt(&sep[1]);
|
|
}
|
|
}
|
|
|
|
char hex[] = "0123456789ABCDEF";
|
|
|
|
eq->lastName[len + 0] = ' ';
|
|
eq->lastName[len + 1] = code;
|
|
eq->lastName[len + 2] = '0' + ((ofs / 1000) % 10);
|
|
eq->lastName[len + 3] = '0' + ((ofs / 100) % 10);
|
|
eq->lastName[len + 4] = '0' + ((ofs / 10) % 10);
|
|
eq->lastName[len + 5] = '0' + (ofs % 10);
|
|
eq->lastName[len + 6] = '=';
|
|
eq->lastName[len + 7] = '0' + ((val / 100) % 10);
|
|
eq->lastName[len + 8] = '0' + ((val / 10) % 10);
|
|
eq->lastName[len + 9] = '0' + (val % 10);
|
|
eq->lastName[len + 10] = 0x00;
|
|
|
|
switch (code)
|
|
{
|
|
case 'a':
|
|
eq->unknown0001[ofs % 4] = val; break;
|
|
case 'b':
|
|
eq->unknown0008 = val; break;
|
|
case 'c':
|
|
eq->unknown0011[ofs % 3] = val; break;
|
|
case 'd':
|
|
eq->unknown0018[ofs % 4] = val; break;
|
|
case 'e':
|
|
eq->unknown0023[ofs % 4] = val; break;
|
|
case 'f':
|
|
eq->unknown0136 = val; break;
|
|
case 'g':
|
|
eq->unknown0166[ofs % 8] = val; break;
|
|
case 'h':
|
|
eq->unknown0175[ofs % 192] = val; break;
|
|
case 'i':
|
|
eq->unknown0370[ofs % 3] = val; break;
|
|
case 'j':
|
|
eq->unknown0374[ofs % 128] = val; break;
|
|
case 'k':
|
|
eq->unknown0507[ofs % 4] = val; break;
|
|
case 'l':
|
|
eq->unknown0512[ofs % 16] = val; break;
|
|
case 'm':
|
|
eq->unknown0529[ofs % 4] = val; break;
|
|
case 'n':
|
|
eq->unknown0539[ofs % 41] = val; break;
|
|
case 'o':
|
|
eq->unknown0614[ofs % 11] = val; break;
|
|
case 'p':
|
|
eq->unknown0626[ofs % 28] = val; break;
|
|
case 'q':
|
|
eq->unknown0690 = val; break;
|
|
case 'r':
|
|
eq->unknown0726[ofs % 4] = val; break;
|
|
case 's':
|
|
eq->unknown0731[ofs % 11] = val; break;
|
|
case 't':
|
|
eq->unknown0767[ofs % 3] = val; break;
|
|
case 'u':
|
|
eq->unknown0883[ofs % 4] = val; break;
|
|
case 'v':
|
|
eq->unknown0895[ofs % 2] = val; break;
|
|
case 'X':
|
|
((uint8*)eq)[ofs % 897] = val; break;
|
|
case 'Z':
|
|
eq->size = (float)val; break; // Test w/ size.
|
|
}
|
|
}*/
|
|
}
|
|
|
|
//kill off the emu structure and send the eq packet.
|
|
delete[] __emu_buffer;
|
|
|
|
//Log.LogDebugType(Logs::General, Logs::Netcode, "[ERROR] Sending zone spawns");
|
|
//Log.Hex(Logs::Netcode, in->pBuffer, in->size);
|
|
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
// DECODE methods
|
|
DECODE(OP_AdventureMerchantSell)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct);
|
|
SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct);
|
|
|
|
IN(npcid);
|
|
emu->slot = SoFToServerSlot(eq->slot);
|
|
IN(charges);
|
|
IN(sell_price);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_AltCurrencySell)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct);
|
|
SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct);
|
|
|
|
IN(merchant_entity_id);
|
|
emu->slot_id = SoFToServerSlot(eq->slot_id);
|
|
IN(charges);
|
|
IN(cost);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_AltCurrencySellSelection)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct);
|
|
SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct);
|
|
|
|
IN(merchant_entity_id);
|
|
emu->slot_id = SoFToServerSlot(eq->slot_id);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ApplyPoison)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct);
|
|
SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct);
|
|
|
|
emu->inventorySlot = SoFToServerSlot(eq->inventorySlot);
|
|
IN(success);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_AugmentInfo)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct);
|
|
SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct);
|
|
|
|
IN(itemid);
|
|
IN(window);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_AugmentItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::AugmentItem_Struct);
|
|
SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct);
|
|
|
|
emu->container_slot = SoFToServerSlot(eq->container_slot);
|
|
emu->augment_slot = eq->augment_slot;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_BookButton)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::BookButton_Struct);
|
|
SETUP_DIRECT_DECODE(BookButton_Struct, structs::BookButton_Struct);
|
|
|
|
emu->invslot = static_cast<int16_t>(SoFToServerSlot(eq->invslot));
|
|
IN(target_id);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_Buff)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
|
|
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
|
|
|
IN(entityid);
|
|
IN(buff.effect_type);
|
|
IN(buff.level);
|
|
IN(buff.bard_modifier);
|
|
IN(buff.spellid);
|
|
IN(buff.duration);
|
|
IN(buff.counters);
|
|
IN(buff.player_id);
|
|
emu->slotid = SoFToServerBuffSlot(eq->slotid);
|
|
IN(bufffade);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_Bug)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::BugReport_Struct);
|
|
SETUP_DIRECT_DECODE(BugReport_Struct, structs::BugReport_Struct);
|
|
|
|
emu->category_id = EQ::bug::CategoryNameToCategoryID(eq->category_name);
|
|
memcpy(emu->category_name, eq, sizeof(structs::BugReport_Struct));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_CastSpell)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
|
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
|
|
|
emu->slot = static_cast<uint32>(SoFToServerCastingSlot(static_cast<spells::CastingSlot>(eq->slot), eq->inventoryslot));
|
|
IN(spell_id);
|
|
emu->inventoryslot = SoFToServerSlot(eq->inventoryslot);
|
|
IN(target_id);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ChannelMessage)
|
|
{
|
|
unsigned char *__eq_buffer = __packet->pBuffer;
|
|
|
|
std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)];
|
|
std::string new_message;
|
|
SoFToServerSayLink(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;
|
|
|
|
memcpy(emu, __eq_buffer, sizeof(ChannelMessage_Struct));
|
|
strcpy(emu->message, new_message.c_str());
|
|
|
|
delete[] __eq_buffer;
|
|
}
|
|
|
|
DECODE(OP_CharacterCreate)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::CharCreate_Struct);
|
|
SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct);
|
|
|
|
IN(class_);
|
|
IN(beardcolor);
|
|
IN(beard);
|
|
IN(hairstyle);
|
|
IN(gender);
|
|
IN(race);
|
|
IN(start_zone);
|
|
IN(haircolor);
|
|
IN(deity);
|
|
IN(STR);
|
|
IN(STA);
|
|
IN(AGI);
|
|
IN(DEX);
|
|
IN(WIS);
|
|
IN(INT);
|
|
IN(CHA);
|
|
IN(face);
|
|
IN(eyecolor1);
|
|
IN(eyecolor2);
|
|
IN(tutorial);
|
|
IN(drakkin_heritage);
|
|
IN(drakkin_tattoo);
|
|
IN(drakkin_details);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ClientUpdate)
|
|
{
|
|
// for some odd reason, there is an extra byte on the end of this on occasion..
|
|
DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct);
|
|
SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct);
|
|
|
|
IN(spawn_id);
|
|
IN(sequence);
|
|
IN(x_pos);
|
|
IN(y_pos);
|
|
IN(z_pos);
|
|
IN(heading);
|
|
IN(delta_x);
|
|
IN(delta_y);
|
|
IN(delta_z);
|
|
IN(delta_heading);
|
|
IN(animation);
|
|
emu->vehicle_id = 0;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_Consider)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Consider_Struct);
|
|
SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct);
|
|
|
|
IN(playerid);
|
|
IN(targetid);
|
|
IN(faction);
|
|
IN(level);
|
|
//emu->cur_hp = 1;
|
|
//emu->max_hp = 2;
|
|
//emu->pvpcon = 0;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); }
|
|
|
|
DECODE(OP_Consume)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Consume_Struct);
|
|
SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct);
|
|
|
|
emu->slot = SoFToServerSlot(eq->slot);
|
|
IN(auto_consumed);
|
|
IN(type);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_DeleteItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::DeleteItem_Struct);
|
|
SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct);
|
|
|
|
emu->from_slot = SoFToServerSlot(eq->from_slot);
|
|
emu->to_slot = SoFToServerSlot(eq->to_slot);
|
|
IN(number_in_stack);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_DzAddPlayer)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
|
|
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
|
|
|
|
strn0cpy(emu->name, eq->name, sizeof(emu->name));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_DzChooseZoneReply)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::DynamicZoneChooseZoneReply_Struct);
|
|
SETUP_DIRECT_DECODE(DynamicZoneChooseZoneReply_Struct, structs::DynamicZoneChooseZoneReply_Struct);
|
|
|
|
emu->unknown000 = eq->unknown000;
|
|
emu->unknown008 = eq->unknown004;
|
|
IN(unknown_id1);
|
|
IN(dz_zone_id);
|
|
IN(dz_instance_id);
|
|
IN(dz_type);
|
|
IN(unknown_id2);
|
|
emu->unknown028 = eq->unknown024;
|
|
emu->unknown032 = eq->unknown028;
|
|
emu->unknown036 = eq->unknown032;
|
|
emu->unknown040 = eq->unknown036;
|
|
emu->unknown044 = eq->unknown040;
|
|
emu->unknown048 = eq->unknown044;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_DzExpeditionInviteResponse)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ExpeditionInviteResponse_Struct);
|
|
SETUP_DIRECT_DECODE(ExpeditionInviteResponse_Struct, structs::ExpeditionInviteResponse_Struct);
|
|
|
|
IN(dz_zone_id);
|
|
IN(dz_instance_id);
|
|
IN(accepted);
|
|
IN(swapping);
|
|
strn0cpy(emu->swap_name, eq->swap_name, sizeof(emu->swap_name));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_DzMakeLeader)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
|
|
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
|
|
|
|
strn0cpy(emu->name, eq->name, sizeof(emu->name));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_DzRemovePlayer)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ExpeditionCommand_Struct);
|
|
SETUP_DIRECT_DECODE(ExpeditionCommand_Struct, structs::ExpeditionCommand_Struct);
|
|
|
|
strn0cpy(emu->name, eq->name, sizeof(emu->name));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_DzSwapPlayer)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ExpeditionCommandSwap_Struct);
|
|
SETUP_DIRECT_DECODE(ExpeditionCommandSwap_Struct, structs::ExpeditionCommandSwap_Struct);
|
|
|
|
strn0cpy(emu->add_player_name, eq->add_player_name, sizeof(emu->add_player_name));
|
|
strn0cpy(emu->rem_player_name, eq->rem_player_name, sizeof(emu->rem_player_name));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_Emote)
|
|
{
|
|
unsigned char *__eq_buffer = __packet->pBuffer;
|
|
|
|
std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset
|
|
std::string new_message;
|
|
SoFToServerSayLink(new_message, old_message);
|
|
|
|
__packet->size = sizeof(Emote_Struct);
|
|
__packet->pBuffer = new unsigned char[__packet->size];
|
|
|
|
char *InBuffer = (char *)__packet->pBuffer;
|
|
|
|
memcpy(InBuffer, __eq_buffer, 4);
|
|
InBuffer += 4;
|
|
strcpy(InBuffer, new_message.substr(0, 1023).c_str());
|
|
InBuffer[1023] = '\0';
|
|
|
|
delete[] __eq_buffer;
|
|
}
|
|
|
|
DECODE(OP_FaceChange)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::FaceChange_Struct);
|
|
SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct);
|
|
|
|
IN(haircolor);
|
|
IN(beardcolor);
|
|
IN(eyecolor1);
|
|
IN(eyecolor2);
|
|
IN(hairstyle);
|
|
IN(beard);
|
|
IN(face);
|
|
IN(drakkin_heritage);
|
|
IN(drakkin_tattoo);
|
|
IN(drakkin_details);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_FindPersonRequest)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct);
|
|
SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct);
|
|
|
|
IN(npc_id);
|
|
IN(client_pos.x);
|
|
IN(client_pos.y);
|
|
IN(client_pos.z);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_GroupFollow)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::GroupFollow_Struct);
|
|
SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct);
|
|
|
|
memcpy(emu->name1, eq->name1, sizeof(emu->name1));
|
|
memcpy(emu->name2, eq->name2, sizeof(emu->name2));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_GroupFollow2)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::GroupFollow_Struct);
|
|
SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct);
|
|
|
|
memcpy(emu->name1, eq->name1, sizeof(emu->name1));
|
|
memcpy(emu->name2, eq->name2, sizeof(emu->name2));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_InspectRequest)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Inspect_Struct);
|
|
SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct);
|
|
|
|
IN(TargetID);
|
|
IN(PlayerID);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ItemLinkClick)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct);
|
|
SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct);
|
|
|
|
MEMSET_IN(ItemViewRequest_Struct);
|
|
|
|
IN(item_id);
|
|
int r;
|
|
for (r = 0; r < 5; r++) {
|
|
IN(augments[r]);
|
|
}
|
|
IN(link_hash);
|
|
IN(icon);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ItemVerifyRequest)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct);
|
|
SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct);
|
|
|
|
emu->slot = SoFToServerSlot(eq->slot);
|
|
IN(target);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_LoadSpellSet)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct);
|
|
SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct);
|
|
|
|
for (int i = 0; i < spells::SPELL_GEM_COUNT; ++i)
|
|
IN(spell[i]);
|
|
for (int i = spells::SPELL_GEM_COUNT; i < EQ::spells::SPELL_GEM_COUNT; ++i)
|
|
emu->spell[i] = 0xFFFFFFFF;
|
|
|
|
IN(unknown);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_LootItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::LootingItem_Struct);
|
|
SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct);
|
|
|
|
Log(Logs::Detail, Logs::Netcode, "SoF::DECODE(OP_LootItem)");
|
|
|
|
IN(lootee);
|
|
IN(looter);
|
|
emu->slot_id = SoFToServerCorpseSlot(eq->slot_id);
|
|
IN(auto_loot);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_MoveItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::MoveItem_Struct);
|
|
SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct);
|
|
|
|
Log(Logs::Detail, Logs::Netcode, "SoF::DECODE(OP_MoveItem)");
|
|
|
|
emu->from_slot = SoFToServerSlot(eq->from_slot);
|
|
emu->to_slot = SoFToServerSlot(eq->to_slot);
|
|
IN(number_in_stack);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_PetCommands)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::PetCommand_Struct);
|
|
SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct);
|
|
|
|
switch (eq->command)
|
|
{
|
|
case 1: // back off
|
|
emu->command = 28;
|
|
break;
|
|
case 2: // get lost
|
|
emu->command = 29;
|
|
break;
|
|
case 3: // as you were ???
|
|
emu->command = 4; // fuck it follow
|
|
break;
|
|
case 4: // report HP
|
|
emu->command = 0;
|
|
break;
|
|
case 5: // guard here
|
|
emu->command = 5;
|
|
break;
|
|
case 6: // guard me
|
|
emu->command = 4; // fuck it follow
|
|
break;
|
|
case 7: // attack
|
|
emu->command = 2;
|
|
break;
|
|
case 8: // follow
|
|
emu->command = 4;
|
|
break;
|
|
case 9: // sit down
|
|
emu->command = 7;
|
|
break;
|
|
case 10: // stand up
|
|
emu->command = 8;
|
|
break;
|
|
case 11: // taunt toggle
|
|
emu->command = 12;
|
|
break;
|
|
case 12: // hold toggle
|
|
emu->command = 15;
|
|
break;
|
|
case 13: // taunt on
|
|
emu->command = 13;
|
|
break;
|
|
case 14: // no taunt
|
|
emu->command = 14;
|
|
break;
|
|
// 15 is target, doesn't send packet
|
|
case 16: // leader
|
|
emu->command = 1;
|
|
break;
|
|
case 17: // feign
|
|
emu->command = 27;
|
|
break;
|
|
case 18: // no cast toggle
|
|
emu->command = 21;
|
|
break;
|
|
case 19: // focus toggle
|
|
emu->command = 24;
|
|
break;
|
|
default:
|
|
emu->command = eq->command;
|
|
}
|
|
IN(target);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_RaidInvite)
|
|
{
|
|
DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct);
|
|
|
|
RaidGeneral_Struct* rgs = (RaidGeneral_Struct*)__packet->pBuffer;
|
|
|
|
switch (rgs->action)
|
|
{
|
|
case raidSetMotd:
|
|
{
|
|
SETUP_VAR_DECODE(RaidMOTD_Struct, structs::RaidMOTD_Struct, motd);
|
|
|
|
IN(general.action);
|
|
IN(general.parameter);
|
|
IN_str(general.leader_name);
|
|
IN_str(general.player_name);
|
|
IN_str(motd);
|
|
|
|
FINISH_VAR_DECODE();
|
|
break;
|
|
}
|
|
case raidSetNote:
|
|
{
|
|
SETUP_VAR_DECODE(RaidNote_Struct, structs::RaidNote_Struct, note);
|
|
|
|
IN(general.action);
|
|
IN(general.parameter);
|
|
IN_str(general.leader_name);
|
|
IN_str(general.player_name);
|
|
IN_str(note);
|
|
|
|
FINISH_VAR_DECODE();
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct);
|
|
IN(action);
|
|
IN(parameter);
|
|
IN_str(leader_name);
|
|
IN_str(player_name);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DECODE(OP_ReadBook)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::BookRequest_Struct);
|
|
SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct);
|
|
|
|
IN(type);
|
|
emu->invslot = static_cast<int16_t>(SoFToServerSlot(eq->invslot));
|
|
IN(target_id);
|
|
emu->window = (uint8)eq->window;
|
|
strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_Save)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Save_Struct);
|
|
SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct);
|
|
|
|
memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_SetServerFilter)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct);
|
|
SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct);
|
|
|
|
int r;
|
|
for (r = 0; r < 29; r++) {
|
|
IN(filters[r]);
|
|
}
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ShopPlayerSell)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct);
|
|
SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct);
|
|
|
|
IN(npcid);
|
|
emu->itemslot = SoFToServerSlot(eq->itemslot);
|
|
IN(quantity);
|
|
IN(price);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_TraderBuy)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
|
|
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
|
MEMSET_IN(TraderBuy_Struct);
|
|
|
|
IN(Action);
|
|
IN(Price);
|
|
IN(TraderID);
|
|
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
|
IN(ItemID);
|
|
IN(Quantity);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_TradeSkillCombine)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::NewCombine_Struct);
|
|
SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct);
|
|
|
|
emu->container_slot = SoFToServerSlot(eq->container_slot);
|
|
IN(guildtribute_slot);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_TributeItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::TributeItem_Struct);
|
|
SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct);
|
|
|
|
emu->slot = SoFToServerSlot(eq->slot);
|
|
IN(quantity);
|
|
IN(tribute_master_id);
|
|
IN(tribute_points);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_WearChange)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::WearChange_Struct);
|
|
SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct);
|
|
|
|
IN(spawn_id);
|
|
IN(material);
|
|
IN(unknown06);
|
|
IN(elite_material);
|
|
IN(color.Color);
|
|
IN(wear_slot_id);
|
|
emu->hero_forge_model = 0;
|
|
emu->unknown18 = 0;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_WhoAllRequest)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Who_All_Struct);
|
|
SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct);
|
|
|
|
memcpy(emu->whom, eq->whom, sizeof(emu->whom));
|
|
IN(wrace);
|
|
IN(wclass);
|
|
IN(lvllow);
|
|
IN(lvlhigh);
|
|
IN(gmlookup);
|
|
IN(guildid);
|
|
IN(type);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
// file scope helper methods
|
|
uint32 NextItemInstSerialNumber = 1;
|
|
uint32 MaxInstances = 2000000000;
|
|
|
|
static inline int32 GetNextItemInstSerialNumber()
|
|
{
|
|
if (NextItemInstSerialNumber >= MaxInstances)
|
|
NextItemInstSerialNumber = 1;
|
|
else
|
|
NextItemInstSerialNumber++;
|
|
|
|
return NextItemInstSerialNumber;
|
|
}
|
|
|
|
void SerializeItem(EQ::OutBuffer& ob, const EQ::ItemInstance *inst, int16 slot_id_in, uint8 depth)
|
|
{
|
|
const EQ::ItemData *item = inst->GetUnscaledItem();
|
|
|
|
SoF::structs::ItemSerializationHeader hdr;
|
|
|
|
hdr.stacksize = (inst->IsStackable() ? ((inst->GetCharges() > 254) ? 0xFFFFFFFF : inst->GetCharges()) : 1);
|
|
hdr.unknown004 = 0;
|
|
|
|
int32 slot_id = ServerToSoFSlot(slot_id_in);
|
|
|
|
hdr.slot = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : slot_id);
|
|
hdr.price = inst->GetPrice();
|
|
hdr.merchant_slot = (inst->GetMerchantSlot() ? inst->GetMerchantCount() : 1);
|
|
hdr.scaled_value = (inst->IsScaling() ? (inst->GetExp() / 100) : 0);
|
|
hdr.instance_id = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : inst->GetSerialNumber());
|
|
hdr.unknown028 = 0;
|
|
hdr.last_cast_time = inst->GetRecastTimestamp();
|
|
hdr.charges = (inst->IsStackable() ? (item->MaxCharges ? 1 : 0) : ((inst->GetCharges() > 254) ? 0xFFFFFFFF : inst->GetCharges()));
|
|
hdr.inst_nodrop = (inst->IsAttuned() ? 1 : 0);
|
|
hdr.unknown044 = 0;
|
|
hdr.unknown048 = 0;
|
|
hdr.unknown052 = 0;
|
|
hdr.unknown056 = 0;
|
|
hdr.unknown060 = 0;
|
|
hdr.unknown061 = 0;
|
|
hdr.ItemClass = item->ItemClass;
|
|
|
|
ob.write((const char*)&hdr, sizeof(SoF::structs::ItemSerializationHeader));
|
|
|
|
if (strlen(item->Name) > 0)
|
|
ob.write(item->Name, strlen(item->Name));
|
|
ob.write("\0", 1);
|
|
|
|
if (strlen(item->Lore) > 0)
|
|
ob.write(item->Lore, strlen(item->Lore));
|
|
ob.write("\0", 1);
|
|
|
|
if (strlen(item->IDFile) > 0)
|
|
ob.write(item->IDFile, strlen(item->IDFile));
|
|
ob.write("\0", 1);
|
|
|
|
SoF::structs::ItemBodyStruct ibs;
|
|
memset(&ibs, 0, sizeof(SoF::structs::ItemBodyStruct));
|
|
|
|
ibs.id = item->ID;
|
|
// weight is uint8 in the struct, and some weights exceed that, so capping at 255.
|
|
ibs.weight = ((item->Weight > 255) ? 255 : item->Weight);
|
|
ibs.norent = item->NoRent;
|
|
ibs.nodrop = item->NoDrop;
|
|
ibs.attune = item->Attuneable;
|
|
ibs.size = item->Size;
|
|
ibs.slots = item->Slots;
|
|
ibs.price = item->Price;
|
|
ibs.icon = item->Icon;
|
|
ibs.unknown1 = 1;
|
|
ibs.unknown2 = 1;
|
|
ibs.BenefitFlag = item->BenefitFlag;
|
|
ibs.tradeskills = item->Tradeskills;
|
|
ibs.CR = item->CR;
|
|
ibs.DR = item->DR;
|
|
ibs.PR = item->PR;
|
|
ibs.MR = item->MR;
|
|
ibs.FR = item->FR;
|
|
ibs.SVCorruption = item->SVCorruption;
|
|
ibs.AStr = item->AStr;
|
|
ibs.ASta = item->ASta;
|
|
ibs.AAgi = item->AAgi;
|
|
ibs.ADex = item->ADex;
|
|
ibs.ACha = item->ACha;
|
|
ibs.AInt = item->AInt;
|
|
ibs.AWis = item->AWis;
|
|
|
|
ibs.HP = item->HP;
|
|
ibs.Mana = item->Mana;
|
|
ibs.Endur = item->Endur;
|
|
ibs.AC = item->AC;
|
|
ibs.regen = item->Regen;
|
|
ibs.mana_regen = item->ManaRegen;
|
|
ibs.end_regen = item->EnduranceRegen;
|
|
ibs.Classes = item->Classes;
|
|
ibs.Races = item->Races;
|
|
ibs.Deity = item->Deity;
|
|
ibs.SkillModValue = item->SkillModValue;
|
|
ibs.SkillModMax = item->SkillModMax;
|
|
ibs.SkillModType = item->SkillModType;
|
|
ibs.BaneDmgRace = item->BaneDmgRace;
|
|
ibs.BaneDmgBody = item->BaneDmgBody;
|
|
ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt;
|
|
ibs.BaneDmgAmt = item->BaneDmgAmt;
|
|
ibs.Magic = item->Magic;
|
|
ibs.CastTime_ = item->CastTime_;
|
|
ibs.ReqLevel = item->ReqLevel;
|
|
ibs.RecLevel = item->RecLevel;
|
|
ibs.RecSkill = item->RecSkill;
|
|
ibs.BardType = item->BardType;
|
|
ibs.BardValue = item->BardValue;
|
|
ibs.Light = item->Light;
|
|
ibs.Delay = item->Delay;
|
|
ibs.ElemDmgType = item->ElemDmgType;
|
|
ibs.ElemDmgAmt = item->ElemDmgAmt;
|
|
ibs.Range = item->Range;
|
|
ibs.Damage = item->Damage;
|
|
ibs.Color = item->Color;
|
|
ibs.ItemType = item->ItemType;
|
|
ibs.Material = item->Material;
|
|
ibs.unknown7 = 0;
|
|
ibs.EliteMaterial = item->EliteMaterial;
|
|
ibs.SellRate = item->SellRate;
|
|
|
|
ibs.CombatEffects = item->CombatEffects;
|
|
ibs.Shielding = item->Shielding;
|
|
ibs.StunResist = item->StunResist;
|
|
ibs.StrikeThrough = item->StrikeThrough;
|
|
ibs.ExtraDmgSkill = item->ExtraDmgSkill;
|
|
ibs.ExtraDmgAmt = item->ExtraDmgAmt;
|
|
ibs.SpellShield = item->SpellShield;
|
|
ibs.Avoidance = item->Avoidance;
|
|
ibs.Accuracy = item->Accuracy;
|
|
ibs.CharmFileID = item->CharmFileID;
|
|
ibs.FactionAmt1 = item->FactionAmt1;
|
|
ibs.FactionMod1 = item->FactionMod1;
|
|
ibs.FactionAmt2 = item->FactionAmt2;
|
|
ibs.FactionMod2 = item->FactionMod2;
|
|
ibs.FactionAmt3 = item->FactionAmt3;
|
|
ibs.FactionMod3 = item->FactionMod3;
|
|
ibs.FactionAmt4 = item->FactionAmt4;
|
|
ibs.FactionMod4 = item->FactionMod4;
|
|
|
|
ob.write((const char*)&ibs, sizeof(SoF::structs::ItemBodyStruct));
|
|
|
|
//charm text
|
|
if (strlen(item->CharmFile) > 0)
|
|
ob.write((const char*)item->CharmFile, strlen(item->CharmFile));
|
|
ob.write("\0", 1);
|
|
|
|
SoF::structs::ItemSecondaryBodyStruct isbs;
|
|
memset(&isbs, 0, sizeof(SoF::structs::ItemSecondaryBodyStruct));
|
|
|
|
isbs.augtype = item->AugType;
|
|
isbs.augrestrict = item->AugRestrict;
|
|
|
|
for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) {
|
|
isbs.augslots[index].type = item->AugSlotType[index];
|
|
isbs.augslots[index].visible = item->AugSlotVisible[index];
|
|
isbs.augslots[index].unknown = item->AugSlotUnk2[index];
|
|
}
|
|
|
|
isbs.ldonpoint_type = item->PointType;
|
|
isbs.ldontheme = item->LDoNTheme;
|
|
isbs.ldonprice = item->LDoNPrice;
|
|
isbs.ldonsellbackrate = item->LDoNSellBackRate;
|
|
isbs.ldonsold = item->LDoNSold;
|
|
|
|
isbs.bagtype = item->BagType;
|
|
isbs.bagslots = item->BagSlots;
|
|
isbs.bagsize = item->BagSize;
|
|
isbs.wreduction = item->BagWR;
|
|
|
|
isbs.book = item->Book;
|
|
isbs.booktype = item->BookType;
|
|
|
|
ob.write((const char*)&isbs, sizeof(SoF::structs::ItemSecondaryBodyStruct));
|
|
|
|
if (strlen(item->Filename) > 0)
|
|
ob.write((const char*)item->Filename, strlen(item->Filename));
|
|
ob.write("\0", 1);
|
|
|
|
SoF::structs::ItemTertiaryBodyStruct itbs;
|
|
memset(&itbs, 0, sizeof(SoF::structs::ItemTertiaryBodyStruct));
|
|
|
|
itbs.loregroup = item->LoreGroup;
|
|
itbs.artifact = item->ArtifactFlag;
|
|
itbs.summonedflag = item->SummonedFlag;
|
|
itbs.favor = item->Favor;
|
|
itbs.fvnodrop = item->FVNoDrop;
|
|
itbs.dotshield = item->DotShielding;
|
|
itbs.atk = item->Attack;
|
|
itbs.haste = item->Haste;
|
|
itbs.damage_shield = item->DamageShield;
|
|
itbs.guildfavor = item->GuildFavor;
|
|
itbs.augdistil = item->AugDistiller;
|
|
itbs.no_pet = item->NoPet;
|
|
|
|
itbs.potion_belt_enabled = item->PotionBelt;
|
|
itbs.potion_belt_slots = item->PotionBeltSlots;
|
|
itbs.stacksize = (inst->IsStackable() ? item->StackSize : 0);
|
|
itbs.no_transfer = item->NoTransfer;
|
|
itbs.expendablearrow = item->ExpendableArrow;
|
|
|
|
// Done to hack older clients to label expendable fishing poles as such
|
|
// July 28th, 2018 patch
|
|
if (item->ItemType == EQ::item::ItemTypeFishingPole && item->SubType == 0) {
|
|
itbs.expendablearrow = 1;
|
|
}
|
|
|
|
ob.write((const char*)&itbs, sizeof(SoF::structs::ItemTertiaryBodyStruct));
|
|
|
|
// Effect Structures Broken down to allow variable length strings for effect names
|
|
int32 effect_unknown = 0;
|
|
|
|
SoF::structs::ClickEffectStruct ices;
|
|
memset(&ices, 0, sizeof(SoF::structs::ClickEffectStruct));
|
|
|
|
ices.effect = item->Click.Effect;
|
|
ices.level2 = item->Click.Level2;
|
|
ices.type = item->Click.Type;
|
|
ices.level = item->Click.Level;
|
|
ices.max_charges = item->MaxCharges;
|
|
ices.cast_time = item->CastTime;
|
|
ices.recast = item->RecastDelay;
|
|
ices.recast_type = item->RecastType;
|
|
|
|
ob.write((const char*)&ices, sizeof(SoF::structs::ClickEffectStruct));
|
|
|
|
if (strlen(item->ClickName) > 0)
|
|
ob.write((const char*)item->ClickName, strlen(item->ClickName));
|
|
ob.write("\0", 1);
|
|
|
|
ob.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7
|
|
|
|
SoF::structs::ProcEffectStruct ipes;
|
|
memset(&ipes, 0, sizeof(SoF::structs::ProcEffectStruct));
|
|
|
|
ipes.effect = item->Proc.Effect;
|
|
ipes.level2 = item->Proc.Level2;
|
|
ipes.type = item->Proc.Type;
|
|
ipes.level = item->Proc.Level;
|
|
ipes.procrate = item->ProcRate;
|
|
|
|
ob.write((const char*)&ipes, sizeof(SoF::structs::ProcEffectStruct));
|
|
|
|
if (strlen(item->ProcName) > 0)
|
|
ob.write((const char*)item->ProcName, strlen(item->ProcName));
|
|
ob.write("\0", 1);
|
|
|
|
ob.write((const char*)&effect_unknown, sizeof(int32)); // unknown5
|
|
|
|
SoF::structs::WornEffectStruct iwes;
|
|
memset(&iwes, 0, sizeof(SoF::structs::WornEffectStruct));
|
|
|
|
iwes.effect = item->Worn.Effect;
|
|
iwes.level2 = item->Worn.Level2;
|
|
iwes.type = item->Worn.Type;
|
|
iwes.level = item->Worn.Level;
|
|
|
|
ob.write((const char*)&iwes, sizeof(SoF::structs::WornEffectStruct));
|
|
|
|
if (strlen(item->WornName) > 0)
|
|
ob.write((const char*)item->WornName, strlen(item->WornName));
|
|
ob.write("\0", 1);
|
|
|
|
ob.write((const char*)&effect_unknown, sizeof(int32)); // unknown6
|
|
|
|
SoF::structs::WornEffectStruct ifes;
|
|
memset(&ifes, 0, sizeof(SoF::structs::WornEffectStruct));
|
|
|
|
ifes.effect = item->Focus.Effect;
|
|
ifes.level2 = item->Focus.Level2;
|
|
ifes.type = item->Focus.Type;
|
|
ifes.level = item->Focus.Level;
|
|
|
|
ob.write((const char*)&ifes, sizeof(SoF::structs::WornEffectStruct));
|
|
|
|
if (strlen(item->FocusName) > 0)
|
|
ob.write((const char*)item->FocusName, strlen(item->FocusName));
|
|
ob.write("\0", 1);
|
|
|
|
ob.write((const char*)&effect_unknown, sizeof(int32)); // unknown6
|
|
|
|
SoF::structs::WornEffectStruct ises;
|
|
memset(&ises, 0, sizeof(SoF::structs::WornEffectStruct));
|
|
|
|
ises.effect = item->Scroll.Effect;
|
|
ises.level2 = item->Scroll.Level2;
|
|
ises.type = item->Scroll.Type;
|
|
ises.level = item->Scroll.Level;
|
|
|
|
ob.write((const char*)&ises, sizeof(SoF::structs::WornEffectStruct));
|
|
|
|
if (strlen(item->ScrollName) > 0)
|
|
ob.write((const char*)item->ScrollName, strlen(item->ScrollName));
|
|
ob.write("\0", 1);
|
|
|
|
ob.write((const char*)&effect_unknown, sizeof(int32)); // unknown6
|
|
// End of Effects
|
|
|
|
SoF::structs::ItemQuaternaryBodyStruct iqbs;
|
|
memset(&iqbs, 0, sizeof(SoF::structs::ItemQuaternaryBodyStruct));
|
|
|
|
iqbs.scriptfileid = item->ScriptFileID;
|
|
iqbs.quest_item = item->QuestItemFlag;
|
|
iqbs.unknown15 = 0xffffffff;
|
|
|
|
iqbs.Purity = item->Purity;
|
|
iqbs.BackstabDmg = item->BackstabDmg;
|
|
iqbs.DSMitigation = item->DSMitigation;
|
|
iqbs.HeroicStr = item->HeroicStr;
|
|
iqbs.HeroicInt = item->HeroicInt;
|
|
iqbs.HeroicWis = item->HeroicWis;
|
|
iqbs.HeroicAgi = item->HeroicAgi;
|
|
iqbs.HeroicDex = item->HeroicDex;
|
|
iqbs.HeroicSta = item->HeroicSta;
|
|
iqbs.HeroicCha = item->HeroicCha;
|
|
iqbs.HeroicMR = item->HeroicMR;
|
|
iqbs.HeroicFR = item->HeroicFR;
|
|
iqbs.HeroicCR = item->HeroicCR;
|
|
iqbs.HeroicDR = item->HeroicDR;
|
|
iqbs.HeroicPR = item->HeroicPR;
|
|
iqbs.HeroicSVCorrup = item->HeroicSVCorrup;
|
|
iqbs.HealAmt = item->HealAmt;
|
|
iqbs.SpellDmg = item->SpellDmg;
|
|
|
|
ob.write((const char*)&iqbs, sizeof(SoF::structs::ItemQuaternaryBodyStruct));
|
|
|
|
EQ::OutBuffer::pos_type count_pos = ob.tellp();
|
|
uint32 subitem_count = 0;
|
|
|
|
ob.write((const char*)&subitem_count, sizeof(uint32));
|
|
|
|
// moved outside of loop since it is not modified within that scope
|
|
int16 SubSlotNumber = EQ::invbag::SLOT_INVALID;
|
|
|
|
if (slot_id_in <= EQ::invslot::slotGeneral8 && slot_id_in >= EQ::invslot::GENERAL_BEGIN)
|
|
SubSlotNumber = EQ::invbag::GENERAL_BAGS_BEGIN + ((slot_id_in - EQ::invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT);
|
|
else if (slot_id_in <= EQ::invslot::GENERAL_END && slot_id_in >= EQ::invslot::slotGeneral9)
|
|
SubSlotNumber = EQ::invbag::SLOT_INVALID;
|
|
else if (slot_id_in == EQ::invslot::slotCursor)
|
|
SubSlotNumber = EQ::invbag::CURSOR_BAG_BEGIN;
|
|
else if (slot_id_in <= EQ::invslot::BANK_END && slot_id_in >= EQ::invslot::BANK_BEGIN)
|
|
SubSlotNumber = EQ::invbag::BANK_BAGS_BEGIN + ((slot_id_in - EQ::invslot::BANK_BEGIN) * EQ::invbag::SLOT_COUNT);
|
|
else if (slot_id_in <= EQ::invslot::SHARED_BANK_END && slot_id_in >= EQ::invslot::SHARED_BANK_BEGIN)
|
|
SubSlotNumber = EQ::invbag::SHARED_BANK_BAGS_BEGIN + ((slot_id_in - EQ::invslot::SHARED_BANK_BEGIN) * EQ::invbag::SLOT_COUNT);
|
|
else
|
|
SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now
|
|
|
|
if (SubSlotNumber != EQ::invbag::SLOT_INVALID) {
|
|
for (uint32 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; ++index) {
|
|
EQ::ItemInstance* sub = inst->GetItem(index);
|
|
if (!sub)
|
|
continue;
|
|
|
|
ob.write((const char*)&index, sizeof(uint32));
|
|
|
|
SerializeItem(ob, sub, SubSlotNumber, (depth + 1));
|
|
++subitem_count;
|
|
}
|
|
|
|
if (subitem_count)
|
|
ob.overwrite(count_pos, (const char*)&subitem_count, sizeof(uint32));
|
|
}
|
|
}
|
|
|
|
static inline uint32 ServerToSoFSlot(uint32 server_slot)
|
|
{
|
|
uint32 sof_slot = invslot::SLOT_INVALID;
|
|
|
|
if (server_slot <= EQ::invslot::slotGeneral8) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invslot::CORPSE_END && server_slot >= EQ::invslot::slotCursor) {
|
|
sof_slot = server_slot - 2;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invbag::GENERAL_BAGS_8_END && server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
|
sof_slot = server_slot + 11;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invbag::CURSOR_BAG_END && server_slot >= EQ::invbag::CURSOR_BAG_BEGIN) {
|
|
sof_slot = server_slot - 9;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invslot::TRIBUTE_END && server_slot >= EQ::invslot::TRIBUTE_BEGIN) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invslot::GUILD_TRIBUTE_END && server_slot >= EQ::invslot::GUILD_TRIBUTE_BEGIN) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
else if (server_slot == EQ::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invslot::BANK_END && server_slot >= EQ::invslot::BANK_BEGIN) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invbag::BANK_BAGS_END && server_slot >= EQ::invbag::BANK_BAGS_BEGIN) {
|
|
sof_slot = server_slot + 1;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invslot::SHARED_BANK_END && server_slot >= EQ::invslot::SHARED_BANK_BEGIN) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invbag::SHARED_BANK_BAGS_END && server_slot >= EQ::invbag::SHARED_BANK_BAGS_BEGIN) {
|
|
sof_slot = server_slot + 1;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invslot::TRADE_END && server_slot >= EQ::invslot::TRADE_BEGIN) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invbag::TRADE_BAGS_END && server_slot >= EQ::invbag::TRADE_BAGS_BEGIN) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
else if (server_slot <= EQ::invslot::WORLD_END && server_slot >= EQ::invslot::WORLD_BEGIN) {
|
|
sof_slot = server_slot;
|
|
}
|
|
|
|
LogNetcode("Convert Server Slot [{}] to SoF Slot [{}]", server_slot, sof_slot);
|
|
|
|
return sof_slot;
|
|
}
|
|
|
|
static inline uint32 ServerToSoFCorpseSlot(uint32 server_corpse_slot)
|
|
{
|
|
uint32 SoFSlot = invslot::SLOT_INVALID;
|
|
|
|
if (server_corpse_slot <= EQ::invslot::slotGeneral8 && server_corpse_slot >= EQ::invslot::slotGeneral1) {
|
|
SoFSlot = server_corpse_slot;
|
|
}
|
|
|
|
else if (server_corpse_slot <= EQ::invslot::CORPSE_END && server_corpse_slot >= EQ::invslot::slotCursor) {
|
|
SoFSlot = server_corpse_slot - 2;
|
|
}
|
|
|
|
Log(Logs::Detail,
|
|
Logs::Netcode,
|
|
"Convert Server Corpse Slot %i to SoF Corpse Slot %i",
|
|
server_corpse_slot,
|
|
SoFSlot);
|
|
|
|
return SoFSlot;
|
|
}
|
|
|
|
static inline uint32 SoFToServerSlot(uint32 sof_slot)
|
|
{
|
|
uint32 server_slot = EQ::invslot::SLOT_INVALID;
|
|
|
|
if (sof_slot <= invslot::slotGeneral8) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
else if (sof_slot <= invslot::CORPSE_END && sof_slot >= invslot::slotCursor) {
|
|
server_slot = sof_slot + 2;
|
|
}
|
|
|
|
else if (sof_slot <= invbag::GENERAL_BAGS_END && sof_slot >= invbag::GENERAL_BAGS_BEGIN) {
|
|
server_slot = sof_slot - 11;
|
|
}
|
|
|
|
else if (sof_slot <= invbag::CURSOR_BAG_END && sof_slot >= invbag::CURSOR_BAG_BEGIN) {
|
|
server_slot = sof_slot + 9;
|
|
}
|
|
|
|
else if (sof_slot <= invslot::TRIBUTE_END && sof_slot >= invslot::TRIBUTE_BEGIN) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
else if (sof_slot <= invslot::GUILD_TRIBUTE_END && sof_slot >= invslot::GUILD_TRIBUTE_BEGIN) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
else if (sof_slot == invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
else if (sof_slot <= invslot::BANK_END && sof_slot >= invslot::BANK_BEGIN) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
else if (sof_slot <= invbag::BANK_BAGS_END && sof_slot >= invbag::BANK_BAGS_BEGIN) {
|
|
server_slot = sof_slot - 1;
|
|
}
|
|
|
|
else if (sof_slot <= invslot::SHARED_BANK_END && sof_slot >= invslot::SHARED_BANK_BEGIN) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
else if (sof_slot <= invbag::SHARED_BANK_BAGS_END && sof_slot >= invbag::SHARED_BANK_BAGS_BEGIN) {
|
|
server_slot = sof_slot - 1;
|
|
}
|
|
|
|
else if (sof_slot <= invslot::TRADE_END && sof_slot >= invslot::TRADE_BEGIN) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
else if (sof_slot <= invbag::TRADE_BAGS_END && sof_slot >= invbag::TRADE_BAGS_BEGIN) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
else if (sof_slot <= invslot::WORLD_END && sof_slot >= invslot::WORLD_BEGIN) {
|
|
server_slot = sof_slot;
|
|
}
|
|
|
|
LogNetcode("Convert SoF Slot [{}] to Server Slot [{}]", sof_slot, server_slot);
|
|
|
|
return server_slot;
|
|
}
|
|
|
|
static inline uint32 SoFToServerCorpseSlot(uint32 sof_corpse_slot)
|
|
{
|
|
uint32 server_slot = EQ::invslot::SLOT_INVALID;
|
|
|
|
if (sof_corpse_slot <= invslot::slotGeneral8 && sof_corpse_slot >= invslot::slotGeneral1) {
|
|
server_slot = sof_corpse_slot;
|
|
}
|
|
|
|
else if (sof_corpse_slot <= invslot::CORPSE_END && sof_corpse_slot >= invslot::slotCursor) {
|
|
server_slot = sof_corpse_slot + 2;
|
|
}
|
|
|
|
Log(Logs::Detail,
|
|
Logs::Netcode,
|
|
"Convert SoF Corpse Slot %i to Server Corpse Slot %i",
|
|
sof_corpse_slot,
|
|
server_slot);
|
|
|
|
return server_slot;
|
|
}
|
|
|
|
static inline void ServerToSoFSayLink(std::string &sof_saylink, const std::string &server_saylink)
|
|
{
|
|
if ((constants::SAY_LINK_BODY_SIZE == EQ::constants::SAY_LINK_BODY_SIZE) || (server_saylink.find('\x12') == std::string::npos)) {
|
|
sof_saylink = server_saylink;
|
|
return;
|
|
}
|
|
|
|
auto segments = Strings::Split(server_saylink, '\x12');
|
|
|
|
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
|
if (segment_iter & 1) {
|
|
if (segments[segment_iter].length() <= EQ::constants::SAY_LINK_BODY_SIZE) {
|
|
sof_saylink.append(segments[segment_iter]);
|
|
// TODO: log size mismatch error
|
|
continue;
|
|
}
|
|
|
|
// Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source)
|
|
// RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56)
|
|
// SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50)
|
|
// Diff: ^^^^^ ^
|
|
|
|
sof_saylink.push_back('\x12');
|
|
sof_saylink.append(segments[segment_iter].substr(0, 31));
|
|
sof_saylink.append(segments[segment_iter].substr(36, 5));
|
|
|
|
if (segments[segment_iter][41] == '0')
|
|
sof_saylink.push_back(segments[segment_iter][42]);
|
|
else
|
|
sof_saylink.push_back('F');
|
|
|
|
sof_saylink.append(segments[segment_iter].substr(43));
|
|
sof_saylink.push_back('\x12');
|
|
}
|
|
else {
|
|
sof_saylink.append(segments[segment_iter]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void SoFToServerSayLink(std::string &server_saylink, const std::string &sof_saylink)
|
|
{
|
|
if ((EQ::constants::SAY_LINK_BODY_SIZE == constants::SAY_LINK_BODY_SIZE) || (sof_saylink.find('\x12') == std::string::npos)) {
|
|
server_saylink = sof_saylink;
|
|
return;
|
|
}
|
|
|
|
auto segments = Strings::Split(sof_saylink, '\x12');
|
|
|
|
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
|
if (segment_iter & 1) {
|
|
if (segments[segment_iter].length() <= constants::SAY_LINK_BODY_SIZE) {
|
|
server_saylink.append(segments[segment_iter]);
|
|
// TODO: log size mismatch error
|
|
continue;
|
|
}
|
|
|
|
// Idx: 0 1 6 11 16 21 26 31 32 36 37 42 (Source)
|
|
// SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50)
|
|
// RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56)
|
|
// Diff: ^^^^^ ^
|
|
|
|
server_saylink.push_back('\x12');
|
|
server_saylink.append(segments[segment_iter].substr(0, 31));
|
|
server_saylink.append("00000");
|
|
server_saylink.append(segments[segment_iter].substr(31, 5));
|
|
server_saylink.push_back('0');
|
|
server_saylink.append(segments[segment_iter].substr(36));
|
|
server_saylink.push_back('\x12');
|
|
}
|
|
else {
|
|
server_saylink.append(segments[segment_iter]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline spells::CastingSlot ServerToSoFCastingSlot(EQ::spells::CastingSlot slot) {
|
|
switch (slot) {
|
|
case EQ::spells::CastingSlot::Gem1:
|
|
return spells::CastingSlot::Gem1;
|
|
case EQ::spells::CastingSlot::Gem2:
|
|
return spells::CastingSlot::Gem2;
|
|
case EQ::spells::CastingSlot::Gem3:
|
|
return spells::CastingSlot::Gem3;
|
|
case EQ::spells::CastingSlot::Gem4:
|
|
return spells::CastingSlot::Gem4;
|
|
case EQ::spells::CastingSlot::Gem5:
|
|
return spells::CastingSlot::Gem5;
|
|
case EQ::spells::CastingSlot::Gem6:
|
|
return spells::CastingSlot::Gem6;
|
|
case EQ::spells::CastingSlot::Gem7:
|
|
return spells::CastingSlot::Gem7;
|
|
case EQ::spells::CastingSlot::Gem8:
|
|
return spells::CastingSlot::Gem8;
|
|
case EQ::spells::CastingSlot::Gem9:
|
|
return spells::CastingSlot::Gem9;
|
|
case EQ::spells::CastingSlot::Item:
|
|
return spells::CastingSlot::Item;
|
|
case EQ::spells::CastingSlot::PotionBelt:
|
|
return spells::CastingSlot::PotionBelt;
|
|
case EQ::spells::CastingSlot::Discipline:
|
|
return spells::CastingSlot::Discipline;
|
|
case EQ::spells::CastingSlot::AltAbility:
|
|
return spells::CastingSlot::AltAbility;
|
|
default: // we shouldn't have any issues with other slots ... just return something
|
|
return spells::CastingSlot::Discipline;
|
|
}
|
|
}
|
|
|
|
static inline EQ::spells::CastingSlot SoFToServerCastingSlot(spells::CastingSlot slot, uint32 item_location) {
|
|
switch (slot) {
|
|
case spells::CastingSlot::Gem1:
|
|
return EQ::spells::CastingSlot::Gem1;
|
|
case spells::CastingSlot::Gem2:
|
|
return EQ::spells::CastingSlot::Gem2;
|
|
case spells::CastingSlot::Gem3:
|
|
return EQ::spells::CastingSlot::Gem3;
|
|
case spells::CastingSlot::Gem4:
|
|
return EQ::spells::CastingSlot::Gem4;
|
|
case spells::CastingSlot::Gem5:
|
|
return EQ::spells::CastingSlot::Gem5;
|
|
case spells::CastingSlot::Gem6:
|
|
return EQ::spells::CastingSlot::Gem6;
|
|
case spells::CastingSlot::Gem7:
|
|
return EQ::spells::CastingSlot::Gem7;
|
|
case spells::CastingSlot::Gem8:
|
|
return EQ::spells::CastingSlot::Gem8;
|
|
case spells::CastingSlot::Gem9:
|
|
return EQ::spells::CastingSlot::Gem9;
|
|
case spells::CastingSlot::Ability:
|
|
return EQ::spells::CastingSlot::Ability;
|
|
// Tit uses 10 for item and discipline casting, but items have a valid location
|
|
case spells::CastingSlot::Item:
|
|
if (item_location == INVALID_INDEX)
|
|
return EQ::spells::CastingSlot::Discipline;
|
|
else
|
|
return EQ::spells::CastingSlot::Item;
|
|
case spells::CastingSlot::PotionBelt:
|
|
return EQ::spells::CastingSlot::PotionBelt;
|
|
case spells::CastingSlot::AltAbility:
|
|
return EQ::spells::CastingSlot::AltAbility;
|
|
default: // we shouldn't have any issues with other slots ... just return something
|
|
return EQ::spells::CastingSlot::Discipline;
|
|
}
|
|
}
|
|
|
|
static inline int ServerToSoFBuffSlot(int index) {
|
|
// we're a disc
|
|
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
|
|
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
|
|
spells::LONG_BUFFS + spells::SHORT_BUFFS;
|
|
// we're a song
|
|
if (index >= EQ::spells::LONG_BUFFS)
|
|
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
|
|
// we're a normal buff
|
|
return index; // as long as we guard against bad slots server side, we should be fine
|
|
}
|
|
|
|
static inline int SoFToServerBuffSlot(int index)
|
|
{
|
|
// we're a disc
|
|
if (index >= spells::LONG_BUFFS + spells::SHORT_BUFFS)
|
|
return index - spells::LONG_BUFFS - spells::SHORT_BUFFS + EQ::spells::LONG_BUFFS +
|
|
EQ::spells::SHORT_BUFFS;
|
|
// we're a song
|
|
if (index >= spells::LONG_BUFFS)
|
|
return index - spells::LONG_BUFFS + EQ::spells::LONG_BUFFS;
|
|
// we're a normal buff
|
|
return index; // as long as we guard against bad slots server side, we should be fine
|
|
}
|
|
}
|