mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-17 14:31:30 +00:00
864 lines
25 KiB
C++
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*/
|