2024-11-07 23:03:38 -08:00

864 lines
25 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 "larion.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 "../inventory_profile.h"
#include "larion_structs.h"
#include "../rulesys.h"
#include "../path_manager.h"
#include "../classes.h"
#include "../races.h"
#include "../raid.h"
#include <iostream>
#include <sstream>
#include <numeric>
#include <cassert>
#include <cinttypes>
namespace Larion
{
static const char* name = "Larion";
static OpcodeManager* opcodes = nullptr;
static Strategy struct_strategy;
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 "larion_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::Larion;
}
#include "ss_define.h"
// ENCODE methods
ENCODE(OP_LogServer) {
SETUP_VAR_ENCODE(LogServer_Struct);
ALLOC_LEN_ENCODE(1840);
//pvp
if (emu->enable_pvp) {
*(char*)&__packet->pBuffer[0x04] = 1;
}
if (emu->enable_FV) {
//FV sets these both to 1
//one appears to enable the no drop flag the other just marks the server as special?
*(char*)&__packet->pBuffer[0x08] = 1;
*(char*)&__packet->pBuffer[0x0a] = 1;
}
//This has something to do with heirloom and prestige items but im not sure what it does
//Seems to sit at 0
*(char*)&__packet->pBuffer[0x71d] = 0;
//not sure what this does, something to do with server select
*(char*)&__packet->pBuffer[0x09] = 0;
//this appears to have some effect on the tradeskill system; disabling made by tags perhaps?
*(char*)&__packet->pBuffer[0x0b] = 0;
//not sure, setting it to the value ive seen
*(char*)&__packet->pBuffer[0x0c] = 1;
//Something to do with languages
*(char*)&__packet->pBuffer[0x0d] = 1;
//These seem to affect if server has betabuff enabled
*(char*)&__packet->pBuffer[0x5c0] = 0;
*(char*)&__packet->pBuffer[0x5c1] = 0;
//This is set on test so it's probably indicating this is a test server
*(char*)&__packet->pBuffer[0x5c2] = 0;
//not sure, but it's grouped with the beta and test stuff
*(char*)&__packet->pBuffer[0x5c3] = 0;
//world short name
strncpy((char*)&__packet->pBuffer[0x15], emu->worldshortname, 32);
//not sure, affects some player calculation but didn't care to look more
*(char*)&__packet->pBuffer[0x5c2] = 0;
//Looks right
if (emu->enablemail) {
*(char*)&__packet->pBuffer[0x5b5] = 1;
}
//Looks right
if (emu->enablevoicemacros) {
*(char*)&__packet->pBuffer[0x5b4] = 1;
}
//Not sure, sending what we've seen
*(char*)&__packet->pBuffer[0x5b6] = 0;
//Not sure sending what we've seen
*(char*)&__packet->pBuffer[0x5b8] = 1;
//Not sure sending what we've seen
*(int32_t*)&__packet->pBuffer[0x5fc] = -1;
//Test sets this to 1, everyone else seems to set it to 0
*(int32_t*)&__packet->pBuffer[0x600] = 0;
//Disassembly puts it next to code dealing with commands, ive not seen anyone send anything but 0
*(char*)&__packet->pBuffer[0x705] = 0;
//Something about item restrictions, seems to always be set to 1
*(char*)&__packet->pBuffer[0x710] = 0;
//This and 0x724 are often multiplied together in guild favor calcs, live and test send 1.0f
*(float*)&__packet->pBuffer[0x720] = 1.0f;
*(float*)&__packet->pBuffer[0x724] = 1.0f;
//This and 0x72c are often multiplied together in non-guild favor calcs, live and test send 1.0f
*(float*)&__packet->pBuffer[0x728] = 1.0f;
*(float*)&__packet->pBuffer[0x72c] = 1.0f;
FINISH_ENCODE();
}
ENCODE(OP_SendMembership) {
ENCODE_LENGTH_EXACT(Membership_Struct);
SETUP_DIRECT_ENCODE(Membership_Struct, structs::Membership_Struct);
eq->membership = emu->membership;
eq->races = emu->races;
eq->classes = emu->classes;
eq->entrysize = 33;
eq->entries[0] = -1; // Max AA Restriction
eq->entries[1] = -1; // Max Level Restriction
eq->entries[2] = -1; // Max Char Slots per Account (not used by client?)
eq->entries[3] = -1; // 1 for Silver
eq->entries[4] = -1; // Main Inventory Size
eq->entries[5] = -1; // Max Platinum per level
eq->entries[6] = 1; // Send Mail
eq->entries[7] = 1; // Use Parcels?
eq->entries[8] = 1; // Voice Chat
eq->entries[9] = -1; // Merc Tiers
eq->entries[10] = 1; // Create Guilds
eq->entries[11] = -1; // Shared Bank Slots
eq->entries[12] = -1; // Max Journal Quests
eq->entries[13] = 1; // Housing Enabled
eq->entries[14] = 1; // Prestiege
eq->entries[15] = 1; // Broker System
eq->entries[16] = 1; // Chat
eq->entries[17] = 1; // Progression Server Access
eq->entries[18] = 1; // Customer Support
eq->entries[19] = -1; // Popup reminders?
eq->entries[20] = -1; // Exit Popup?
eq->entries[21] = 0;
eq->entries[22] = 0;
eq->entries[23] = 0; // This is the highest we actually see in detail entries
eq->entries[24] = 0;
eq->entries[25] = 0;
eq->entries[26] = 0;
eq->entries[27] = 0;
eq->entries[28] = 0;
eq->entries[29] = 0;
eq->entries[30] = 0;
eq->entries[31] = 0;
eq->entries[32] = 0;
FINISH_ENCODE();
}
ENCODE(OP_SendMembershipDetails) {
ENCODE_LENGTH_EXACT(Membership_Details_Struct);
SETUP_DIRECT_ENCODE(Membership_Details_Struct, structs::Membership_Details_Struct);
int32 settings[96][3] = {
{ 0, 0, 250 }, { 1, 0, 1000 }, { 0, 1, -1 }, { 1, 1, -1 },
{ 0, 2, 2 }, { 2, 0, -1 }, { 3, 0, -1 }, { 1, 2, 4 },
{ 0, 3, 1 }, { 2, 1, -1 }, { 3, 1, -1 }, { 1, 3, 1 },
{ 0, 4, -1 }, { 2, 2, -1 }, { 3, 2, -1 }, { 1, 4, -1 },
{ 0, 5, -1 }, { 2, 3, -1 }, { 3, 3, -1 }, { 1, 5, -1 },
{ 0, 6, 0 }, { 2, 4, -1 }, { 3, 4, -1 }, { 1, 6, 0 },
{ 0, 7, 1 }, { 2, 5, -1 }, { 3, 5, -1 }, { 1, 7, 1 },
{ 0, 8, 1 }, { 2, 6, 1 }, { 3, 6, 1 }, { 1, 8, 1 },
{ 0, 9, 5 }, { 2, 7, 1 }, { 3, 7, 1 }, { 1, 9, 5 },
{ 0, 10, 0 }, { 2, 8, 1 }, { 3, 8, 1 }, { 0, 11, -1 },
{ 1, 10, 1 }, { 2, 9, -1 }, { 3, 9, -1 }, { 0, 12, -1 },
{ 1, 11, -1 }, { 2, 10, 1 }, { 3, 10, 1 }, { 0, 13, 0 },
{ 1, 12, -1 }, { 2, 11, -1 }, { 3, 11, -1 }, { 0, 14, 0 },
{ 1, 13, 1 }, { 2, 12, -1 }, { 3, 12, -1 }, { 0, 15, 0 },
{ 1, 14, 0 }, { 2, 13, 1 }, { 3, 13, 1 }, { 0, 16, 0 },
{ 1, 15, 0 }, { 2, 14, 1 }, { 3, 14, 1 }, { 0, 17, 0 },
{ 1, 16, 1 }, { 2, 15, 1 }, { 3, 15, 1 }, { 0, 18, 0 },
{ 1, 17, 0 }, { 2, 16, 1 }, { 3, 16, 1 }, { 0, 19, 0 },
{ 1, 18, 0 }, { 2, 17, 1 }, { 3, 17, 1 }, { 0, 20, 0 },
{ 1, 19, 0 }, { 2, 18, 1 }, { 3, 18, 1 }, { 0, 21, 0 },
{ 1, 20, 0 }, { 2, 19, -1 }, { 3, 19, -1 }, { 0, 22, 0 },
{ 1, 21, 0 }, { 2, 20, -1 }, { 3, 20, -1 }, { 2, 21, 0 },
{ 0, 23, 0 }, { 1, 22, 0 }, { 3, 21, 0 }, { 2, 22, 0 },
{ 1, 23, 0 }, { 3, 22, 0 }, { 2, 23, 0 }, { 3, 23, 0 }
};
uint32 races[17][2] = {
{ 1, 131071 },
{ 333, 131071 },
{ 90287, 131071 },
{ 90289, 16 },
{ 90290, 32 },
{ 90291, 64 },
{ 90292, 128 },
{ 90293, 256 },
{ 90294, 512 },
{ 90295, 1024 },
{ 90296, 2048 },
{ 90297, 8192 },
{ 90298, 16384 },
{ 90299, 32768 },
{ 90300, 65536 },
{ 2012271, 131071 },
{ 2012277, 131071 }
};
uint32 classes[17][2] = {
{ 1, 131071 },
{ 333, 131071 },
{ 90287, 131071 },
{ 90301, 8 },
{ 90302, 16 },
{ 90303, 32 },
{ 90304, 64 },
{ 90305, 128 },
{ 90306, 256 },
{ 90307, 1024 },
{ 90308, 2048 },
{ 90309, 8192 },
{ 90310, 16384 },
{ 90311, 32768 },
{ 90312, 65536 },
{ 2012271, 131071 },
{ 2012277, 131071 }
};
eq->membership_setting_count = 96;
for (int i = 0; i < 96; ++i) {
eq->settings[i].setting_index = (int8)settings[i][0];
eq->settings[i].setting_id = settings[i][1];
eq->settings[i].setting_value = settings[i][2];
}
eq->class_entry_count = 17;
for (int i = 0; i < 17; ++i) {
eq->membership_classes[i].purchase_id = classes[i][0];
eq->membership_classes[i].bitwise_entry = classes[i][1];
}
eq->race_entry_count = 17;
for (int i = 0; i < 17; ++i) {
eq->membership_races[i].purchase_id = races[i][0];
eq->membership_races[i].bitwise_entry = races[i][1];
}
eq->exit_url_length = 0;
FINISH_ENCODE();
}
ENCODE(OP_SendMaxCharacters) {
ENCODE_LENGTH_EXACT(MaxCharacters_Struct);
SETUP_DIRECT_ENCODE(MaxCharacters_Struct, structs::MaxCharacters_Struct);
//OUT(max_chars);
eq->max_chars = 8; //needs to be fixed
eq->marketplace_chars = 0;
eq->unknown008 = -1;
eq->unknown00c = 196608;
eq->unknown010 = 0;
eq->unknown014 = 0;
eq->unknown018 = 0;
eq->unknown01c = 0;
eq->unknown020 = -1;
eq->unknown024 = 0;
eq->unknown028 = 0;
eq->unknown02c = 0;
eq->unknown030 = 0;
eq->unknown034 = 0;
FINISH_ENCODE();
}
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;
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) {
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;
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
strcpy(eq_cse->Name, emu_cse->Name);
eq_ptr += strlen(emu_cse->Name);
eq_cse = (structs::CharacterSelectEntry_Struct*)eq_ptr;
eq_cse->Name[0] = '\0';
eq_cse->Class = emu_cse->Class;
eq_cse->Race = emu_cse->Race;
eq_cse->Level = emu_cse->Level;
eq_cse->ShroudClass = emu_cse->ShroudClass;
eq_cse->ShroudRace = emu_cse->ShroudRace;
eq_cse->Zone = emu_cse->Zone;
eq_cse->Instance = emu_cse->Instance;
eq_cse->Gender = emu_cse->Gender;
eq_cse->Face = emu_cse->Face;
for (int equip_index = 0; 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].HeroForgeModel = emu_cse->Equip[equip_index].HerosForgeModel;
eq_cse->Equip[equip_index].Material2 = emu_cse->Equip[equip_index].Unknown2;
eq_cse->Equip[equip_index].Color = emu_cse->Equip[equip_index].Color;
}
eq_cse->Unknown1 = 255;
eq_cse->Unknown2 = 0;
eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
eq_cse->Deity = emu_cse->Deity;
eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
eq_cse->HairColor = emu_cse->HairColor;
eq_cse->BeardColor = emu_cse->BeardColor;
eq_cse->EyeColor1 = emu_cse->EyeColor1;
eq_cse->EyeColor2 = emu_cse->EyeColor2;
eq_cse->HairStyle = emu_cse->HairStyle;
eq_cse->Beard = emu_cse->Beard;
eq_cse->GoHome = emu_cse->GoHome;
eq_cse->Tutorial = emu_cse->Tutorial;
eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
eq_cse->Enabled = emu_cse->Enabled;
eq_cse->LastLogin = emu_cse->LastLogin;
eq_cse->Unknown3 = 0;
eq_cse->Unknown4 = 0;
eq_cse->Unknown5 = 0;
eq_cse->Unknown6 = 0;
eq_cse->Unknown7 = 0;
eq_cse->CharacterId = 0;
eq_cse->Unknown8 = 1;
emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
}
DumpPacket(__packet);
FINISH_ENCODE();
}
ENCODE(OP_ExpansionInfo)
{
ENCODE_LENGTH_EXACT(ExpansionInfo_Struct);
SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct);
OUT(Expansions);
FINISH_ENCODE();
}
ENCODE(OP_SpawnAppearance)
{
EQApplicationPacket* in = *p;
*p = nullptr;
unsigned char* emu_buffer = in->pBuffer;
SpawnAppearance_Struct* sas = (SpawnAppearance_Struct*)emu_buffer;
if (sas->type != AppearanceType::Size)
{
//larion struct is different than rof2's but the idea is the same
auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct));
structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer;
eq->spawn_id = sas->spawn_id;
eq->type = sas->type;
eq->parameter = sas->parameter;
dest->FastQueuePacket(&outapp, ack_req);
delete in;
return;
}
auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct));
ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer;
css->EntityID = sas->spawn_id;
css->Size = (float)sas->parameter;
css->Unknown08 = 0;
css->Unknown12 = 1.0f;
dest->FastQueuePacket(&outapp, ack_req);
delete in;
}
ENCODE(OP_PlayerProfile) {
EQApplicationPacket* in = *p;
*p = nullptr;
unsigned char* __emu_buffer = in->pBuffer;
PlayerProfile_Struct* emu = (PlayerProfile_Struct*)__emu_buffer;
SerializeBuffer buffer;
buffer.WriteUInt32(0); //crc
buffer.WriteUInt32(0); //size;
buffer.WriteUInt32(0); //profile_type;
buffer.WriteUInt32(0); //profile_id;
buffer.WriteUInt32(0); //shroud_template_id;
buffer.WriteUInt8(emu->gender);
buffer.WriteUInt32(emu->race);
buffer.WriteUInt32(emu->class_);
buffer.WriteUInt8(emu->level);
buffer.WriteUInt8(emu->level2);
buffer.WriteUInt32(5); //bind_count
for (int r = 0; r < 5; r++) //binds
{
buffer.WriteUInt32(emu->binds[r].zone_id);
buffer.WriteFloat(emu->binds[r].x);
buffer.WriteFloat(emu->binds[r].y);
buffer.WriteFloat(emu->binds[r].z);
buffer.WriteFloat(emu->binds[r].heading);
}
buffer.WriteUInt32(emu->deity);
buffer.WriteUInt32(emu->intoxication);
buffer.WriteUInt32(10); //properties count
for (int r = 0; r < 10; r++) //properties
{
buffer.WriteUInt32(0);
}
buffer.WriteUInt32(22); // Equipment count
for (int r = EQ::textures::textureBegin; r < EQ::textures::materialCount; r++)
{
buffer.WriteUInt32(emu->item_material.Slot[r].Material);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
}
// Write zeroes for the next 13 equipment slots
for (int r = 0; r < 13; r++)
{
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
}
buffer.WriteUInt32(EQ::textures::materialCount); // Base Equipment count
//live seems to send the materials you had when you created the character here
//not entirely sure for what purpose though
for (int r = EQ::textures::textureBegin; r < EQ::textures::materialCount; r++)
{
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
}
buffer.WriteUInt32(EQ::textures::materialCount); // Tint Count
for (int r = 0; r < 7; r++)
{
buffer.WriteUInt32(emu->item_tint.Slot[r].Color);
}
// Write extra two tint values
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(EQ::textures::materialCount); // Equip Tint Count
for (int r = 0; r < 7; r++)
{
buffer.WriteUInt32(emu->item_tint.Slot[r].Color);
}
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt8(emu->haircolor);
buffer.WriteUInt8(emu->beardcolor);
buffer.WriteUInt32(0); //npc tint index
buffer.WriteUInt8(emu->eyecolor1);
buffer.WriteUInt8(emu->eyecolor2);
buffer.WriteUInt8(emu->hairstyle);
buffer.WriteUInt8(emu->beard);
buffer.WriteUInt8(emu->face);
buffer.WriteUInt8(0); //old_face
buffer.WriteUInt32(emu->drakkin_heritage);
buffer.WriteUInt32(emu->drakkin_tattoo);
buffer.WriteUInt32(emu->drakkin_details);
buffer.WriteInt8(-1); //texture type
buffer.WriteInt8(0); //material
buffer.WriteInt8(0); //variation
buffer.WriteFloat(5.0f); //height
buffer.WriteFloat(3.0f); //width
buffer.WriteFloat(2.5f); //length
buffer.WriteFloat(5.5f); //view height
buffer.WriteInt32(0); //primary_actor
buffer.WriteInt32(0); //secondary_actor
buffer.WriteUInt32(emu->points); // Unspent skill points
buffer.WriteUInt32(emu->mana);
buffer.WriteUInt32(emu->cur_hp);
buffer.WriteUInt32(emu->STR);
buffer.WriteUInt32(emu->STA);
buffer.WriteUInt32(emu->CHA);
buffer.WriteUInt32(emu->DEX);
buffer.WriteUInt32(emu->INT);
buffer.WriteUInt32(emu->AGI);
buffer.WriteUInt32(emu->WIS);
buffer.WriteUInt32(0); //heroic_sta
buffer.WriteUInt32(0); //heroic_str
buffer.WriteUInt32(0); //heroic_cha
buffer.WriteUInt32(0); //heroic_dex
buffer.WriteUInt32(0); //heroic_int
buffer.WriteUInt32(0); //heroic_agi
buffer.WriteUInt32(0); //heroic_wis
buffer.WriteUInt32(structs::MAX_PP_AA_ARRAY); // AA Count
for (uint32 r = 0; r < MAX_PP_AA_ARRAY; r++)
{
buffer.WriteUInt32(emu->aa_array[r].AA);
buffer.WriteUInt32(emu->aa_array[r].value);
buffer.WriteUInt32(emu->aa_array[r].charges);
}
//honestly we should look to up the base size and just limit titanium later
for (uint32 r = 0; r < structs::MAX_PP_AA_ARRAY - MAX_PP_AA_ARRAY; r++)
{
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
}
buffer.WriteUInt32(structs::MAX_PP_SKILL);
for (uint32 r = 0; r < structs::MAX_PP_SKILL; r++)
{
buffer.WriteUInt32(emu->skills[r]);
}
buffer.WriteUInt32(structs::MAX_PP_INNATE_SKILL); // Innate Skills count
for (uint32 r = 0; r < structs::MAX_PP_INNATE_SKILL; r++)
{
buffer.WriteUInt32(emu->InnateSkills[r]); // Innate Skills (regen, slam, etc)
}
buffer.WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count
for (uint32 r = 0; r < MAX_PP_DISCIPLINES; r++)
{
buffer.WriteUInt32(emu->disciplines.values[r]);
}
for (uint32 r = 0; r < structs::MAX_PP_DISCIPLINES - MAX_PP_DISCIPLINES; r++)
{
buffer.WriteUInt32(0);
}
buffer.WriteUInt32(structs::MAX_PP_DISCIPLINE_TIMERS); // Disc timers
for (uint32 r = 0; r < structs::MAX_PP_DISCIPLINE_TIMERS; r++)
{
buffer.WriteUInt32(0); //todo: support sending actual timestamps
}
buffer.WriteUInt32(0); // Unk Ability Count
buffer.WriteUInt32(structs::MAX_RECAST_TYPES); // linked_spell_timer_count
for (uint32 r = 0; r < MAX_RECAST_TYPES; r++)
{
buffer.WriteUInt32(emu->recastTimers[r]);
}
for (uint32 r = 0; r < structs::MAX_RECAST_TYPES - MAX_RECAST_TYPES; r++)
{
buffer.WriteUInt32(0);
}
buffer.WriteUInt32(structs::MAX_ITEM_RECAST_TYPES); // item recast timers
for (uint32 r = 0; r < structs::MAX_ITEM_RECAST_TYPES; r++)
{
buffer.WriteUInt32(0);
}
buffer.WriteUInt32(spells::SPELLBOOK_SIZE);
for (uint32 r = 0; r < EQ::spells::SPELLBOOK_SIZE; r++)
{
if (emu->spell_book[r] <= spells::SPELL_ID_MAX)
buffer.WriteUInt32(emu->spell_book[r]);
else
buffer.WriteInt32(-1);
}
for (uint32 r = 0; r < spells::SPELLBOOK_SIZE - EQ::spells::SPELLBOOK_SIZE; r++)
{
buffer.WriteInt32(-1);
}
buffer.WriteUInt32(spells::SPELL_GEM_COUNT); // Memorised spell slots
for (uint32 r = 0; r < EQ::spells::SPELL_GEM_COUNT; r++) // write first 12, we need to actually look into this for larion im sure it's changed
{
buffer.WriteUInt32(emu->mem_spells[r]);
}
for (uint32 r = 0; r < spells::SPELL_GEM_COUNT - EQ::spells::SPELL_GEM_COUNT; r++)
{
buffer.WriteInt32(-1);
}
buffer.WriteUInt32(spells::SPELL_GEM_RECAST_TIMER);
for (uint32 r = 0; r < EQ::spells::SPELL_GEM_COUNT; r++)
{
buffer.WriteUInt32(emu->spellSlotRefresh[r]); // spell gem refresh
}
//also refresh
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt32(0);
buffer.WriteUInt8(0);
buffer.WriteUInt32(structs::BUFF_COUNT);
}
/*ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); }
ENCODE(OP_ZoneSpawns)
{
EQApplicationPacket* in = *p;
*p = nullptr;
//store away the emu struct
unsigned char* __emu_buffer = in->pBuffer;
Spawn_Struct* emu = (Spawn_Struct*)__emu_buffer;
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;
}
for (int i = 0; i < entrycount; i++, emu++) {
SerializeBuffer buffer;
auto SpawnSize = emu->size;
if (!((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin))
)
{
if (emu->size == 0)
{
emu->size = 6;
SpawnSize = 6;
}
}
if (SpawnSize == 0)
{
SpawnSize = 3;
}
buffer.WriteString(emu->name);
buffer.WriteUInt32(emu->spawnId);
buffer.WriteUInt8(emu->level);
if (emu->DestructibleObject) //bounding radius: we should consider supporting this officially in the future
{
buffer.WriteFloat(10.0f);
}
else
{
buffer.WriteFloat(SpawnSize - 0.7f);
}
buffer.WriteUInt32(emu->spawnId); //player "id" we should consider supporting this in the future
buffer.WriteUInt32(103); //not sure
buffer.WriteUInt8(emu->NPC); //npc/player flag
structs::Spawn_Struct_Bitfields flags;
flags.gender = emu->gender;
flags.gender = emu->gender;
flags.ispet = emu->is_pet;
flags.afk = emu->afk;
flags.anon = emu->anon;
flags.gm = emu->gm;
flags.sneak = 0;
flags.lfg = emu->lfg;
flags.invis = emu->invis;
flags.linkdead = 0;
flags.showhelm = emu->showhelm;
flags.trader = emu->trader ? 1 : 0;
flags.targetable = 1;
flags.targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0;
flags.showname = emu->show_name;
}
}*/
// DECODE methods
DECODE(OP_ZoneEntry)
{
DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct);
SETUP_DIRECT_DECODE(ClientZoneEntry_Struct, structs::ClientZoneEntry_Struct);
memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name));
FINISH_DIRECT_DECODE();
}
} /*Larion*/