/* 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 #include #include #include #include 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*/