mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-17 07:18:37 +00:00
2dacb523fc
Hopefully resolves issues with toons on Titanium spawning out of bounds when starting in tutorial. Resolves setting home bind to tutorial and being out of bounds.
1341 lines
33 KiB
C++
1341 lines
33 KiB
C++
#include "../debug.h"
|
|
#include "client62.h"
|
|
#include "../opcodemgr.h"
|
|
#include "../logsys.h"
|
|
#include "../eq_stream_ident.h"
|
|
#include "../crc32.h"
|
|
|
|
#include "../eq_packet_structs.h"
|
|
#include "../misc_functions.h"
|
|
#include "../string_util.h"
|
|
#include "../item.h"
|
|
#include "../clientversions.h"
|
|
#include "client62_structs.h"
|
|
|
|
namespace Client62
|
|
{
|
|
static const char *name = "6.2";
|
|
static OpcodeManager *opcodes = nullptr;
|
|
static Strategy struct_strategy;
|
|
|
|
char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth);
|
|
|
|
// server to client inventory location converters
|
|
static inline int16 ServerToClient62Slot(uint32 ServerSlot);
|
|
static inline int16 ServerToClient62CorpseSlot(uint32 ServerCorpse);
|
|
|
|
// client to server inventory location converters
|
|
static inline uint32 Client62ToServerSlot(int16 Client62Slot);
|
|
static inline uint32 Client62ToServerCorpseSlot(int16 Client62Corpse);
|
|
|
|
void Register(EQStreamIdentifier &into)
|
|
{
|
|
//create our opcode manager if we havent already
|
|
if (opcodes == nullptr) {
|
|
//TODO: get this file name from the config file
|
|
std::string opfile = "patch_";
|
|
opfile += name;
|
|
opfile += ".conf";
|
|
//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())) {
|
|
_log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//ok, now we have what we need to register.
|
|
|
|
EQStream::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);
|
|
|
|
_log(NET__IDENTIFY, "Registered patch %s", 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) {
|
|
//TODO: get this file name from the config file
|
|
std::string opfile = "patch_";
|
|
opfile += name;
|
|
opfile += ".conf";
|
|
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
|
_log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name);
|
|
return;
|
|
}
|
|
_log(NET__OPCODES, "Reloaded opcodes for patch %s", name);
|
|
}
|
|
}
|
|
|
|
Strategy::Strategy() : StructStrategy()
|
|
{
|
|
//all opcodes default to passthrough.
|
|
#include "ss_register.h"
|
|
#include "client62_ops.h"
|
|
}
|
|
|
|
std::string Strategy::Describe() const
|
|
{
|
|
std::string r;
|
|
r += "Patch ";
|
|
r += name;
|
|
return(r);
|
|
}
|
|
|
|
const EQClientVersion Strategy::ClientVersion() const
|
|
{
|
|
return EQClient62;
|
|
}
|
|
|
|
#include "ss_define.h"
|
|
|
|
// ENCODE methods
|
|
EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ;
|
|
|
|
EAT_ENCODE(OP_ZoneServerReady); // added ;
|
|
|
|
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(sequence);
|
|
OUT(type);
|
|
//OUT(damage);
|
|
OUT(spell);
|
|
OUT(buff_unknown); // 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 = ServerToClient62Slot(emu->slot);
|
|
OUT(charges);
|
|
OUT(sell_price);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ApplyPoison)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ApplyPoison_Struct);
|
|
SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct);
|
|
|
|
eq->inventorySlot = ServerToClient62Slot(emu->inventorySlot);
|
|
OUT(success);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_BazaarSearch)
|
|
{
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
char *Buffer = (char *)in->pBuffer;
|
|
|
|
uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
|
|
|
|
if (SubAction != BazaarSearchResults)
|
|
{
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
|
|
return;
|
|
}
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer;
|
|
|
|
int EntryCount = in->size / sizeof(BazaarSearchResults_Struct);
|
|
|
|
if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0)
|
|
{
|
|
_log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct);
|
|
|
|
in->pBuffer = new unsigned char[in->size];
|
|
|
|
memset(in->pBuffer, 0, in->size);
|
|
|
|
structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer;
|
|
|
|
for (int i = 0; i < EntryCount; ++i, ++emu, ++eq)
|
|
{
|
|
OUT(Beginning.Action);
|
|
OUT(NumItems);
|
|
OUT(SerialNumber);
|
|
OUT(SellerID);
|
|
OUT(Cost);
|
|
OUT(ItemStat);
|
|
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->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_CharInventory)
|
|
{
|
|
//consume the packet
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
int itemcount = in->size / sizeof(InternalSerializedItem_Struct);
|
|
if (itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) {
|
|
_log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer;
|
|
|
|
//do the transform...
|
|
int r;
|
|
std::string serial_string;
|
|
for (r = 0; r < itemcount; r++, eq++) {
|
|
uint32 length;
|
|
char *serialized = SerializeItem((ItemInst*)eq->inst, eq->slot_id, &length, 0);
|
|
if (serialized) {
|
|
serial_string.append(serialized, length + 1);
|
|
safe_delete_array(serialized);
|
|
}
|
|
else {
|
|
_log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id);
|
|
}
|
|
|
|
}
|
|
|
|
in->size = serial_string.length();
|
|
in->pBuffer = new unsigned char[in->size];
|
|
memcpy(in->pBuffer, serial_string.c_str(), serial_string.length());
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
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 = ServerToClient62Slot(emu->from_slot);
|
|
eq->to_slot = ServerToClient62Slot(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_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);
|
|
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);
|
|
|
|
if (emu->race > 473)
|
|
eq->race = 1;
|
|
else
|
|
OUT(race);
|
|
|
|
OUT(gender);
|
|
OUT(texture);
|
|
OUT(helmtexture);
|
|
OUT(face);
|
|
OUT(hairstyle);
|
|
OUT(haircolor);
|
|
OUT(beard);
|
|
OUT(beardcolor);
|
|
OUT(size);
|
|
|
|
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
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer;
|
|
InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem);
|
|
|
|
uint32 length;
|
|
char *serialized = SerializeItem((const ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0);
|
|
|
|
if (!serialized) {
|
|
_log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id);
|
|
delete in;
|
|
return;
|
|
}
|
|
in->size = length + 5; // ItemPacketType + Serialization + \0
|
|
in->pBuffer = new unsigned char[in->size];
|
|
ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer;
|
|
new_item_pkt->PacketType = old_item_pkt->PacketType;
|
|
memcpy(new_item_pkt->SerializedItem, serialized, length + 1);
|
|
|
|
delete[] __emu_buffer;
|
|
safe_delete_array(serialized);
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
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_LootItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(LootingItem_Struct);
|
|
SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct);
|
|
|
|
OUT(lootee);
|
|
OUT(looter);
|
|
eq->slot_id = ServerToClient62CorpseSlot(emu->slot_id);
|
|
OUT(auto_loot);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_MoveItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(MoveItem_Struct);
|
|
SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct);
|
|
|
|
eq->from_slot = ServerToClient62Slot(emu->from_slot);
|
|
eq->to_slot = ServerToClient62Slot(emu->to_slot);
|
|
OUT(number_in_stack);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); }
|
|
|
|
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;
|
|
|
|
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < 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;
|
|
|
|
memset(eq->unknown3224, 0xff, 448);
|
|
memset(eq->unknown3704, 0xff, 32);
|
|
|
|
// OUT(checksum);
|
|
OUT(gender);
|
|
OUT(race);
|
|
OUT(class_);
|
|
OUT(level);
|
|
eq->level2 = emu->level;
|
|
|
|
eq->bind_zone_id = emu->binds[0].zoneId;
|
|
eq->bind_x[0] = emu->binds[0].x;
|
|
eq->bind_y[0] = emu->binds[0].y;
|
|
eq->bind_z[0] = emu->binds[0].z;
|
|
eq->bind_heading[0] = emu->binds[0].heading;
|
|
//just making this up base on organization of struct:
|
|
eq->zone_safe_x = emu->binds[4].x;
|
|
eq->zone_safe_y = emu->binds[4].y;
|
|
eq->zone_safe_z = emu->binds[4].z;
|
|
eq->zone_safe_heading = emu->binds[4].heading;
|
|
|
|
OUT(deity);
|
|
OUT(intoxication);
|
|
OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL);
|
|
OUT(abilitySlotRefresh);
|
|
// OUT(unknown0166[4]);
|
|
OUT(haircolor);
|
|
OUT(beardcolor);
|
|
OUT(eyecolor1);
|
|
OUT(eyecolor2);
|
|
OUT(hairstyle);
|
|
OUT(beard);
|
|
for (r = 0; r < 9; r++) {
|
|
OUT(item_material[r]);
|
|
OUT(item_tint[r].color);
|
|
}
|
|
for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) {
|
|
OUT(aa_array[r].AA);
|
|
OUT(aa_array[r].value);
|
|
}
|
|
OUT(points);
|
|
OUT(mana);
|
|
OUT(cur_hp);
|
|
OUT(STR);
|
|
OUT(STA);
|
|
OUT(CHA);
|
|
OUT(DEX);
|
|
OUT(INT);
|
|
OUT(AGI);
|
|
OUT(WIS);
|
|
OUT(face);
|
|
OUT_array(spell_book, structs::MAX_PP_SPELLBOOK);
|
|
OUT_array(mem_spells, structs::MAX_PP_MEMSPELL);
|
|
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(toxicity);
|
|
OUT(thirst_level);
|
|
OUT(hunger_level);
|
|
for (r = 0; r < structs::BUFF_COUNT; r++) {
|
|
OUT(buffs[r].slotid);
|
|
OUT(buffs[r].level);
|
|
OUT(buffs[r].bard_modifier);
|
|
OUT(buffs[r].effect);
|
|
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(endurance);
|
|
OUT(aapoints_spent);
|
|
OUT(aapoints);
|
|
for (r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) {
|
|
OUT_str(bandoliers[r].name);
|
|
uint32 k;
|
|
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) {
|
|
OUT(bandoliers[r].items[k].item_id);
|
|
OUT(bandoliers[r].items[k].icon);
|
|
OUT_str(bandoliers[r].items[k].item_name);
|
|
}
|
|
}
|
|
for (r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) {
|
|
OUT(potionbelt.items[r].item_id);
|
|
OUT(potionbelt.items[r].icon);
|
|
OUT_str(potionbelt.items[r].item_name);
|
|
}
|
|
// OUT(available_slots);
|
|
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(exp);
|
|
OUT_array(languages, structs::MAX_PP_LANGUAGE);
|
|
OUT(x);
|
|
OUT(y);
|
|
OUT(z);
|
|
OUT(heading);
|
|
OUT(platinum_bank);
|
|
OUT(gold_bank);
|
|
OUT(silver_bank);
|
|
OUT(copper_bank);
|
|
OUT(platinum_shared);
|
|
OUT(expansions);
|
|
OUT(autosplit);
|
|
OUT(zone_id);
|
|
OUT(zoneInstance);
|
|
for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) {
|
|
OUT_str(groupMembers[r]);
|
|
}
|
|
// OUT_str(groupLeader); //this is prolly right after groupMembers, but I dont feel like checking.
|
|
// OUT(leadAAActive);
|
|
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(tribute_time_remaining);
|
|
OUT(career_tribute_points);
|
|
OUT(tribute_points);
|
|
OUT(tribute_active);
|
|
for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) {
|
|
OUT(tributes[r].tribute);
|
|
OUT(tributes[r].tier);
|
|
}
|
|
OUT(group_leadership_exp);
|
|
OUT(raid_leadership_exp);
|
|
OUT(group_leadership_points);
|
|
OUT(raid_leadership_points);
|
|
OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY);
|
|
OUT(air_remaining);
|
|
OUT(entityid);
|
|
OUT(leadAAActive);
|
|
OUT(expAA);
|
|
OUT(currentRadCrystals);
|
|
OUT(careerRadCrystals);
|
|
OUT(currentEbonCrystals);
|
|
OUT(careerEbonCrystals);
|
|
OUT(groupAutoconsent);
|
|
OUT(raidAutoconsent);
|
|
OUT(guildAutoconsent);
|
|
// OUT(showhelm);
|
|
|
|
//set the checksum...
|
|
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ReadBook)
|
|
{
|
|
// no apparent slot translation needed -U
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer;
|
|
|
|
in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext);
|
|
|
|
in->pBuffer = new unsigned char[in->size];
|
|
|
|
structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer;
|
|
|
|
eq_BookText_Struct->window = emu_BookText_Struct->window;
|
|
eq_BookText_Struct->type = emu_BookText_Struct->type;
|
|
strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext);
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_RespondAA)
|
|
{
|
|
ENCODE_LENGTH_EXACT(AATable_Struct);
|
|
SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct);
|
|
|
|
unsigned int r;
|
|
for (r = 0; r < MAX_PP_AA_ARRAY; r++) {
|
|
OUT(aa_list[r].aa_skill);
|
|
OUT(aa_list[r].aa_value);
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_SendAATable)
|
|
{
|
|
ENCODE_LENGTH_ATLEAST(SendAA_Struct);
|
|
|
|
SETUP_VAR_ENCODE(SendAA_Struct);
|
|
ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability));
|
|
|
|
// Check clientver field to verify this AA should be sent for SoF
|
|
// clientver 1 is for all clients and 2 is for 6.2
|
|
if (emu->clientver <= 2)
|
|
{
|
|
OUT(id);
|
|
OUT(hotkey_sid);
|
|
OUT(hotkey_sid2);
|
|
OUT(title_sid);
|
|
OUT(desc_sid);
|
|
OUT(class_type);
|
|
OUT(cost);
|
|
OUT(seq);
|
|
OUT(current_level);
|
|
OUT(prereq_skill);
|
|
OUT(prereq_minpoints);
|
|
OUT(type);
|
|
OUT(spellid);
|
|
OUT(spell_type);
|
|
OUT(spell_refresh);
|
|
OUT(classes);
|
|
OUT(berserker);
|
|
OUT(max_level);
|
|
OUT(last_id);
|
|
OUT(next_id);
|
|
OUT(cost2);
|
|
OUT(unknown80[0]);
|
|
OUT(unknown80[1]);
|
|
OUT(total_abilities);
|
|
unsigned int r;
|
|
for (r = 0; r < emu->total_abilities; r++) {
|
|
OUT(abilities[r].skill_id);
|
|
OUT(abilities[r].base1);
|
|
OUT(abilities[r].base2);
|
|
OUT(abilities[r].slot);
|
|
}
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_SendCharInfo)
|
|
{
|
|
ENCODE_LENGTH_EXACT(CharacterSelect_Struct);
|
|
SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct);
|
|
|
|
int r;
|
|
for (r = 0; r < 10; r++) {
|
|
OUT(zone[r]);
|
|
OUT(eyecolor1[r]);
|
|
OUT(eyecolor2[r]);
|
|
OUT(hairstyle[r]);
|
|
OUT(primary[r]);
|
|
OUT(race[r]);
|
|
OUT(class_[r]);
|
|
OUT_str(name[r]);
|
|
OUT(gender[r]);
|
|
OUT(level[r]);
|
|
OUT(secondary[r]);
|
|
OUT(face[r]);
|
|
OUT(beard[r]);
|
|
int k;
|
|
for (k = 0; k < 9; k++) {
|
|
OUT(equip[r][k]);
|
|
OUT(cs_colors[r][k].color);
|
|
}
|
|
OUT(haircolor[r]);
|
|
OUT(gohome[r]);
|
|
OUT(deity[r]);
|
|
OUT(beardcolor[r]);
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ShopPlayerSell)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct);
|
|
SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct);
|
|
|
|
OUT(npcid);
|
|
eq->itemslot = ServerToClient62Slot(emu->itemslot);
|
|
OUT(quantity);
|
|
OUT(price);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
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)
|
|
{
|
|
_log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", 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_TributeItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(TributeItem_Struct);
|
|
SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct);
|
|
|
|
eq->slot = ServerToClient62Slot(emu->slot);
|
|
OUT(quantity);
|
|
OUT(tribute_master_id);
|
|
OUT(tribute_points);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_WearChange)
|
|
{
|
|
ENCODE_LENGTH_EXACT(WearChange_Struct);
|
|
SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct);
|
|
|
|
OUT(spawn_id);
|
|
OUT(material);
|
|
OUT(color.color);
|
|
OUT(wear_slot_id);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); }
|
|
|
|
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) {
|
|
_log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", 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->gm = emu->gm;
|
|
eq->aaitle = emu->aaitle;
|
|
eq->anon = emu->anon;
|
|
eq->face = emu->face;
|
|
strcpy(eq->name, emu->name);
|
|
eq->deity = emu->deity;
|
|
eq->size = emu->size;
|
|
eq->NPC = emu->NPC;
|
|
eq->invis = emu->invis;
|
|
eq->haircolor = emu->haircolor;
|
|
eq->curHp = emu->curHp;
|
|
eq->max_hp = emu->max_hp;
|
|
eq->findable = emu->findable;
|
|
eq->deltaHeading = emu->deltaHeading;
|
|
eq->x = emu->x;
|
|
eq->y = emu->y;
|
|
eq->animation = emu->animation;
|
|
eq->z = emu->z;
|
|
eq->deltaY = emu->deltaY;
|
|
eq->deltaX = emu->deltaX;
|
|
eq->heading = emu->heading;
|
|
eq->deltaZ = emu->deltaZ;
|
|
eq->eyecolor1 = emu->eyecolor1;
|
|
// eq->showhelm = emu->showhelm;
|
|
eq->is_npc = emu->is_npc;
|
|
eq->hairstyle = emu->hairstyle;
|
|
eq->beard = emu->beard;
|
|
eq->level = emu->level;
|
|
eq->beardcolor = emu->beardcolor;
|
|
strcpy(eq->suffix, emu->suffix);
|
|
eq->petOwnerId = emu->petOwnerId;
|
|
eq->guildrank = emu->guildrank;
|
|
for (k = 0; k < 9; k++) {
|
|
eq->equipment[k] = emu->equipment[k];
|
|
eq->colors[k].color = emu->colors[k].color;
|
|
}
|
|
for (k = 0; k < 8; k++) {
|
|
eq->set_to_0xFF[k] = 0xFF;
|
|
}
|
|
eq->runspeed = emu->runspeed;
|
|
eq->afk = emu->afk;
|
|
eq->guildID = emu->guildID;
|
|
strcpy(eq->title, emu->title);
|
|
eq->helm = emu->helm;
|
|
eq->race = emu->race;
|
|
strcpy(eq->lastName, emu->lastName);
|
|
eq->walkspeed = emu->walkspeed;
|
|
eq->is_pet = emu->is_pet;
|
|
eq->light = emu->light;
|
|
eq->class_ = emu->class_;
|
|
eq->eyecolor2 = emu->eyecolor2;
|
|
eq->gender = emu->gender;
|
|
eq->bodytype = emu->bodytype;
|
|
eq->equip_chest2 = emu->equip_chest2;
|
|
eq->spawnId = emu->spawnId;
|
|
eq->lfg = emu->lfg;
|
|
eq->flymode = emu->flymode;
|
|
}
|
|
|
|
//kill off the emu structure and send the eq packet.
|
|
delete[] __emu_buffer;
|
|
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 = Client62ToServerSlot(eq->slot);
|
|
IN(charges);
|
|
IN(sell_price);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ApplyPoison)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct);
|
|
SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct);
|
|
|
|
emu->inventorySlot = Client62ToServerSlot(eq->inventorySlot);
|
|
IN(success);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_AugmentItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::AugmentItem_Struct);
|
|
SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct);
|
|
|
|
emu->container_slot = Client62ToServerSlot(eq->container_slot);
|
|
emu->augment_slot = eq->augment_slot;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_CastSpell)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
|
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
|
|
|
IN(slot);
|
|
IN(spell_id);
|
|
emu->inventoryslot = Client62ToServerSlot(eq->inventoryslot);
|
|
IN(target_id);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_CharacterCreate)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::CharCreate_Struct);
|
|
SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct);
|
|
|
|
IN(class_);
|
|
IN(beardcolor);
|
|
IN(beard);
|
|
IN(haircolor);
|
|
IN(gender);
|
|
IN(race);
|
|
IN(start_zone);
|
|
IN(hairstyle);
|
|
IN(deity);
|
|
IN(STR);
|
|
IN(STA);
|
|
IN(AGI);
|
|
IN(DEX);
|
|
IN(WIS);
|
|
IN(INT);
|
|
IN(CHA);
|
|
IN(face);
|
|
IN(eyecolor1);
|
|
IN(eyecolor2);
|
|
IN(tutorial);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_Consume)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Consume_Struct);
|
|
SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct);
|
|
|
|
emu->slot = Client62ToServerSlot(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 = Client62ToServerSlot(eq->from_slot);
|
|
emu->to_slot = Client62ToServerSlot(eq->to_slot);
|
|
IN(number_in_stack);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_LootItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::LootingItem_Struct);
|
|
SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct);
|
|
|
|
IN(lootee);
|
|
IN(looter);
|
|
emu->slot_id = Client62ToServerCorpseSlot(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(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot);
|
|
|
|
emu->from_slot = Client62ToServerSlot(eq->from_slot);
|
|
emu->to_slot = Client62ToServerSlot(eq->to_slot);
|
|
IN(number_in_stack);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ReadBook)
|
|
{
|
|
// no apparent slot translation needed -U
|
|
DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct);
|
|
SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct);
|
|
|
|
IN(window);
|
|
IN(type);
|
|
strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile));
|
|
|
|
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 < 25; r++) {
|
|
IN(filters[r]);
|
|
}
|
|
emu->filters[25] = 1;
|
|
emu->filters[26] = 1;
|
|
emu->filters[27] = 1;
|
|
emu->filters[28] = 1;
|
|
|
|
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 = Client62ToServerSlot(eq->itemslot);
|
|
IN(quantity);
|
|
IN(price);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_TradeSkillCombine)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::NewCombine_Struct);
|
|
SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct);
|
|
|
|
emu->container_slot = Client62ToServerSlot(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 = Client62ToServerSlot(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(color.color);
|
|
IN(wear_slot_id);
|
|
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);
|
|
emu->type = 3;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
// file scope helper methods
|
|
char *SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth)
|
|
{
|
|
char *serialization = nullptr;
|
|
char *instance = nullptr;
|
|
const char *protection = (const char *)"\\\\\\\\\\";
|
|
char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
|
|
bool stackable = inst->IsStackable();
|
|
int16 slot_id = ServerToClient62Slot(slot_id_in);
|
|
uint32 merchant_slot = inst->GetMerchantSlot();
|
|
int16 charges = inst->GetCharges();
|
|
const Item_Struct *item = inst->GetItem();
|
|
int i;
|
|
uint32 sub_length;
|
|
|
|
// not sure if 6.2 has a recast timer timestamp field..but, something seems amiss between this and Ti's ordering
|
|
MakeAnyLenString(&instance,
|
|
"%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|",
|
|
stackable ? charges : 1,
|
|
0,
|
|
//(merchant_slot == 0) ? slot_id : merchant_slot, // change when translator activated
|
|
(merchant_slot == 0) ? slot_id_in : merchant_slot,
|
|
inst->GetPrice(),
|
|
(merchant_slot == 0) ? 1 : inst->GetMerchantCount(),
|
|
0,
|
|
//merchant_slot, //instance ID, bullshit for now
|
|
// The 'Merchant Slot' needs to be some unique id for bazaar to work properly
|
|
(merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot,
|
|
inst->IsInstNoDrop() ? 1 : 0, //not sure where this field is
|
|
(stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? charges : 0) : charges),
|
|
0
|
|
);
|
|
|
|
for (i = 0; i<10; i++) {
|
|
ItemInst *sub = inst->GetItem(i);
|
|
if (sub) {
|
|
sub_items[i] = SerializeItem(sub, 0, &sub_length, depth + 1);
|
|
}
|
|
}
|
|
|
|
|
|
*length = MakeAnyLenString(&serialization,
|
|
"%.*s%s" // For leading quotes (and protection) if a subitem;
|
|
"%s" // Instance data
|
|
"%.*s\"" // Quotes (and protection, if needed) around static data
|
|
"%i" // item->ItemClass so we can do |%s instead of %s|
|
|
#define I(field) "|%i"
|
|
#define C(field) "|%s"
|
|
#define S(field) "|%s"
|
|
#define F(field) "|%f"
|
|
#include "client62_itemfields.h"
|
|
"%.*s\"" // Quotes (and protection, if needed) around static data
|
|
"|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items
|
|
"%.*s%s" // For trailing quotes (and protection) if a subitem;
|
|
, depth ? depth - 1 : 0, protection, (depth) ? "\"" : ""
|
|
, instance
|
|
, depth, protection
|
|
, item->ItemClass
|
|
#define I(field) ,item->field
|
|
#define C(field) ,field
|
|
#define S(field) ,item->field
|
|
#define F(field) ,item->field
|
|
#include "client62_itemfields.h"
|
|
, depth, protection
|
|
, sub_items[0] ? sub_items[0] : ""
|
|
, sub_items[1] ? sub_items[1] : ""
|
|
, sub_items[2] ? sub_items[2] : ""
|
|
, sub_items[3] ? sub_items[3] : ""
|
|
, sub_items[4] ? sub_items[4] : ""
|
|
, sub_items[5] ? sub_items[5] : ""
|
|
, sub_items[6] ? sub_items[6] : ""
|
|
, sub_items[7] ? sub_items[7] : ""
|
|
, sub_items[8] ? sub_items[8] : ""
|
|
, sub_items[9] ? sub_items[9] : ""
|
|
, (depth) ? depth - 1 : 0, protection, (depth) ? "\"" : ""
|
|
);
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
if (sub_items[i])
|
|
safe_delete_array(sub_items[i]);
|
|
}
|
|
|
|
safe_delete_array(instance);
|
|
|
|
return serialization;
|
|
}
|
|
|
|
static inline int16 ServerToClient62Slot(uint32 ServerSlot)
|
|
{
|
|
//int16 Client62Slot;
|
|
if (ServerSlot == INVALID_INDEX)
|
|
return INVALID_INDEX;
|
|
|
|
return ServerSlot; // deprecated
|
|
}
|
|
|
|
static inline int16 ServerToClient62CorpseSlot(uint32 ServerCorpse)
|
|
{
|
|
//int16 Client62Corpse;
|
|
return ServerCorpse;
|
|
}
|
|
|
|
static inline uint32 Client62ToServerSlot(int16 Client62Slot)
|
|
{
|
|
//uint32 ServerSlot;
|
|
if (Client62Slot == INVALID_INDEX)
|
|
return INVALID_INDEX;
|
|
|
|
return Client62Slot; // deprecated
|
|
}
|
|
|
|
static inline uint32 Client62ToServerCorpseSlot(int16 Client62Corpse)
|
|
{
|
|
//uint32 ServerCorpse;
|
|
return Client62Corpse;
|
|
}
|
|
}
|
|
// end namespace Client62
|