Compare commits

...

60 Commits

Author SHA1 Message Date
dannuic 743fd45b17 Added component-based patch system (#5070)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-04-25 23:29:12 -07:00
Knightly 0ada77f340 Login Updates for TOB (#5068)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-04-20 22:45:58 -07:00
Alex 9c8107ce96 Merge pull request #5069 from Knightly1/update_ci
CI tob_patch branch
2026-04-20 21:13:28 -07:00
Knightly 4581059e78 CI tob_patch branch 2026-04-20 16:29:00 -10:00
KimLS dceec36fad Merge branch 'master' into tob_patch 2026-04-20 18:41:45 -07:00
Alex 3035e906fe Merge pull request #5067 from dannuic/tob_fullylogin
Validated up to OP_SimpleMessage
2026-04-20 18:40:08 -07:00
dannuic 0e0162edc0 Added spell links to interrupt and fizzle messages 2026-04-19 23:07:02 -06:00
dannuic af06fb703c confirmed consider 2026-04-18 12:37:56 -06:00
dannuic c5d089de68 Validated message and interrupt packets, needs refactor for full functionality 2026-04-18 12:08:54 -06:00
dannuic b8ee811ac6 Fixed unmemming spells on cast 2026-04-18 06:53:37 -06:00
dannuic 08cdd8234d Validated to OP_CastSpell -- still causes a spell to unmem after cast 2026-04-18 00:08:18 -06:00
dannuic 3bb7f94713 Fixed memorization for parity with RoF2 2026-04-17 23:35:34 -06:00
dannuic ec5a9d0bd4 Validated up to OP_MemorizeSpell (still needs testing in client) 2026-04-17 18:07:39 -06:00
dannuic 2da6d3f37c Fixed item index mapping 2026-04-17 14:57:35 -06:00
dannuic 6a7baf8f1c Validated through OP_SetServerFilter 2026-04-17 11:56:46 -06:00
dannuic a8e3ab41e1 Validated up to OP_ExpUpdate 2026-04-16 17:13:17 -06:00
dannuic fe4146050f Validated up to OP_UpdateAA 2026-04-16 16:51:41 -06:00
dannuic 36ea946255 Attempt to fix opcodes formatting 2026-04-16 16:28:08 -06:00
dannuic f29d87aced Validated up to OP_WorldObjectsSent 2026-04-16 15:59:54 -06:00
KimLS 767f04731b Merge fix 2026-04-15 22:06:39 -07:00
Alex 8e7964b835 Merge pull request #5066 from dannuic/tob_loginpatches
TOB packet work through zone in
2026-04-15 22:03:39 -07:00
dannuic a9333fb51b Added padding into client position struct 2026-04-15 22:52:21 -06:00
dannuic 27ad857ee5 Fixed spawn position struct 2026-04-15 22:51:23 -06:00
dannuic 5549daedb1 Some ZoneChange work 2026-04-15 21:10:41 -05:00
dannuic b5cc8dfab1 Verified and corrected packets through character select 2026-04-14 13:39:28 -05:00
dannuic 865f619e21 Validated and filled out SendMaxCharacters 2026-04-13 22:56:49 -06:00
dannuic a4785d30e0 Added some comments for trivial opcodes 2026-04-13 17:48:11 -05:00
dannuic a54711817d Validated EnterWorld (in & out) 2026-04-13 17:34:21 -05:00
dannuic 139575661d Validated SendCharInfo 2026-04-13 17:10:38 -05:00
dannuic 8dd24f4a70 Updated comments for LogServer 2026-04-13 15:47:13 -05:00
KimLS 517d9419a7 Update opcode tracker 2026-04-12 17:03:29 -07:00
KimLS a789b22fc7 Change from obr to tob dir, added a status doc 2026-04-12 13:16:23 -07:00
KimLS 6e1fe45090 Merge branch 'tob_patch' of https://github.com/EQEmu/EQEmu into tob_patch 2026-04-09 20:41:53 -07:00
KimLS 492d848f6a adjust offsets by 2 in OP_LogServer on tob, I had done it simply from assembly and misread the register and it's off by 2 for each one. 2026-04-09 20:41:40 -07:00
Alex ce5e216be9 Merge pull request #5058 from dannuic/tob_zonein
TOB zonein + all opcodes for TOB
2026-04-09 00:01:53 -07:00
dannuic 48e0847f21 renamed straggler file for tob 2026-04-08 23:06:09 -05:00
dannuic 7f42add39b Finished up player profile 2026-04-08 22:13:37 -05:00
dannuic 49161a618f Added up to DoN currency (not inclusive) 2026-04-08 17:57:08 -05:00
dannuic aac7bbf48a Fixed enough to allow zoning 2026-04-08 15:57:37 -05:00
dannuic 2c4d82f1b9 Fixed packed structs 2026-04-08 10:26:36 -05:00
dannuic dea5031d83 WIP attempts at zoning in 2026-04-07 23:35:16 -05:00
KimLS 30c9c6317f Update OP_LogServer
Fix the source group matching in common lib
Added a couple packet structure documentations I had looked at.
2026-04-06 20:51:52 -07:00
Alex c7eea72997 Merge pull request #5057 from dannuic/tob_charactercreate
Implemented through character creation packets
2026-04-06 14:51:18 -07:00
dannuic 28e6ef29d4 Corrected missing find/replace for steam_latest 2026-04-06 16:23:06 -05:00
dannuic a6f4438c0d Removed the _t for consistency 2026-04-06 16:15:20 -05:00
dannuic e5a111d8d8 Implemented through character creation packets 2026-04-06 16:09:59 -05:00
KimLS abbaf6f9a1 update opcodes 2026-04-04 16:47:41 -07:00
KimLS ccdc9f2e43 fix dumb compile errors from my hand merge 2026-04-04 15:48:43 -07:00
KimLS a9effc7bac A couple laurion renames i missed 2026-04-04 15:28:59 -07:00
KimLS a2b3b36cf1 WIP, porting old laurion changes to tob 2026-04-04 15:11:21 -07:00
KimLS 75ddf8dfc3 Merge branch 'master' into obr_login 2026-04-04 14:14:47 -07:00
KimLS a3802ff257 opcode fixes 2026-04-02 11:50:08 -07:00
KimLS ca23b8612e WIP world select packets 2026-03-30 21:54:15 -07:00
KimLS 485ae4809d some changes, working on login 2026-03-30 18:36:13 -07:00
KimLS 452407ed67 Fix seq in on login handshake 2026-03-26 21:59:53 -07:00
KimLS aa1c481f65 update opcodes 2026-03-26 13:01:24 -07:00
KimLS 780dcdab5a Renamed "larion" to "steam latest" 2026-03-26 12:50:46 -07:00
KimLS 16ec08e71c Some investigation on packets, mostly the same as rof2 / laurion but not entirely. 2026-03-25 18:08:46 -07:00
KimLS 0024073cee Login exploration 2026-03-24 23:59:42 -07:00
KimLS 37b8428c48 Starting research 2026-03-24 23:22:08 -07:00
78 changed files with 12869 additions and 407 deletions
+1
View File
@@ -3,6 +3,7 @@ on:
push:
branches:
- master
- tob_patch
pull_request:
jobs:
+1 -1
View File
@@ -71,7 +71,7 @@ if(UNIX)
endif()
endif()
find_package(Boost REQUIRED COMPONENTS dynamic_bitset foreach tuple)
find_package(Boost REQUIRED COMPONENTS dynamic_bitset foreach tuple CONFIG REQUIRED)
find_package(cereal CONFIG REQUIRED)
find_package(fmt CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
+13 -2
View File
@@ -86,6 +86,7 @@ set(common_sources
packet_dump_file.cpp
packet_dump.cpp
packet_functions.cpp
patches/client_version.cpp
patches/patches.cpp
patches/rof_limits.cpp
patches/rof.cpp
@@ -97,6 +98,8 @@ set(common_sources
patches/sof.cpp
patches/titanium_limits.cpp
patches/titanium.cpp
patches/tob.cpp
patches/tob_limits.cpp
patches/uf_limits.cpp
patches/uf.cpp
path_manager.cpp
@@ -133,6 +136,7 @@ set(common_sources
util/directory.cpp
util/uuid.cpp
zone_store.cpp
links.cpp
)
set(repositories
@@ -652,6 +656,8 @@ set(common_headers
packet_dump_file.h
packet_dump.h
packet_functions.h
patches/IMessage.h
patches/client_version.h
patches/patches.h
patches/rof_limits.h
patches/rof_ops.h
@@ -668,7 +674,7 @@ set(common_headers
patches/sof_limits.h
patches/sof_ops.h
patches/sof_structs.h
patches/sof.h
patches/sof.h
patches/ss_declare.h
patches/ss_define.h
patches/ss_register.h
@@ -676,6 +682,10 @@ set(common_headers
patches/titanium_ops.h
patches/titanium_structs.h
patches/titanium.h
patches/tob.h
patches/tob_limits.h
patches/tob_ops.h
patches/tob_structs.h
patches/uf_limits.h
patches/uf_ops.h
patches/uf_structs.h
@@ -727,7 +737,8 @@ set(common_headers
util/memory_stream.h
util/uuid.h
version.h
zone_store.h
zone_store.h
links.h
)
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES ${common_sources})
+1
View File
@@ -62,6 +62,7 @@ public:
void WriteUInt8(uint8 value) { *(uint8 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8); }
void WriteUInt32(uint32 value) { *(uint32 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint32); }
void WriteUInt64(uint64 value) { *(uint64 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint64); }
void WriteSInt16(int32 value) { *(int16*)(pBuffer + _wpos) = value; _wpos += sizeof(int16); }
void WriteUInt16(uint32 value) { *(uint16 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint16); }
void WriteSInt32(int32 value) { *(int32 *)(pBuffer + _wpos) = value; _wpos += sizeof(int32); }
void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); }
+3
View File
@@ -71,6 +71,9 @@ namespace Class {
constexpr uint8 FellowshipMaster = 69;
constexpr uint8 AlternateCurrencyMerchant = 70;
constexpr uint8 MercenaryLiaison = 71;
constexpr uint8 RealEstateMerchant = 72;
constexpr uint8 LoyaltyMerchant = 73;
constexpr uint8 TributeMaster2 = 74;
constexpr uint8 PLAYER_CLASS_COUNT = 16;
constexpr uint16 ALL_CLASSES_BITMASK = 65535;
+18
View File
@@ -99,6 +99,24 @@ uint32 CRC32::GenerateNoFlip(const uint8* buf, uint32 bufsize) {
return Update(buf, bufsize);
}
unsigned long CRC32::GetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at)
{
unsigned long data;
unsigned long check = 0xffffffff;
for (uint32 i = start_at; i < in_length; i++)
{
data = in_data[i];
data = data ^ (check);
data = data & 0x000000ff;
check = check >> 8;
data = CRC32Table[data];
check = check ^ data;
}
return check;
}
void CRC32::SetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at)
{
unsigned long data;
+1
View File
@@ -25,6 +25,7 @@ public:
static uint32 Generate(const uint8* buf, uint32 bufsize);
static uint32 GenerateNoFlip(const uint8* buf, uint32 bufsize); // Same as Generate(), but without the ~
static void SetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at=4);
static unsigned long GetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at = 4);
// Multiple buffer CRC32
static uint32 Update(const uint8* buf, uint32 bufsize, uint32 crc32 = 0xFFFFFFFF);
+2 -2
View File
@@ -18,8 +18,6 @@
// system use
N(OP_ExploreUnknown),
// start (please add new opcodes in descending order and re-order any name changes where applicable)
N(OP_0x0193),
N(OP_0x0347),
N(OP_AAAction),
N(OP_AAExpUpdate),
N(OP_AcceptNewTask),
@@ -381,6 +379,7 @@ N(OP_MercenaryTimer),
N(OP_MercenaryTimerRequest),
N(OP_MercenaryUnknown1),
N(OP_MercenaryUnsuspendResponse),
N(OP_MerchantBulkItems),
N(OP_MobEnduranceUpdate),
N(OP_MobHealth),
N(OP_MobManaUpdate),
@@ -399,6 +398,7 @@ N(OP_MultiLineMsg),
N(OP_NewSpawn),
N(OP_NewTitlesAvailable),
N(OP_NewZone),
N(OP_NPCMoveUpdate),
N(OP_OnLevelMessage),
N(OP_OpenContainer),
N(OP_OpenDiscordMerchant),
+111
View File
@@ -54,6 +54,8 @@ const char* EQ::versions::ClientVersionName(ClientVersion client_version)
return "RoF";
case ClientVersion::RoF2:
return "RoF2";
case ClientVersion::TOB:
return "TOB";
default:
return "Invalid Version";
};
@@ -74,6 +76,8 @@ uint32 EQ::versions::ConvertClientVersionToClientVersionBit(ClientVersion client
return bitRoF;
case ClientVersion::RoF2:
return bitRoF2;
case ClientVersion::TOB:
return bitTOB;
default:
return bitUnknown;
}
@@ -94,6 +98,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertClientVersionBitToClientVersion
return ClientVersion::RoF;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::RoF2) - 1)) :
return ClientVersion::RoF2;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::TOB) - 1)) :
return ClientVersion::TOB;
default:
return ClientVersion::Unknown;
}
@@ -182,6 +188,8 @@ const char* EQ::versions::MobVersionName(MobVersion mob_version)
return "RoF";
case MobVersion::RoF2:
return "RoF2";
case MobVersion::TOB:
return "TOB";
case MobVersion::NPC:
return "NPC";
case MobVersion::NPCMerchant:
@@ -210,6 +218,8 @@ const char* EQ::versions::MobVersionName(MobVersion mob_version)
return "Offline RoF";
case MobVersion::OfflineRoF2:
return "Offline RoF2";
case MobVersion::OfflineTOB:
return "Offline TOB";
default:
return "Invalid Version";
};
@@ -233,6 +243,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertMobVersionToClientVersion(MobVe
return ClientVersion::RoF;
case MobVersion::RoF2:
return ClientVersion::RoF2;
case MobVersion::TOB:
return ClientVersion::TOB;
default:
return ClientVersion::Unknown;
}
@@ -256,6 +268,8 @@ EQ::versions::MobVersion EQ::versions::ConvertClientVersionToMobVersion(ClientVe
return MobVersion::RoF;
case ClientVersion::RoF2:
return MobVersion::RoF2;
case ClientVersion::TOB:
return MobVersion::TOB;
default:
return MobVersion::Unknown;
}
@@ -276,6 +290,8 @@ EQ::versions::MobVersion EQ::versions::ConvertPCMobVersionToOfflinePCMobVersion(
return MobVersion::OfflineRoF;
case MobVersion::RoF2:
return MobVersion::OfflineRoF2;
case MobVersion::TOB:
return MobVersion::OfflineTOB;
default:
return MobVersion::Unknown;
}
@@ -296,6 +312,8 @@ EQ::versions::MobVersion EQ::versions::ConvertOfflinePCMobVersionToPCMobVersion(
return MobVersion::RoF;
case MobVersion::OfflineRoF2:
return MobVersion::RoF2;
case MobVersion::OfflineTOB:
return MobVersion::TOB;
default:
return MobVersion::Unknown;
}
@@ -316,6 +334,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertOfflinePCMobVersionToClientVers
return ClientVersion::RoF;
case MobVersion::OfflineRoF2:
return ClientVersion::RoF2;
case MobVersion::OfflineTOB:
return ClientVersion::TOB;
default:
return ClientVersion::Unknown;
}
@@ -336,6 +356,8 @@ EQ::versions::MobVersion EQ::versions::ConvertClientVersionToOfflinePCMobVersion
return MobVersion::OfflineRoF;
case ClientVersion::RoF2:
return MobVersion::OfflineRoF2;
case ClientVersion::TOB:
return MobVersion::OfflineTOB;
default:
return MobVersion::Unknown;
}
@@ -386,6 +408,28 @@ const char* EQ::expansions::ExpansionName(Expansion expansion)
return "Rain of Fear";
case Expansion::CotF:
return "Call of the Forsaken";
case Expansion::TDS:
return "The Darkened Sea";
case Expansion::TBM:
return "The Broken Mirror";
case Expansion::EoK:
return "Empires of Kunark";
case Expansion::RoS:
return "Ring of Scale";
case Expansion::TBL:
return "The Burning Lands";
case Expansion::ToV:
return "Torment of Velious";
case Expansion::CoV:
return "Claws of Veeshan";
case Expansion::ToL:
return "Terror of Luclin";
case Expansion::NoS:
return "Night of Shadows";
case Expansion::LS:
return "Laurion's Song";
case Expansion::TOB:
return "The Outer Brood";
default:
return "Invalid Expansion";
}
@@ -439,6 +483,29 @@ uint32 EQ::expansions::ConvertExpansionToExpansionBit(Expansion expansion)
return bitRoF;
case Expansion::CotF:
return bitCotF;
case Expansion::TDS:
return bitTDS;
case Expansion::TBM:
return bitTBM;
case Expansion::EoK:
return bitEoK;
case Expansion::RoS:
return bitRoS;
case Expansion::TBL:
return bitTBL;
case Expansion::ToV:
return bitToV;
case Expansion::CoV:
return bitCoV;
case Expansion::ToL:
return bitToL;
case Expansion::NoS:
return bitNoS;
case Expansion::LS:
return bitLS;
case Expansion::TOB:
return bitTOB;
default:
return bitEverQuest;
}
@@ -487,6 +554,28 @@ EQ::expansions::Expansion EQ::expansions::ConvertExpansionBitToExpansion(uint32
return Expansion::RoF;
case bitCotF:
return Expansion::CotF;
case bitTDS:
return Expansion::TDS;
case bitTBM:
return Expansion::TBM;
case bitEoK:
return Expansion::EoK;
case bitRoS:
return Expansion::RoS;
case bitTBL:
return Expansion::TBL;
case bitToV:
return Expansion::ToV;
case bitCoV:
return Expansion::CoV;
case bitToL:
return Expansion::ToL;
case bitNoS:
return Expansion::NoS;
case bitLS:
return Expansion::LS;
case bitTOB:
return Expansion::TOB;
default:
return Expansion::EverQuest;
}
@@ -535,6 +624,28 @@ uint32 EQ::expansions::ConvertExpansionToExpansionsMask(Expansion expansion)
return maskRoF;
case Expansion::CotF:
return maskCotF;
case Expansion::TDS:
return maskTDS;
case Expansion::TBM:
return maskTBM;
case Expansion::EoK:
return maskEoK;
case Expansion::RoS:
return maskRoS;
case Expansion::TBL:
return maskTBL;
case Expansion::ToV:
return maskToV;
case Expansion::CoV:
return maskCoV;
case Expansion::ToL:
return maskToL;
case Expansion::NoS:
return maskNoS;
case Expansion::LS:
return maskLS;
case Expansion::TOB:
return maskTOB;
default:
return maskEverQuest;
}
+49 -10
View File
@@ -32,7 +32,8 @@ namespace EQ
SoD, // Build: 'Dec 19 2008 15:22:49'
UF, // Build: 'Jun 8 2010 16:44:32'
RoF, // Build: 'Dec 10 2012 17:35:44'
RoF2 // Build: 'May 10 2013 23:30:08'
RoF2, // Build: 'May 10 2013 23:30:08'
TOB // Build: 'Sep 11 2025 11:54:10'
};
enum ClientVersionBitmask : uint32 {
@@ -44,6 +45,7 @@ namespace EQ
bitUF = 0x00000010,
bitRoF = 0x00000020,
bitRoF2 = 0x00000040,
bitTOB = 0x00000080,
maskUnknown = 0x00000000,
maskTitaniumAndEarlier = 0x00000003,
maskSoFAndEarlier = 0x00000007,
@@ -55,11 +57,12 @@ namespace EQ
maskUFAndLater = 0xFFFFFFF0,
maskRoFAndLater = 0xFFFFFFE0,
maskRoF2AndLater = 0xFFFFFFC0,
maskTOBAndLater = 0xFFFFFF80,
maskAllClients = 0xFFFFFFFF
};
const ClientVersion LastClientVersion = ClientVersion::RoF2;
const size_t ClientVersionCount = (static_cast<size_t>(LastClientVersion) + 1);
inline constexpr ClientVersion LastClientVersion = ClientVersion::TOB;
inline constexpr size_t ClientVersionCount = (static_cast<size_t>(LastClientVersion) + 1);
bool IsValidClientVersion(ClientVersion client_version);
ClientVersion ValidateClientVersion(ClientVersion client_version);
@@ -76,6 +79,7 @@ namespace EQ
UF,
RoF,
RoF2,
TOB,
NPC,
NPCMerchant,
Merc,
@@ -89,13 +93,14 @@ namespace EQ
OfflineSoD,
OfflineUF,
OfflineRoF,
OfflineRoF2
OfflineRoF2,
OfflineTOB
};
const MobVersion LastMobVersion = MobVersion::OfflineRoF2;
const MobVersion LastPCMobVersion = MobVersion::RoF2;
const MobVersion LastMobVersion = MobVersion::OfflineTOB;
const MobVersion LastPCMobVersion = MobVersion::TOB;
const MobVersion LastNonPCMobVersion = MobVersion::BotPet;
const MobVersion LastOfflinePCMobVersion = MobVersion::OfflineRoF2;
const MobVersion LastOfflinePCMobVersion = MobVersion::OfflineTOB;
const size_t MobVersionCount = (static_cast<size_t>(LastMobVersion) + 1);
bool IsValidMobVersion(MobVersion mob_version);
@@ -127,7 +132,8 @@ namespace EQ
ucsSoDCombined = 'D',
ucsUFCombined = 'E',
ucsRoFCombined = 'F',
ucsRoF2Combined = 'G'
ucsRoF2Combined = 'G',
ucsTOBCombined = 'H'
};
} /*versions*/
@@ -154,7 +160,18 @@ namespace EQ
HoT,
VoA,
RoF,
CotF
CotF,
TDS,
TBM,
EoK,
RoS,
TBL,
ToV,
CoV,
ToL,
NoS,
LS,
TOB
};
enum ExpansionBitmask : uint32 {
@@ -179,6 +196,17 @@ namespace EQ
bitVoA = 0x00020000,
bitRoF = 0x00040000,
bitCotF = 0x00080000,
bitTDS = 0x00100000,
bitTBM = 0x00200000,
bitEoK = 0x00400000,
bitRoS = 0x00800000,
bitTBL = 0x01000000,
bitToV = 0x02000000,
bitCoV = 0x04000000,
bitToL = 0x08000000,
bitNoS = 0x10000000,
bitLS = 0x20000000,
bitTOB = 0x40000000,
maskEverQuest = 0x00000000,
maskRoK = 0x00000001,
maskSoV = 0x00000003,
@@ -199,7 +227,18 @@ namespace EQ
maskHoT = 0x0001FFFF,
maskVoA = 0x0003FFFF,
maskRoF = 0x0007FFFF,
maskCotF = 0x000FFFFF
maskCotF = 0x000FFFFF,
maskTDS = 0x001FFFFF,
maskTBM = 0x003FFFFF,
maskEoK = 0x007FFFFF,
maskRoS = 0x00FFFFFF,
maskTBL = 0x01FFFFFF,
maskToV = 0x03FFFFFF,
maskCoV = 0x07FFFFFF,
maskToL = 0x0FFFFFFF,
maskNoS = 0x1FFFFFFF,
maskLS = 0x3FFFFFFF,
maskTOB = 0x7FFFFFFF,
};
const char* ExpansionName(Expansion expansion);
+40
View File
@@ -759,6 +759,46 @@ typedef enum {
FilterStrikethrough = 26, //0=show, 1=hide // RoF2 Confirmed
FilterStuns = 27, //0=show, 1=hide // RoF2 Confirmed
FilterBardSongsOnPets = 28, //0=show, 1=hide // RoF2 Confirmed
FilterSwarmPetDeath = 29,
FilterFellowshipChat = 30,
FilterMercenaryMessages = 31,
FilterSpam = 32,
FilterAchievements = 33,
FilterPvPMessages = 34,
FilterSpellNameInCast = 35,
FilterRandomMine = 36,
FilterRandomGroupRaid = 37,
FilterRandomOthers = 38,
FilterEnvironmentalDamage = 39,
FilterMessages = 40,
FilterOverwriteDetrimental = 41,
FilterOverwriteBeneficial = 42,
FilterCantUseCommand = 43,
FilterCombatAbilityReuse = 44,
FilterAAAbilityReuse = 45,
FilterProcBeginCasting = 46,
FilterDestroyedItems = 47,
FilterYourAuras = 48,
FilterOtherAuras = 49,
FilterYourHeals = 50,
FilterOtherHeals = 51,
FilterYourDoTs = 52,
FilterOtherDoTs = 53,
FilterOtherDirectDamage = 54,
FilterSpellEmotes = 55,
FilterFactionMessages = 56,
FilterTauntMessages = 57,
FilterYourDisciplines = 58,
FilterOtherDisplines = 59,
FilterAchievementsOthers = 60,
FilterRaidVictory = 61,
FilterOtherDirectDamageCrits = 62,
FilterDoTYoursCritical = 63,
FilterDoTOthersCritical = 64,
FilterDoTDamageTaken = 65,
FilterHealsReceived = 66,
FilterHealsYoursCritical = 67,
FilterHealsOthersCritical = 68,
_FilterCount
} eqFilterType;
+89
View File
@@ -110,6 +110,15 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
RoF2::constants::CHARACTER_CREATION_LIMIT,
RoF2::constants::SAY_LINK_BODY_SIZE,
RoF2::constants::MAX_BAZAAR_TRADERS
),
/*[ClientVersion::TOB] =*/
EQ::constants::LookupEntry(
TOB::constants::EXPANSION,
TOB::constants::EXPANSION_BIT,
TOB::constants::EXPANSIONS_MASK,
TOB::constants::CHARACTER_CREATION_LIMIT,
TOB::constants::SAY_LINK_BODY_SIZE,
TOB::constants::MAX_BAZAAR_TRADERS
)
};
@@ -376,6 +385,34 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
RoF2::inventory::ConcatenateInvTypeLimbo,
RoF2::inventory::AllowOverLevelEquipment
),
/*[MobVersion::TOB] =*/
//TOBTodo: These need to be set to the latest values not just use RoF2
EQ::inventory::LookupEntry(
EQ::inventory::LookupEntry::InventoryTypeSize_Struct(
EQ::invtype::POSSESSIONS_SIZE, RoF2::invtype::BANK_SIZE, RoF2::invtype::SHARED_BANK_SIZE,
RoF2::invtype::TRADE_SIZE, RoF2::invtype::WORLD_SIZE, RoF2::invtype::LIMBO_SIZE,
RoF2::invtype::TRIBUTE_SIZE, RoF2::invtype::TROPHY_TRIBUTE_SIZE, RoF2::invtype::GUILD_TRIBUTE_SIZE,
RoF2::invtype::MERCHANT_SIZE, RoF2::invtype::DELETED_SIZE, RoF2::invtype::CORPSE_SIZE,
RoF2::invtype::BAZAAR_SIZE, RoF2::invtype::INSPECT_SIZE, RoF2::invtype::REAL_ESTATE_SIZE,
RoF2::invtype::VIEW_MOD_PC_SIZE, RoF2::invtype::VIEW_MOD_BANK_SIZE, RoF2::invtype::VIEW_MOD_SHARED_BANK_SIZE,
RoF2::invtype::VIEW_MOD_LIMBO_SIZE, RoF2::invtype::ALT_STORAGE_SIZE, RoF2::invtype::ARCHIVED_SIZE,
RoF2::invtype::MAIL_SIZE, RoF2::invtype::GUILD_TROPHY_TRIBUTE_SIZE, RoF2::invtype::KRONO_SIZE,
RoF2::invtype::GUILD_BANK_MAIN_SIZE, RoF2::invtype::GUILD_BANK_DEPOSIT_SIZE, RoF2::invtype::OTHER_SIZE
),
RoF2::invslot::EQUIPMENT_BITMASK,
RoF2::invslot::GENERAL_BITMASK,
RoF2::invslot::CURSOR_BITMASK,
RoF2::invslot::POSSESSIONS_BITMASK,
RoF2::invslot::CORPSE_BITMASK,
RoF2::invbag::SLOT_COUNT,
RoF2::invaug::SOCKET_COUNT,
RoF2::inventory::AllowEmptyBagInBag,
RoF2::inventory::AllowClickCastFromBag,
RoF2::inventory::ConcatenateInvTypeLimbo,
RoF2::inventory::AllowOverLevelEquipment
),
/*[MobVersion::NPC] =*/
EQ::inventory::LookupEntry(
EQ::inventory::LookupEntry::InventoryTypeSize_Struct(
@@ -748,6 +785,35 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
RoF2::INULL,
RoF2::invbag::SLOT_COUNT,
RoF2::invaug::SOCKET_COUNT,
false,
false,
false,
false
),
/*[MobVersion::OfflineTOB] =*/
//TOBTodo: Need to use their own values instead of RoF2
EQ::inventory::LookupEntry(
EQ::inventory::LookupEntry::InventoryTypeSize_Struct(
RoF2::INULL, RoF2::INULL, RoF2::INULL,
RoF2::invtype::TRADE_SIZE, RoF2::INULL, RoF2::INULL,
RoF2::INULL, RoF2::INULL, RoF2::INULL,
RoF2::invtype::MERCHANT_SIZE, RoF2::INULL, RoF2::INULL,
RoF2::invtype::BAZAAR_SIZE, RoF2::invtype::INSPECT_SIZE, RoF2::INULL,
RoF2::invtype::VIEW_MOD_PC_SIZE, RoF2::invtype::VIEW_MOD_BANK_SIZE, RoF2::invtype::VIEW_MOD_SHARED_BANK_SIZE,
RoF2::invtype::VIEW_MOD_LIMBO_SIZE, RoF2::INULL, RoF2::INULL,
RoF2::INULL, RoF2::INULL, RoF2::INULL,
RoF2::INULL, RoF2::INULL, RoF2::INULL
),
RoF2::INULL,
RoF2::INULL,
RoF2::INULL,
RoF2::INULL,
RoF2::INULL,
RoF2::invbag::SLOT_COUNT,
RoF2::invaug::SOCKET_COUNT,
false,
false,
@@ -1000,6 +1066,11 @@ static const EQ::behavior::LookupEntry behavior_static_lookup_entries[EQ::versio
EQ::behavior::LookupEntry(
RoF2::behavior::CoinHasWeight
),
/*[MobVersion::TOB] =*/
//TOBTodo: We need this value set properly
EQ::behavior::LookupEntry(
RoF2::behavior::CoinHasWeight
),
/*[MobVersion::NPC] =*/
EQ::behavior::LookupEntry(
EQ::behavior::CoinHasWeight
@@ -1053,6 +1124,11 @@ static const EQ::behavior::LookupEntry behavior_static_lookup_entries[EQ::versio
RoF::behavior::CoinHasWeight
),
/*[MobVersion::OfflineRoF2] =*/
EQ::behavior::LookupEntry(
RoF2::behavior::CoinHasWeight
),
/*[MobVersion::OfflineTOB] =*/
//TOBTodo: We need this value set properly
EQ::behavior::LookupEntry(
RoF2::behavior::CoinHasWeight
)
@@ -1208,6 +1284,19 @@ static const EQ::spells::LookupEntry spells_static_lookup_entries[EQ::versions::
RoF2::spells::NPC_BUFFS,
RoF2::spells::PET_BUFFS,
RoF2::spells::MERC_BUFFS
),
/*[ClientVersion::TOB] =*/
EQ::spells::LookupEntry(
TOB::spells::SPELL_ID_MAX,
TOB::spells::SPELLBOOK_SIZE,
UF::spells::SPELL_GEM_COUNT, // client translators are setup to allow the max value a client supports..however, the top 4 indices are not valid in this case
TOB::spells::LONG_BUFFS,
TOB::spells::SHORT_BUFFS,
TOB::spells::DISC_BUFFS,
TOB::spells::TOTAL_BUFFS,
TOB::spells::NPC_BUFFS,
TOB::spells::PET_BUFFS,
TOB::spells::MERC_BUFFS
)
};
+1
View File
@@ -23,6 +23,7 @@
#include "common/patches/rof2_limits.h"
#include "common/patches/sod_limits.h"
#include "common/patches/sof_limits.h"
#include "common/patches/tob_limits.h"
#include "common/patches/titanium_limits.h"
#include "common/patches/uf_limits.h"
#include "common/types.h"
+8
View File
@@ -47,6 +47,13 @@ static const uint32 ADVANCED_LORE_LENGTH = 8192;
#pragma pack(push)
#pragma pack(1)
struct EqGuid
{
uint32_t Id;
uint16_t WorldId;
uint16_t Reserved;
};
struct LoginInfo {
/*000*/ char login_info[64];
/*064*/ uint8 unknown064[124];
@@ -326,6 +333,7 @@ union
bool buyer;
bool untargetable;
uint32 npc_tint_id;
EqGuid CharacterGuid;
};
struct PlayerState_Struct {
+31
View File
@@ -0,0 +1,31 @@
//
// Created by dannu on 4/18/2026.
//
#include "links.h"
#include "spdat.h"
void Links::FormatItemLink(char* Buffer, size_t BufferSize, const EQ::ItemInstance* item)
{
// TODO: Reverse 0x14064B220 to get definition of this function
}
void Links::FormatSpellLink(char* Buffer, size_t BufferSize, uint32_t SpellID,
const char* spellNameOverride)
{
snprintf(Buffer, BufferSize, "%c%d3^%d^0^'%s%c", ITEM_TAG_CHAR, ETAG_SPELL, SpellID,
spellNameOverride && spellNameOverride[0] ? spellNameOverride : GetSpellName(SpellID), ITEM_TAG_CHAR);
}
void Links::FormatDialogLink(char* Buffer, size_t BufferSize, std::string_view keyword, std::string_view text)
{
if (text.empty()) {
snprintf(Buffer, BufferSize, "%c%d%.*s%c", ITEM_TAG_CHAR, ETAG_DIALOG_RESPONSE,
static_cast<int>(keyword.length()), keyword.data(), ITEM_TAG_CHAR);
} else {
snprintf(Buffer, BufferSize, "%c%d%.*s:%.*s%c", ITEM_TAG_CHAR, ETAG_DIALOG_RESPONSE,
static_cast<int>(keyword.length()), keyword.data(),
static_cast<int>(text.length()), text.data(), ITEM_TAG_CHAR);
}
}
+61
View File
@@ -0,0 +1,61 @@
//
// Created by dannu on 4/18/2026.
//
#pragma once
#include "item_instance.h"
namespace EQ { class ItemInstance; }
namespace Links
{
// Max Link Size in bytes
constexpr size_t MAX_LINK_SIZE = 512;
// Universal link tag character
constexpr char ITEM_TAG_CHAR = '\x12';
// Enumeration of different types of item tags
enum ETagCodes
{
ETAG_ITEM = 0,
ETAG_PLAYER,
ETAG_SPAM,
ETAG_ACHIEVEMENT,
ETAG_DIALOG_RESPONSE,
ETAG_COMMAND,
ETAG_SPELL,
ETAG_FACTION,
ETAG_COMMAND2,
ETAG_UNKNOWN9,
ETAG_COUNT,
ETAG_FIRST = ETAG_ITEM,
ETAG_LAST = ETAG_UNKNOWN9,
ETAG_INVALID = -1,
};
//----------------------------------------------------------------------------
// Link Formatting -- Pulled from MQ code
// Create an achievement link for the given achievement.
// TODO: implement this when achievements are added, leave the signature here for reference. Code in eqlib's ItemLinks.cpp
// void FormatAchievementLink(char* Buffer, size_t BufferSize, const Achievement* achievement,
// std::string_view playerName);
// Create an item link from the given item.
void FormatItemLink(char* Buffer, size_t BufferSize, const EQ::ItemInstance* item);
// Create a spell link for the given spell, with optional spell name override. Spells on items often have
// spell name overrides that changes the display name of the spell.
void FormatSpellLink(char* Buffer, size_t BufferSize, uint32_t SpellID,
const char* spellNameOverride = nullptr);
// Format text into a clickable dialog link. The keyword is the text that will be displayed in the chat window,
// and the text is the text that will be sent to the server when the link is clicked. If no text is provided,
// then the keyword will be used as the text.
void FormatDialogLink(char* Buffer, size_t BufferSize, std::string_view keyword,
std::string_view text = {});
}
+51
View File
@@ -0,0 +1,51 @@
/* EQEmu: EQEmulator
Copyright (C) 2001-2026 EQEmu Development Team
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; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "client_version.h"
// Migration path: replace string_ids.h usage with ID enum values one call site at a time.
class Client;
class Mob;
class EQApplicationPacket;
namespace Message {
template<typename... Args>
concept AllConstChar = (std::is_convertible_v<Args, const char*> && ...);
class IMessage
{
public:
IMessage() = default;
virtual ~IMessage() = default;
// these two are the basic string message packets
virtual std::unique_ptr<EQApplicationPacket> Simple(uint32_t color, uint32_t id) const = 0;
virtual std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
const std::array<const char*, 9>& args) const = 0;
// These aren't technically messages, but they use the same format and are similar enough to include here
virtual std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
const char* spell_link) const = 0;
virtual std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
const char* name, const char* spell_link) const = 0;
};
} // namespace Message
+86
View File
@@ -0,0 +1,86 @@
/* EQEmu: EQEmulator
Copyright (C) 2001-2026 EQEmu Development Team
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; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; 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, see <http://www.gnu.org/licenses/>.
*/
#include "client_version.h"
#include "common/patches/titanium.h"
#include "common/patches/sof.h"
#include "common/patches/sod.h"
#include "common/patches/uf.h"
#include "common/patches/rof.h"
#include "common/patches/rof2.h"
#include "common/patches/tob.h"
#include <array>
using Version = EQ::versions::ClientVersion;
struct ClientComponents
{
explicit ClientComponents(Version version) : version(version)
{
switch (version) {
case Version::TOB:
messageComponent = std::make_unique<Message::TOB>();
break;
case Version::RoF2:
messageComponent = std::make_unique<Message::RoF2>();
break;
case Version::RoF:
messageComponent = std::make_unique<Message::RoF>();
break;
case Version::UF:
messageComponent = std::make_unique<Message::UF>();
break;
case Version::SoD:
messageComponent = std::make_unique<Message::SoD>();
break;
case Version::SoF:
messageComponent = std::make_unique<Message::SoF>();
break;
case Version::Titanium:
messageComponent = std::make_unique<Message::Titanium>();
break;
default:
break;
}
}
const Version version;
std::unique_ptr<Message::IMessage> messageComponent;
};
// this array must be in the same order as the Version enum because it converts Version to index directly
static const std::array<ClientComponents, EQ::versions::ClientVersionCount> s_patches = {
{
ClientComponents(Version::Unknown), // empty
ClientComponents(Version::Client62), // empty
ClientComponents(Version::Titanium),
ClientComponents(Version::SoF),
ClientComponents(Version::SoD),
ClientComponents(Version::UF),
ClientComponents(Version::RoF),
ClientComponents(Version::RoF2),
ClientComponents(Version::TOB),
}
};
const std::unique_ptr<Message::IMessage>& GetMessageComponent(Version version)
{
return s_patches.at(static_cast<uint32_t>(version)).messageComponent;
}
+13
View File
@@ -0,0 +1,13 @@
//
// Created by dannu on 4/21/2026.
//
#pragma once
#include "common/emu_versions.h"
#include <memory>
namespace Message { class IMessage; }
// store all static functions for the different patches here, this can return nullptr for unsupported patches
const std::unique_ptr<Message::IMessage>& GetMessageComponent(EQ::versions::ClientVersion version);
+3
View File
@@ -21,6 +21,7 @@
#include "common/patches/rof2.h"
#include "common/patches/sod.h"
#include "common/patches/sof.h"
#include "common/patches/tob.h"
#include "common/patches/titanium.h"
#include "common/patches/uf.h"
@@ -33,6 +34,7 @@ void RegisterAllPatches(EQStreamIdentifier &into)
UF::Register(into);
RoF::Register(into);
RoF2::Register(into);
TOB::Register(into);
}
void ReloadAllPatches()
@@ -43,4 +45,5 @@ void ReloadAllPatches()
UF::Reload();
RoF::Reload();
RoF2::Reload();
TOB::Reload();
}
+12
View File
@@ -17,6 +17,7 @@
*/
#pragma once
#include "uf.h"
#include "common/struct_strategy.h"
class EQStreamIdentifier;
@@ -48,3 +49,14 @@ namespace RoF
};
} /*RoF*/
namespace Message {
class RoF : public UF
{
public:
RoF() = default;
~RoF() override = default;
};
} // namespace Message
+12
View File
@@ -17,6 +17,7 @@
*/
#pragma once
#include "rof.h"
#include "common/struct_strategy.h"
class EQStreamIdentifier;
@@ -48,3 +49,14 @@ namespace RoF2
};
}; /*RoF2*/
namespace Message {
class RoF2 : public RoF
{
public:
RoF2() = default;
~RoF2() override = default;
};
} // namespace Message
+12
View File
@@ -17,6 +17,7 @@
*/
#pragma once
#include "sof.h"
#include "common/struct_strategy.h"
class EQStreamIdentifier;
@@ -48,3 +49,14 @@ namespace SoD
};
} /*SoD*/
namespace Message {
class SoD : public SoF
{
public:
SoD() = default;
~SoD() override = default;
};
} // namespace Message
+12
View File
@@ -17,6 +17,7 @@
*/
#pragma once
#include "titanium.h"
#include "common/struct_strategy.h"
class EQStreamIdentifier;
@@ -48,3 +49,14 @@ namespace SoF
};
} /*SoF*/
namespace Message {
class SoF : public Titanium
{
public:
SoF() = default;
~SoF() override = default;
};
} // namespace Message
+96
View File
@@ -32,6 +32,7 @@
#include "common/raid.h"
#include "common/rulesys.h"
#include "common/strings.h"
#include "zone/string_ids.h"
#include <sstream>
@@ -3919,3 +3920,98 @@ namespace Titanium
return index; // as long as we guard against bad slots server side, we should be fine
}
} /*Titanium*/
namespace Message {
std::unique_ptr<EQApplicationPacket> Titanium::Simple(uint32_t color, uint32_t id) const
{
uint32_t string_id = ResolveID(id);
if (string_id > 0) {
auto outapp = std::make_unique<EQApplicationPacket>(OP_SimpleMessage, sizeof(SimpleMessage_Struct));
auto* sms = reinterpret_cast<SimpleMessage_Struct*>(outapp->pBuffer);
sms->string_id = string_id;
sms->color = color;
sms->unknown8 = 0;
return outapp;
}
return nullptr;
}
std::unique_ptr<EQApplicationPacket> Titanium::Formatted(
uint32_t color, uint32_t id, const std::array<const char*, 9>& args) const
{
uint32_t string_id = ResolveID(id);
if (string_id > 0) {
std::array<const char*, 9> resolved_args = args;
ResolveArguments(id, resolved_args);
if (!resolved_args[0])
return Simple(color, id);
SerializeBuffer buf(20);
buf.WriteUInt32(0);
buf.WriteUInt32(string_id);
buf.WriteUInt32(color);
for (const auto* a : resolved_args) {
if (a != nullptr)
buf.WriteString(a);
}
buf.WriteUInt8(0);
return std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
}
return nullptr;
}
std::unique_ptr<EQApplicationPacket> Titanium::InterruptSpell(uint32_t message, uint32_t spawn_id,
const char* spell_link) const
{
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct));
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
ic->messageid = ResolveID(message);
ic->spawnid = spawn_id;
outapp->priority = 5;
return outapp;
}
std::unique_ptr<EQApplicationPacket> Titanium::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
const char* name,
const char* spell_link) const
{
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1);
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
ic->messageid = ResolveID(message);
ic->spawnid = spawn_id;
fmt::format_to_n(ic->message, strlen(name) + 1, "{}\0", name);
return outapp;
}
// A value of 0 means that the string isn't mapped in this client, valid string ids start at 1
uint32_t Titanium::ResolveID(uint32_t id) const
{
// passthrough — string IDs are defined at the base client level;
// override in patches where IDs need remapping
return id;
}
void Titanium::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
{
switch (id) {
case SPELL_FIZZLE:
case MISS_NOTE:
args[0] = nullptr; // drop spell link
break;
case SPELL_FIZZLE_OTHER:
case MISSED_NOTE_OTHER:
args[1] = nullptr; // drop spell link
break;
default:
break;
}
}
} // namespace Message
+28
View File
@@ -17,6 +17,7 @@
*/
#pragma once
#include "IMessage.h"
#include "common/struct_strategy.h"
class EQStreamIdentifier;
@@ -48,3 +49,30 @@ namespace Titanium
};
} /*Titanium*/
// out-going message packets
namespace Message {
class Titanium : public IMessage
{
public:
Titanium() = default;
~Titanium() override = default;
std::unique_ptr<EQApplicationPacket> Simple(uint32_t color, uint32_t id) const override;
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
const std::array<const char*, 9>& args) const override;
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
const char* spell_link) const override;
std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
const char* name,
const char* spell_link) const override;
protected:
[[nodiscard]] virtual uint32_t ResolveID(uint32_t id) const;
virtual void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const;
};
} // namespace Message
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
#pragma once
#include "rof2.h"
#include "../struct_strategy.h"
class EQStreamIdentifier;
namespace TOB
{
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier& into);
extern void Reload();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "tob_ops.h"
};
}; /*TOB*/
namespace Message {
class TOB : public RoF2
{
public:
TOB() {}
~TOB() override {}
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
const std::array<const char*, 9>& args) const override;
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
const char* spell_link) const override;
std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
const char* name, const char* spell_link) const override;
protected:
[[nodiscard]] uint32_t ResolveID(uint32_t id) const override;
void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const override;
};
} // namespace Message
+265
View File
@@ -0,0 +1,265 @@
#include "tob_limits.h"
#include "../strings.h"
int16 TOB::invtype::GetInvTypeSize(int16 inv_type)
{
switch (inv_type) {
case invtype::typePossessions:
return invtype::POSSESSIONS_SIZE;
case invtype::typeBank:
return invtype::BANK_SIZE;
case invtype::typeSharedBank:
return invtype::SHARED_BANK_SIZE;
case invtype::typeTrade:
return invtype::TRADE_SIZE;
case invtype::typeWorld:
return invtype::WORLD_SIZE;
case invtype::typeLimbo:
return invtype::LIMBO_SIZE;
case invtype::typeTribute:
return invtype::TRIBUTE_SIZE;
case invtype::typeTrophyTribute:
return invtype::TROPHY_TRIBUTE_SIZE;
case invtype::typeGuildTribute:
return invtype::GUILD_TRIBUTE_SIZE;
case invtype::typeMerchant:
return invtype::MERCHANT_SIZE;
case invtype::typeDeleted:
return invtype::DELETED_SIZE;
case invtype::typeCorpse:
return invtype::CORPSE_SIZE;
case invtype::typeBazaar:
return invtype::BAZAAR_SIZE;
case invtype::typeInspect:
return invtype::INSPECT_SIZE;
case invtype::typeRealEstate:
return invtype::REAL_ESTATE_SIZE;
case invtype::typeViewMODPC:
return invtype::VIEW_MOD_PC_SIZE;
case invtype::typeViewMODBank:
return invtype::VIEW_MOD_BANK_SIZE;
case invtype::typeViewMODSharedBank:
return invtype::VIEW_MOD_SHARED_BANK_SIZE;
case invtype::typeViewMODLimbo:
return invtype::VIEW_MOD_LIMBO_SIZE;
case invtype::typeAltStorage:
return invtype::ALT_STORAGE_SIZE;
case invtype::typeArchived:
return invtype::ARCHIVED_SIZE;
case invtype::typeMail:
return invtype::MAIL_SIZE;
case invtype::typeGuildTrophyTribute:
return invtype::GUILD_TROPHY_TRIBUTE_SIZE;
case invtype::typeKrono:
return invtype::KRONO_SIZE;
case invtype::typeOther:
return invtype::OTHER_SIZE;
default:
return INULL;
}
}
const char* TOB::invtype::GetInvTypeName(int16 inv_type)
{
switch (inv_type) {
case invtype::TYPE_INVALID:
return "Invalid Type";
case invtype::typePossessions:
return "Possessions";
case invtype::typeBank:
return "Bank";
case invtype::typeSharedBank:
return "Shared Bank";
case invtype::typeTrade:
return "Trade";
case invtype::typeWorld:
return "World";
case invtype::typeLimbo:
return "Limbo";
case invtype::typeTribute:
return "Tribute";
case invtype::typeTrophyTribute:
return "Trophy Tribute";
case invtype::typeGuildTribute:
return "Guild Tribute";
case invtype::typeMerchant:
return "Merchant";
case invtype::typeDeleted:
return "Deleted";
case invtype::typeCorpse:
return "Corpse";
case invtype::typeBazaar:
return "Bazaar";
case invtype::typeInspect:
return "Inspect";
case invtype::typeRealEstate:
return "Real Estate";
case invtype::typeViewMODPC:
return "View MOD PC";
case invtype::typeViewMODBank:
return "View MOD Bank";
case invtype::typeViewMODSharedBank:
return "View MOD Shared Bank";
case invtype::typeViewMODLimbo:
return "View MOD Limbo";
case invtype::typeAltStorage:
return "Alt Storage";
case invtype::typeArchived:
return "Archived";
case invtype::typeMail:
return "Mail";
case invtype::typeGuildTrophyTribute:
return "Guild Trophy Tribute";
case invtype::typeKrono:
return "Krono";
case invtype::typeOther:
return "Other";
default:
return "Unknown Type";
}
}
bool TOB::invtype::IsInvTypePersistent(int16 inv_type)
{
switch (inv_type) {
case invtype::typePossessions:
case invtype::typeBank:
case invtype::typeSharedBank:
case invtype::typeTrade:
case invtype::typeWorld:
case invtype::typeLimbo:
case invtype::typeTribute:
case invtype::typeTrophyTribute:
case invtype::typeGuildTribute:
return true;
default:
return false;
}
}
const char* TOB::invslot::GetInvPossessionsSlotName(int16 inv_slot)
{
switch (inv_slot) {
case invslot::SLOT_INVALID:
return "Invalid Slot";
case invslot::slotCharm:
return "Charm";
case invslot::slotEar1:
return "Ear 1";
case invslot::slotHead:
return "Head";
case invslot::slotFace:
return "Face";
case invslot::slotEar2:
return "Ear 2";
case invslot::slotNeck:
return "Neck";
case invslot::slotShoulders:
return "Shoulders";
case invslot::slotArms:
return "Arms";
case invslot::slotBack:
return "Back";
case invslot::slotWrist1:
return "Wrist 1";
case invslot::slotWrist2:
return "Wrist 2";
case invslot::slotRange:
return "Range";
case invslot::slotHands:
return "Hands";
case invslot::slotPrimary:
return "Primary";
case invslot::slotSecondary:
return "Secondary";
case invslot::slotFinger1:
return "Finger 1";
case invslot::slotFinger2:
return "Finger 2";
case invslot::slotChest:
return "Chest";
case invslot::slotLegs:
return "Legs";
case invslot::slotFeet:
return "Feet";
case invslot::slotWaist:
return "Waist";
case invslot::slotPowerSource:
return "Power Source";
case invslot::slotAmmo:
return "Ammo";
case invslot::slotGeneral1:
return "General 1";
case invslot::slotGeneral2:
return "General 2";
case invslot::slotGeneral3:
return "General 3";
case invslot::slotGeneral4:
return "General 4";
case invslot::slotGeneral5:
return "General 5";
case invslot::slotGeneral6:
return "General 6";
case invslot::slotGeneral7:
return "General 7";
case invslot::slotGeneral8:
return "General 8";
case invslot::slotGeneral9:
return "General 9";
case invslot::slotGeneral10:
return "General 10";
case invslot::slotCursor:
return "Cursor";
default:
return "Unknown Slot";
}
}
const char* TOB::invslot::GetInvSlotName(int16 inv_type, int16 inv_slot)
{
if (inv_type == invtype::typePossessions)
return invslot::GetInvPossessionsSlotName(inv_slot);
int16 type_size = invtype::GetInvTypeSize(inv_type);
if (!type_size || inv_slot == invslot::SLOT_INVALID)
return "Invalid Slot";
if ((inv_slot + 1) >= type_size)
return "Unknown Slot";
static std::string ret_str;
ret_str = StringFormat("Slot %i", (inv_slot + 1));
return ret_str.c_str();
}
const char* TOB::invbag::GetInvBagIndexName(int16 bag_index)
{
if (bag_index == invbag::SLOT_INVALID)
return "Invalid Bag";
if (bag_index >= invbag::SLOT_COUNT)
return "Unknown Bag";
static std::string ret_str;
ret_str = StringFormat("Bag %i", (bag_index + 1));
return ret_str.c_str();
}
const char* TOB::invaug::GetInvAugIndexName(int16 aug_index)
{
if (aug_index == invaug::SOCKET_INVALID)
return "Invalid Augment";
if (aug_index >= invaug::SOCKET_COUNT)
return "Unknown Augment";
static std::string ret_str;
ret_str = StringFormat("Augment %i", (aug_index + 1));
return ret_str.c_str();
}
+337
View File
@@ -0,0 +1,337 @@
#ifndef COMMON_LAURION_LIMITS_H
#define COMMON_LAURION_LIMITS_H
#include "../types.h"
#include "../emu_versions.h"
#include "../skills.h"
namespace TOB
{
const int16 IINVALID = -1;
const int16 INULL = 0;
namespace inventory {
inline EQ::versions::ClientVersion GetInventoryRef() { return EQ::versions::ClientVersion::TOB; }
const bool ConcatenateInvTypeLimbo = false;
const bool AllowOverLevelEquipment = true;
const bool AllowEmptyBagInBag = true;
const bool AllowClickCastFromBag = true;
} /*inventory*/
namespace invtype {
inline EQ::versions::ClientVersion GetInvTypeRef() { return EQ::versions::ClientVersion::TOB; }
namespace enum_ {
enum InventoryTypes : int16 {
typePossessions = INULL,
typeBank,
typeSharedBank,
typeTrade,
typeWorld,
typeLimbo,
typeTribute,
typeTrophyTribute,
typeGuildTribute,
typeMerchant,
typeDeleted,
typeCorpse,
typeBazaar,
typeInspect,
typeRealEstate,
typeViewMODPC,
typeViewMODBank,
typeViewMODSharedBank,
typeViewMODLimbo,
typeAltStorage,
typeArchived,
typeMail,
typeGuildTrophyTribute,
typeKrono,
typeOther,
typeMercenaryItems,
typeViewModMercenaryItems,
typeMountKeyRingItems,
typeViewModMountKeyRingItems,
typeIllusionKeyRingItems,
typeViewModIllusionKeyRingItems,
typeFamiliarKeyRingItems,
typeViewModFamiliarKeyRingItems,
typeHeroForgeKeyRingItems,
typeViewModHeroForgeKeyRingItems,
typeTeleportationKeyRingItems,
typeViewModTeleportationKeyRingItems,
typeOverflow,
typeDragonHoard,
typeTradeskillDepot,
typeGuildTradeskillDepot
};
} // namespace enum_
using namespace enum_;
const int16 POSSESSIONS_SIZE = 34;
const int16 BANK_SIZE = 24;
const int16 SHARED_BANK_SIZE = 2;
const int16 TRADE_SIZE = 8;
const int16 WORLD_SIZE = 10;
const int16 LIMBO_SIZE = 36;
const int16 TRIBUTE_SIZE = 5;
const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown
const int16 GUILD_TRIBUTE_SIZE = 2;//unverified
const int16 MERCHANT_SIZE = 200;
const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab"
const int16 CORPSE_SIZE = POSSESSIONS_SIZE;
const int16 BAZAAR_SIZE = 200;
const int16 INSPECT_SIZE = 23;
const int16 REAL_ESTATE_SIZE = 0;//unknown
const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE;
const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE;
const int16 VIEW_MOD_SHARED_BANK_SIZE = SHARED_BANK_SIZE;
const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE;
const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank"
const int16 ARCHIVED_SIZE = 0;//unknown
const int16 MAIL_SIZE = 0;//unknown
const int16 GUILD_TROPHY_TRIBUTE_SIZE = 0;//unknown
const int16 KRONO_SIZE = 0;//unknown
const int16 OTHER_SIZE = 0;//unknown
const int16 TRADE_NPC_SIZE = 4; // defined by implication
const int16 TYPE_INVALID = IINVALID;
const int16 TYPE_BEGIN = typePossessions;
const int16 TYPE_END = typeOther;
const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1;
int16 GetInvTypeSize(int16 inv_type);
const char* GetInvTypeName(int16 inv_type);
bool IsInvTypePersistent(int16 inv_type);
} /*invtype*/
namespace invslot {
inline EQ::versions::ClientVersion GetInvSlotRef() { return EQ::versions::ClientVersion::TOB; }
namespace enum_ {
enum InventorySlots : int16 {
slotCharm = INULL,
slotEar1,
slotHead,
slotFace,
slotEar2,
slotNeck,
slotShoulders,
slotArms,
slotBack,
slotWrist1,
slotWrist2,
slotRange,
slotHands,
slotPrimary,
slotSecondary,
slotFinger1,
slotFinger2,
slotChest,
slotLegs,
slotFeet,
slotWaist,
slotPowerSource,
slotAmmo,
slotGeneral1,
slotGeneral2,
slotGeneral3,
slotGeneral4,
slotGeneral5,
slotGeneral6,
slotGeneral7,
slotGeneral8,
slotGeneral9,
slotGeneral10,
slotGeneral11,
slotGeneral12,
slotCursor
};
constexpr int16 format_as(InventorySlots slot) { return static_cast<int16>(slot); }
} // namespace enum_
using namespace enum_;
const int16 SLOT_INVALID = IINVALID;
const int16 SLOT_BEGIN = INULL;
const int16 POSSESSIONS_BEGIN = slotCharm;
const int16 POSSESSIONS_END = slotCursor;
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
const int16 EQUIPMENT_BEGIN = slotCharm;
const int16 EQUIPMENT_END = slotAmmo;
const int16 EQUIPMENT_COUNT = (EQUIPMENT_END - EQUIPMENT_BEGIN) + 1;
//We support more if enabled but for now lets leave it at the 10 slots
const int16 GENERAL_BEGIN = slotGeneral1;
const int16 GENERAL_END = slotGeneral10;
const int16 GENERAL_COUNT = (GENERAL_END - GENERAL_BEGIN) + 1;
const int16 BONUS_BEGIN = invslot::slotCharm;
const int16 BONUS_STAT_END = invslot::slotPowerSource;
const int16 BONUS_SKILL_END = invslot::slotAmmo;
const int16 CORPSE_BEGIN = invslot::slotGeneral1;
const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor;
const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF;
const uint64 GENERAL_BITMASK = 0x00000007FF800000;
const uint64 CURSOR_BITMASK = 0x0000000800000000;
const uint64 POSSESSIONS_BITMASK = (EQUIPMENT_BITMASK | GENERAL_BITMASK | CURSOR_BITMASK); // based on 36-slot count (TOB+)
const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 36)); // based on 36-slot count (TOB+)
const char* GetInvPossessionsSlotName(int16 inv_slot);
const char* GetInvSlotName(int16 inv_type, int16 inv_slot);
} /*invslot*/
namespace invbag {
inline EQ::versions::ClientVersion GetInvBagRef() { return EQ::versions::ClientVersion::TOB; }
const int16 SLOT_INVALID = IINVALID;
const int16 SLOT_BEGIN = INULL;
const int16 SLOT_END = 199;
const int16 SLOT_COUNT = 200; // server Size will be 200..unsure what actual client is (test)
const char* GetInvBagIndexName(int16 bag_index);
} /*invbag*/
namespace invaug {
inline EQ::versions::ClientVersion GetInvAugRef() { return EQ::versions::ClientVersion::TOB; }
const int16 SOCKET_INVALID = IINVALID;
const int16 SOCKET_BEGIN = INULL;
const int16 SOCKET_END = 5;
const int16 SOCKET_COUNT = 6;
const char* GetInvAugIndexName(int16 aug_index);
} /*invaug*/
namespace item {
inline EQ::versions::ClientVersion GetItemRef() { return EQ::versions::ClientVersion::TOB; }
//enum Unknown : int { // looks like item class..but, RoF has it too - nothing in UF-
// Unknown1 = 0,
// Unknown2 = 1,
// Unknown3 = 2,
// Unknown4 = 5 // krono?
//};
enum ItemPacketType : int {
ItemPacketMerchant = 0x64,
ItemPacketTradeView = 0x65,
ItemPacketLoot = 0x66,
ItemPacketTrade = 0x67,
//looks like they added something at 0x68 that didn't exist before and shifted everything after it up by 1
ItemPacketUnknown068 = 0x68, //Not sure but it seems to deal with the cursor somehow.
ItemPacketCharInventory = 0x6A, //Rof 0x69 -> Larion 0x6a (requires translation)
ItemPacketLimbo = 0x6B, //0x6A -> 0x6B
ItemPacketWorldContainer = 0x6C,
ItemPacketTributeItem = 0x6D,
ItemPacketGuildTribute = 0x6E,
ItemPacketCharmUpdate = 0x6f,
ItemPacketRecovery = 0x72,
ItemPacketParcel = 0x74,
ItemPacketUnknown075 = 0x75, //Not sure but uses a lot of the same logic as the trade and char inventory types
ItemPacketOverflow = 0x76,
ItemPacketDragonHoard = 0x77,
ItemPacketTradeskill = 0x78,
ItemPacketTradeskillDepot = 0x79,
ItemPacketInvalid = 0xFF
};
} /*item*/
namespace profile {
inline EQ::versions::ClientVersion GetProfileRef() { return EQ::versions::ClientVersion::TOB; }
const int16 BANDOLIERS_SIZE = 20; // number of bandolier instances
const int16 BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
const int16 POTION_BELT_SIZE = 5;
const int16 SKILL_ARRAY_SIZE = 100;
} /*profile*/
namespace constants {
inline EQ::versions::ClientVersion GetConstantsRef() { return EQ::versions::ClientVersion::TOB; }
const EQ::expansions::Expansion EXPANSION = EQ::expansions::Expansion::LS;
const uint32 EXPANSION_BIT = EQ::expansions::bitLS;
const uint32 EXPANSIONS_MASK = EQ::expansions::maskLS;
const size_t CHARACTER_CREATION_LIMIT = 12;
const size_t SAY_LINK_BODY_SIZE = 56;
const uint32 MAX_GUILD_ID = 50000;
const uint32 MAX_BAZAAR_TRADERS = 600;
} /*constants*/
namespace behavior {
inline EQ::versions::ClientVersion GetBehaviorRef() { return EQ::versions::ClientVersion::TOB; }
const bool CoinHasWeight = false;
} /*behavior*/
namespace skills {
inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::TOB; }
const size_t LastUsableSkill = EQ::skills::Skill2HPiercing;
} /*skills*/
namespace spells {
inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::TOB; }
enum class CastingSlot : uint32 {
Gem1 = 0,
Gem2 = 1,
Gem3 = 2,
Gem4 = 3,
Gem5 = 4,
Gem6 = 5,
Gem7 = 6,
Gem8 = 7,
Gem9 = 8,
Gem10 = 9,
Gem11 = 10,
Gem12 = 11,
MaxGems = 18, // fallacy..only 12 slot are useable...
Item = 12,
Discipline = 13,
AltAbility = 0xFF
};
const int SPELL_ID_MAX = 71999;
const int SPELLBOOK_SIZE = 1120;
const int SPELL_GEM_COUNT = static_cast<uint32>(CastingSlot::MaxGems);
const int SPELL_GEM_RECAST_TIMER = 15;
const int LONG_BUFFS = 42;
const int SHORT_BUFFS = 30;
const int DISC_BUFFS = 1;
const int TOTAL_BUFFS = LONG_BUFFS + SHORT_BUFFS + DISC_BUFFS;
const int NPC_BUFFS = 400;
const int PET_BUFFS = NPC_BUFFS;
const int MERC_BUFFS = LONG_BUFFS;
} /*spells*/
}; /* TOB */
#endif /*COMMON_LAURION_LIMITS_H*/
+100
View File
@@ -0,0 +1,100 @@
//list of packets we need to encode on the way out:
E(OP_AAExpUpdate)
E(OP_Action)
E(OP_Animation)
E(OP_ApplyPoison)
E(OP_AugmentInfo)
E(OP_BeginCast)
E(OP_BlockedBuffs)
E(OP_Buff)
E(OP_BuffCreate)
E(OP_CancelTrade)
E(OP_CastSpell)
E(OP_ChannelMessage)
E(OP_CharacterCreateRequest)
E(OP_CharInventory)
E(OP_ClickObjectAction)
E(OP_ClientUpdate)
E(OP_Consider)
E(OP_Damage)
E(OP_Death)
E(OP_DeleteCharge)
E(OP_DeleteItem)
E(OP_DeleteSpawn)
E(OP_DisciplineUpdate)
E(OP_ExpansionInfo)
E(OP_ExpUpdate)
E(OP_GMTraining)
E(OP_GMTrainSkillConfirm)
E(OP_GroundSpawn)
E(OP_HPUpdate)
E(OP_Illusion)
E(OP_ItemPacket)
E(OP_LogServer)
E(OP_ManaChange)
E(OP_MemorizeSpell)
E(OP_MobHealth)
E(OP_MoneyOnCorpse)
E(OP_MoveItem)
E(OP_NewSpawn)
E(OP_NewZone)
E(OP_OnLevelMessage)
E(OP_PlayerProfile)
E(OP_RemoveBlockedBuffs)
E(OP_RespondAA)
E(OP_RequestClientZoneChange)
E(OP_RecipeAutoCombine)
E(OP_SendAATable)
E(OP_SendCharInfo)
E(OP_SendMaxCharacters)
E(OP_SendMembership)
E(OP_SendMembershipDetails)
E(OP_SendZonepoints)
E(OP_ShopPlayerBuy)
E(OP_ShopPlayerSell)
E(OP_ShopRequest)
E(OP_SkillUpdate)
E(OP_SpecialMesg)
E(OP_SpawnAppearance)
E(OP_SpawnDoor)
E(OP_Stun)
E(OP_WearChange)
E(OP_ZoneChange)
E(OP_ZoneEntry)
E(OP_ZonePlayerToBind)
E(OP_ZoneSpawns)
//list of packets we need to decode on the way in:
D(OP_Animation)
D(OP_ApplyPoison)
D(OP_ApproveName)
D(OP_AugmentInfo)
D(OP_AugmentItem)
D(OP_BlockedBuffs)
D(OP_CastSpell)
D(OP_ChannelMessage)
D(OP_CharacterCreate)
D(OP_ClientUpdate)
D(OP_ClickDoor)
D(OP_Consider)
D(OP_ConsiderCorpse)
D(OP_DeleteItem)
D(OP_EnterWorld)
D(OP_GMTraining)
D(OP_GroupDisband)
D(OP_GroupInvite)
D(OP_GroupInvite2)
D(OP_MemorizeSpell)
D(OP_MoveItem)
D(OP_RemoveBlockedBuffs)
D(OP_SetServerFilter)
D(OP_ShopPlayerBuy)
D(OP_ShopPlayerSell)
D(OP_ShopRequest)
D(OP_SpawnAppearance)
D(OP_TradeSkillCombine)
D(OP_WearChange)
D(OP_ZoneEntry)
D(OP_ZoneChange)
#undef E
#undef D
File diff suppressed because it is too large Load Diff
+12
View File
@@ -17,6 +17,7 @@
*/
#pragma once
#include "sod.h"
#include "common/struct_strategy.h"
class EQStreamIdentifier;
@@ -48,3 +49,14 @@ namespace UF
};
}; /*UF*/
namespace Message {
class UF : public SoD
{
public:
UF() = default;
~UF() override = default;
};
} // namespace Message
+2 -1
View File
@@ -350,11 +350,12 @@ RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to ha
RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C")
RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame")
RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag")
RULE_STRING(World, SupportedClients, "RoF2", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2")
RULE_STRING(World, SupportedClients, "RoF2,TOB", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2 | TOB. Example: Titanium,RoF2,TOB")
RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1")
RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files")
RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified")
RULE_BOOL(World, RealTimeCalculateGuilds, false, "(Temp feature flag) If true, guilds will be calculated in real time instead of at zone boot. This is a performance hit but allows for more dynamic guilds.")
RULE_INT(World, Id, 100, "Used by later clients to create GUIDs, expected to be Unique to the world but ultimately not that important")
RULE_CATEGORY_END()
RULE_CATEGORY(Zone)
+41 -47
View File
@@ -88,6 +88,9 @@ bool Client::Process()
SendPlayToWorld((const char *) app->pBuffer);
break;
}
case OP_SystemFingerprint: {
break;
}
}
delete app;
@@ -104,16 +107,19 @@ void Client::HandleSessionReady(const char *data, unsigned int size)
return;
}
if (size < sizeof(unsigned int)) {
if (size < sizeof(int32)) {
LogError("Session ready was too small");
return;
}
//existing sequence id
int32 sequence_in = *(int32*)data;
m_client_status = cs_waiting_for_login;
auto *outapp = new EQApplicationPacket(OP_ChatMessage, sizeof(LoginHandShakeReply));
auto buf = reinterpret_cast<LoginHandShakeReply *>(outapp->pBuffer);
buf->base_header.sequence = 0x02;
buf->base_header.sequence = sequence_in;
buf->base_reply.success = true;
buf->base_reply.error_str_id = 0x65; // 101 "No Error"
@@ -296,20 +302,13 @@ void Client::SendPlayResponse(EQApplicationPacket *outapp)
void Client::GenerateRandomLoginKey()
{
m_key.clear();
int count = 0;
while (count < 10) {
static const char key_selection[] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9'
};
static constexpr std::string_view key_selection = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
constexpr size_t key_length = 10;
m_key.append((const char *) &key_selection[m_random.Int(0, 35)], 1);
count++;
m_key.clear();
m_key.reserve(key_length);
for (size_t i = 0; i < key_length; ++i) {
m_key += key_selection[m_random.Int(0, key_selection.size() - 1)];
}
}
@@ -358,16 +357,17 @@ void Client::SendFailedLogin()
// unencrypted
LoginBaseMessage h{};
h.sequence = m_login_base_message.sequence; // login (3)
h.sequence = m_login_base_message.sequence;
h.encrypt_type = m_login_base_message.encrypt_type;
h.unk3 = m_login_base_message.unk3;
// encrypted
PlayerLoginReply r{};
r.base_reply.success = false;
r.base_reply.error_str_id = 105; // Error - The username and/or password were not valid
PlayerLoginReply r = m_client_version == cv_tob ? PlayerLoginReply(PlayerLoginReplyTOB{}) : PlayerLoginReply(PlayerLoginReplyOld{});
r.set_error_code(LS::ErrStr::ERROR_INVALID_CREDS);
// We don't care what key we send, just that it exists and is 10 characters so that we do not shift
r.set_key("InvalidKey");
char encrypted_buffer[80] = {0};
auto rc = eqcrypt_block((const char *) &r, sizeof(r), encrypted_buffer, 1);
char encrypted_buffer[80] = { 0 };
auto rc = eqcrypt_block(r.data(), r.size(), encrypted_buffer, 1);
if (rc == nullptr) {
LogDebug("Failed to encrypt eqcrypt block for failed login");
}
@@ -378,6 +378,7 @@ void Client::SendFailedLogin()
outapp.WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
m_connection->QueuePacket(&outapp);
m_client_status = cs_failed_to_login;
}
@@ -465,40 +466,33 @@ void Client::DoSuccessfulLogin(LoginAccountsRepository::LoginAccounts &a)
// unencrypted
LoginBaseMessage h{};
h.sequence = m_login_base_message.sequence;
h.compressed = false;
h.sequence = m_login_base_message.sequence;
h.compressed = false;
h.encrypt_type = m_login_base_message.encrypt_type;
h.unk3 = m_login_base_message.unk3;
h.unk3 = m_login_base_message.unk3;
// not serializing any of the variable length strings so just use struct directly
PlayerLoginReply r{};
r.base_reply.success = true;
r.base_reply.error_str_id = 101; // No Error
r.unk1 = 0;
r.unk2 = 0;
r.lsid = a.id;
r.failed_attempts = 0;
r.show_player_count = server.options.IsShowPlayerCountEnabled();
r.offer_min_days = 99;
r.offer_min_views = -1;
r.offer_cooldown_minutes = 0;
r.web_offer_number = 0;
r.web_offer_min_days = 99;
r.web_offer_min_views = -1;
r.web_offer_cooldown_minutes = 0;
memcpy(r.key, m_key.c_str(), m_key.size());
PlayerLoginReply r = m_client_version == cv_tob ? PlayerLoginReply(PlayerLoginReplyTOB{}) : PlayerLoginReply(PlayerLoginReplyOld{});
SendExpansionPacketData(r);
r.set_success(true);
r.set_error_code(LS::ErrStr::ERROR_NONE);
r.set_lsid(a.id);
r.set_show_player_count(server.options.IsShowPlayerCountEnabled());
r.set_key(m_key);
char encrypted_buffer[80] = {0};
if (m_client_version != cv_tob) {
SendExpansionPacketData(r.old());
}
auto rc = eqcrypt_block((const char *) &r, sizeof(r), encrypted_buffer, 1);
char encrypted_buffer[80] = { 0 };
auto rc = eqcrypt_block(r.data(), r.size(), encrypted_buffer, 1);
if (rc == nullptr) {
LogDebug("Failed to encrypt eqcrypt block");
}
constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer);
auto outapp = std::make_unique<EQApplicationPacket>(OP_LoginAccepted, outsize);
auto outapp = std::make_unique<EQApplicationPacket>(OP_LoginAccepted, outsize);
outapp->WriteData(&h, sizeof(h));
outapp->WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
@@ -507,7 +501,7 @@ void Client::DoSuccessfulLogin(LoginAccountsRepository::LoginAccounts &a)
m_client_status = cs_logged_in;
}
void Client::SendExpansionPacketData(PlayerLoginReply &plrs)
void Client::SendExpansionPacketData(PlayerLoginReplyOld &plrs)
{
SerializeBuffer buf;
//from eqlsstr_us.txt id of each expansion, excluding 'Everquest'
@@ -534,7 +528,7 @@ void Client::SendExpansionPacketData(PlayerLoginReply &plrs)
//generate expansion data
for (int i = 0; i < 19; i++) {
buf.WriteInt32(i); //sequenctial number
buf.WriteInt32(i); //sequential number
buf.WriteInt32((expansion & (1 << i)) == (1 << i) ? 0x01 : 0x00); //1 own 0 not own
buf.WriteInt8(0x00);
buf.WriteInt32(ExpansionLookup[i]); //from eqlsstr_us.txt
+1 -1
View File
@@ -38,7 +38,7 @@ public:
// Titanium uses the encrypted data block to contact the expansion (You own xxx:) and the max expansions (of yyy)
// Rof uses a separate data packet specifically for the expansion data
// Live, as of July 2021 uses a similar but slightly different seperate data packet
void SendExpansionPacketData(PlayerLoginReply &plrs);
void SendExpansionPacketData(PlayerLoginReplyOld &plrs);
void SendPlayToWorld(const char *data);
void SendServerListPacket(uint32 seq);
void SendPlayResponse(EQApplicationPacket *outapp);
+20 -20
View File
@@ -74,7 +74,7 @@ void CheckSoDOpcodeFile(const std::string &path)
}
}
void CheckLarionOpcodeFile(const std::string &path)
void CheckTOBOpcodeFile(const std::string &path)
{
if (File::Exists(path)) {
return;
@@ -87,15 +87,15 @@ void CheckLarionOpcodeFile(const std::string &path)
fprintf(f, "OP_Login=0x0002\n");
fprintf(f, "OP_ServerListRequest=0x0004\n");
fprintf(f, "OP_PlayEverquestRequest=0x000d\n");
fprintf(f, "OP_PlayEverquestResponse=0x0022\n");
fprintf(f, "OP_ChatMessage=0x0017\n");
fprintf(f, "OP_LoginAccepted=0x0018\n");
fprintf(f, "OP_ServerListResponse=0x0019\n");
fprintf(f, "OP_Poll=0x0029\n");
fprintf(f, "OP_PlayEverquestResponse=0x0023\n");
fprintf(f, "OP_ChatMessage=0x0018\n");
fprintf(f, "OP_LoginAccepted=0x0019\n");
fprintf(f, "OP_ServerListResponse=0x001a\n");
fprintf(f, "OP_Poll=0x002a\n");
fprintf(f, "OP_EnterChat=0x000f\n");
fprintf(f, "OP_PollResponse=0x0011\n");
fprintf(f, "OP_SystemFingerprint=0x0016\n");
fprintf(f, "OP_ExpansionList=0x0030\n");
fprintf(f, "OP_ExpansionList=0x0031\n");
fclose(f);
}
}
@@ -177,40 +177,40 @@ ClientManager::ClientManager()
}
);
int larion_port = server.config.GetVariableInt("client_configuration", "larion_port", 15900);
int tob_port = server.config.GetVariableInt("client_configuration", "tob_port", 15900);
EQStreamManagerInterfaceOptions larion_opts(larion_port, false, false);
EQStreamManagerInterfaceOptions tob_opts(tob_port, false, false);
m_larion_stream = new EQ::Net::EQStreamManager(larion_opts);
m_larion_ops = new RegularOpcodeManager;
m_tob_stream = new EQ::Net::EQStreamManager(tob_opts);
m_tob_ops = new RegularOpcodeManager;
opcodes_path = fmt::format(
"{}/{}",
PathManager::Instance()->GetOpcodePath(),
"login_opcodes_larion.conf"
"login_opcodes_tob.conf"
);
CheckLarionOpcodeFile(opcodes_path);
CheckTOBOpcodeFile(opcodes_path);
if (!m_larion_ops->LoadOpcodes(opcodes_path.c_str())) {
if (!m_tob_ops->LoadOpcodes(opcodes_path.c_str())) {
LogError(
"ClientManager fatal error: couldn't load opcodes for Larion file [{}]",
server.config.GetVariableString("client_configuration", "larion_opcodes", "login_opcodes.conf")
"ClientManager fatal error: couldn't load opcodes for TOB file [{}]",
server.config.GetVariableString("client_configuration", "tob_opcodes", "login_opcodes.conf")
);
run_server = false;
}
m_larion_stream->OnNewConnection(
m_tob_stream->OnNewConnection(
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
LogInfo(
"New Larion client connection from [{}:{}]",
"New TOB client connection from [{}:{}]",
long2ip(stream->GetRemoteIP()),
stream->GetRemotePort()
);
stream->SetOpcodeManager(&m_larion_ops);
Client *c = new Client(stream, cv_larion);
stream->SetOpcodeManager(&m_tob_ops);
Client *c = new Client(stream, cv_tob);
m_clients.push_back(c);
}
);
+2 -2
View File
@@ -37,6 +37,6 @@ private:
EQ::Net::EQStreamManager *m_titanium_stream;
OpcodeManager *m_sod_ops;
EQ::Net::EQStreamManager *m_sod_stream;
OpcodeManager *m_larion_ops;
EQ::Net::EQStreamManager *m_larion_stream;
OpcodeManager *m_tob_ops;
EQ::Net::EQStreamManager *m_tob_stream;
};
+93 -3
View File
@@ -20,6 +20,7 @@
#include "common/types.h"
#include <string>
#include <variant>
#pragma pack(push)
#pragma pack(1)
@@ -45,7 +46,7 @@ struct LoginHandShakeReply {
};
// variable length, can use directly if not serializing strings
struct PlayerLoginReply {
struct PlayerLoginReplyOld {
// base header excluded to make struct data easier to encrypt
//LoginBaseMessage base_header;
LoginBaseReplyMessage base_reply;
@@ -61,10 +62,90 @@ struct PlayerLoginReply {
int32_t offer_cooldown_minutes; // guess (default: 0)
int32_t web_offer_number; // web order view number, 0 nothing (default: 0)
int32_t web_offer_min_days; // number of days to show offer (based on first offer time in client eqls ini) (default: 99)
int32_t web_offer_min_views; // mininum views, -1 for no minimum, 0 for never shows (based on client eqls ini) (default: -1)
int32_t web_offer_min_views; // minimum views, -1 for no minimum, 0 for never shows (based on client eqls ini) (default: -1)
int32_t web_offer_cooldown_minutes; // minimum minutes between offers (based on last offer time in client eqls ini) (default: 0)
char username[1]; // variable length, if not empty client attempts to re-login to server select when quitting from char select and sends this in a struct
char unknown[1]; // variable length, password unlikely? client doesn't send this on re-login from char select
void set_success(bool v) { base_reply.success = v; }
void set_error_code(int32_t v) { base_reply.error_str_id = v; }
void set_lsid(int32_t v) { lsid = v; }
void set_show_player_count(bool v) { show_player_count = v; }
void set_hardcoded_success_values() {
offer_min_days = 99;
offer_min_views = -1;
web_offer_min_days = 99;
web_offer_min_views = -1;
}
};
static_assert(sizeof(PlayerLoginReplyOld) == 58, "PlayerLoginReplyOld struct size does not match expected size");
static_assert(std::is_trivially_copyable_v<PlayerLoginReplyOld>);
static_assert(std::is_standard_layout_v<PlayerLoginReplyOld>);
struct PlayerLoginReplyTOB {
LoginBaseReplyMessage base_reply;
int8_t unk1 = 0;
int8_t unk2 = 0;
int32_t lsid = -1;
char key[11] = {};
int32_t failed_attempts = 0;
int32_t display_error_str_id = 0;
int32_t unk3 = 0;
bool show_player_count = false;
char username[1] = {};
char unk4[1] = {};
void set_success(bool v) { base_reply.success = v; }
void set_error_code(int32_t v) { display_error_str_id = v; base_reply.error_str_id = v; }
void set_lsid(int32_t v) { lsid = v; }
void set_show_player_count(bool v) { show_player_count = v; }
void set_hardcoded_success_values() {}
};
static_assert(sizeof(PlayerLoginReplyTOB) == 38, "PlayerLoginReplyTOB struct size does not match expected size");
static_assert(std::is_trivially_copyable_v<PlayerLoginReplyTOB>);
static_assert(std::is_standard_layout_v<PlayerLoginReplyTOB>);
class PlayerLoginReply {
std::variant<PlayerLoginReplyOld, PlayerLoginReplyTOB> v_;
static_assert(sizeof(PlayerLoginReplyOld::key) == sizeof(PlayerLoginReplyTOB::key), "Old and TOB key buffers must match in size due to code assumptions");
public:
PlayerLoginReply(PlayerLoginReplyOld s) : v_(s) {}
PlayerLoginReply(PlayerLoginReplyTOB s) : v_(s) {}
void set_success(bool val) {
std::visit([val](auto& s) { s.set_success(val); }, v_);
}
void set_error_code(int32_t val) {
std::visit([val](auto& s) { s.set_error_code(val); }, v_);
}
void set_lsid(int32_t val) {
std::visit([val](auto& s) { s.set_lsid(val); }, v_);
}
void set_show_player_count(bool val) {
std::visit([val](auto& s) { s.set_show_player_count(val); }, v_);
}
void set_key(std::string_view s) {
std::visit([&](auto& st) {
const size_t n = s.copy(st.key, sizeof(st.key) - 1);
st.key[n] = '\0';
}, v_);
}
template<size_t N>
void set_key(const char (&s)[N]) {
static_assert(N != (sizeof(PlayerLoginReplyTOB::key) - 1), "Key literal does not match reply struct's key buffer (without null terminator)");
set_key(std::string_view{s, N - 1});
}
PlayerLoginReplyOld& old() { return std::get<PlayerLoginReplyOld>(v_); }
const PlayerLoginReplyOld& old() const { return std::get<PlayerLoginReplyOld>(v_); }
char* data() noexcept {
return std::visit([](auto& s) { return reinterpret_cast<char*>(&s); }, v_);
}
size_t size() const noexcept {
return std::visit([](auto const& s) { return sizeof(s); }, v_);
}
};
// variable length, for reference
@@ -100,12 +181,18 @@ struct PlayEverquestResponse {
uint32 server_number;
};
//for reference
struct SystemFingerprint {
LoginBaseMessage base_header;
char fingerprint[1];
};
#pragma pack()
enum LSClientVersion {
cv_titanium,
cv_sod,
cv_larion
cv_tob
};
enum LSClientStatus {
@@ -174,11 +261,14 @@ namespace LS {
namespace ErrStr {
constexpr static int ERROR_NONE = 101; // No Error
constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred
constexpr static int ERROR_INVALID_CREDS = 105; // Error - Invalid Account Name or Password
constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again.
constexpr static int ERROR_PASSWORD_RESET = 112; // Require password reset
constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later.
constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information.
constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information.
constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later.
constexpr static int ERROR_REQUIRE_2FA = 342; // This account requires two-factor authentication.
};
}
@@ -3,12 +3,12 @@ OP_SessionReady=0x0001
OP_Login=0x0002
OP_ServerListRequest=0x0004
OP_PlayEverquestRequest=0x000d
OP_PlayEverquestResponse=0x0022
OP_ChatMessage=0x0017
OP_LoginAccepted=0x0018
OP_ServerListResponse=0x0019
OP_Poll=0x0029
OP_PlayEverquestResponse=0x0023
OP_ChatMessage=0x0018
OP_LoginAccepted=0x0019
OP_ServerListResponse=0x001a
OP_Poll=0x002a
OP_EnterChat=0x000f
OP_PollResponse=0x0011
OP_SystemFingerprint=0x0016
OP_ExpansionList=0x0030
OP_ExpansionList=0x0031
+68 -31
View File
@@ -706,19 +706,17 @@ bool WorldServer::ValidateWorldServerAdminLogin(
void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip, LSClientVersion version) const
{
// see LoginClientServerData_Struct
if (use_local_ip) {
out.WriteString(GetLocalIP());
}
else {
out.WriteString(m_remote_ip_address);
}
if (version == cv_tob) {
if (use_local_ip) {
out.WriteString(GetLocalIP());
}
else {
out.WriteString(m_remote_ip_address);
}
if (version == cv_larion) {
out.WriteUInt32(9000);
}
out.WriteInt32(9000); // port, not currently settable in eqemu but needed for compat
switch (GetServerListID()) {
switch (GetServerListID()) {
case LS::ServerType::Legends:
out.WriteInt32(LS::ServerTypeFlags::Legends);
break;
@@ -728,35 +726,74 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_lo
default:
out.WriteInt32(LS::ServerTypeFlags::Standard);
break;
}
if (version == cv_larion) {
auto server_id = m_server_id;
//if this is 0, the client will not show the server in the list
out.WriteUInt32(1);
out.WriteUInt32(server_id);
}
else {
}
out.WriteInt32(289); //unsure what this is yet
out.WriteUInt32(m_server_id);
}
out.WriteString(m_server_long_name);
out.WriteString("us"); // country code
out.WriteString("en"); // language code
out.WriteString(m_server_long_name);
out.WriteString("US"); // country code
out.WriteString("EN"); // language code
out.WriteString("Standard");
out.WriteString("This server has no description set currently.");
// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
if (GetStatus() < 0) {
if (GetZonesBooted() == 0) {
out.WriteInt32(LS::ServerStatusFlags::Down);
if (GetStatus() < 0) {
if (GetZonesBooted() == 0) {
out.WriteInt32(LS::ServerStatusFlags::Down);
}
else {
out.WriteInt32(LS::ServerStatusFlags::Locked);
}
}
else {
out.WriteInt32(LS::ServerStatusFlags::Locked);
out.WriteInt32(LS::ServerStatusFlags::Up);
}
out.WriteUInt32(GetPlayersOnline());
out.WriteInt32(31); //expansions
out.WriteInt32(0); //truebox
}
else {
out.WriteInt32(LS::ServerStatusFlags::Up);
}
// see LoginClientServerData_Struct
if (use_local_ip) {
out.WriteString(GetLocalIP());
}
else {
out.WriteString(m_remote_ip_address);
}
switch (GetServerListID()) {
case LS::ServerType::Legends:
out.WriteInt32(LS::ServerTypeFlags::Legends);
break;
case LS::ServerType::Preferred:
out.WriteInt32(LS::ServerTypeFlags::Preferred);
break;
default:
out.WriteInt32(LS::ServerTypeFlags::Standard);
break;
}
out.WriteUInt32(GetPlayersOnline());
out.WriteUInt32(m_server_id);
out.WriteString(m_server_long_name);
out.WriteString("us"); // country code
out.WriteString("en"); // language code
// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
if (GetStatus() < 0) {
if (GetZonesBooted() == 0) {
out.WriteInt32(LS::ServerStatusFlags::Down);
}
else {
out.WriteInt32(LS::ServerStatusFlags::Locked);
}
}
else {
out.WriteInt32(LS::ServerStatusFlags::Up);
}
out.WriteUInt32(GetPlayersOnline());
}
}
void WorldServer::FormatWorldServerName(char *name, int8 server_list_type)
+636
View File
@@ -0,0 +1,636 @@
### Status
Below is a status list for the 450 opcodes we currently use on the server for the TOB client. Currently uses 3 status levels (let me know if we should do more):
- 🔴 Not-Set (Opcode not set in the patch file)
- 🟡 Unverified (Opcode set but structure hasn't been verified as completely working)
- 🟢 Verified (Opcode set and structure is working)
### World/Zone Opcode Implementation Status
| Opcode | Status | Notes | Working On |
|:----------------------------------|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------|
| `OP_AAAction` | 🟡 Unverified | | |
| `OP_AAExpUpdate` | 🟢 Verified | | |
| `OP_AcceptNewTask` | 🔴 Not-Set | | |
| `OP_AckPacket` | 🟢 Verified | | |
| `OP_Action` | 🟡 Unverified | | |
| `OP_Action2` | 🔴 Not-Set | | |
| `OP_AddNimbusEffect` | 🟡 Unverified | | |
| `OP_AdventureData` | 🔴 Not-Set | | |
| `OP_AdventureDetails` | 🔴 Not-Set | | |
| `OP_AdventureFinish` | 🔴 Not-Set | | |
| `OP_AdventureInfo` | 🔴 Not-Set | | |
| `OP_AdventureInfoRequest` | 🔴 Not-Set | | |
| `OP_AdventureLeaderboardReply` | 🔴 Not-Set | | |
| `OP_AdventureLeaderboardRequest` | 🔴 Not-Set | | |
| `OP_AdventureMerchantPurchase` | 🔴 Not-Set | | |
| `OP_AdventureMerchantRequest` | 🔴 Not-Set | | |
| `OP_AdventureMerchantResponse` | 🔴 Not-Set | | |
| `OP_AdventureMerchantSell` | 🔴 Not-Set | | |
| `OP_AdventurePointsUpdate` | 🔴 Not-Set | | |
| `OP_AdventureRequest` | 🔴 Not-Set | | |
| `OP_AdventureStatsReply` | 🔴 Not-Set | | |
| `OP_AdventureStatsRequest` | 🔴 Not-Set | | |
| `OP_AdventureUpdate` | 🔴 Not-Set | | |
| `OP_AggroMeterLockTarget` | 🔴 Not-Set | | |
| `OP_AggroMeterTargetInfo` | 🔴 Not-Set | | |
| `OP_AggroMeterUpdate` | 🔴 Not-Set | | |
| `OP_AltCurrency` | 🔴 Not-Set | | |
| `OP_AltCurrencyMerchantReply` | 🔴 Not-Set | | |
| `OP_AltCurrencyMerchantRequest` | 🔴 Not-Set | | |
| `OP_AltCurrencyPurchase` | 🔴 Not-Set | | |
| `OP_AltCurrencyReclaim` | 🔴 Not-Set | | |
| `OP_AltCurrencySell` | 🔴 Not-Set | | |
| `OP_AltCurrencySellSelection` | 🔴 Not-Set | | |
| `OP_Animation` | 🟡 Unverified | | |
| `OP_AnnoyingZoneUnknown` | 🔴 Not-Set | | |
| `OP_ApplyPoison` | 🟡 Unverified | | |
| `OP_ApproveName` | 🟡 Unverified | This takes multiple parameters from the client, and it can take multiple integer values from the server | |
| `OP_ApproveWorld` | 🔴 Not-Set | | |
| `OP_ApproveZone` | 🔴 Not-Set | | |
| `OP_Assist` | 🟡 Unverified | | |
| `OP_AssistGroup` | 🟡 Unverified | | |
| `OP_AugmentInfo` | 🟡 Unverified | | |
| `OP_AugmentItem` | 🟡 Unverified | | |
| `OP_AutoAttack` | 🟡 Unverified | | |
| `OP_AutoAttack2` | 🟡 Unverified | | |
| `OP_AutoFire` | 🟡 Unverified | | |
| `OP_Bandolier` | 🔴 Not-Set | | |
| `OP_BankerChange` | 🟡 Unverified | | |
| `OP_Barter` | 🔴 Not-Set | | |
| `OP_Bazaar` | 🔴 Not-Set | | |
| `OP_BazaarInspect` | 🔴 Not-Set | | |
| `OP_BazaarSearch` | 🔴 Not-Set | | |
| `OP_BecomeCorpse` | 🔴 Not-Set | | |
| `OP_BecomeTrader` | 🔴 Not-Set | | |
| `OP_Begging` | 🟡 Unverified | | |
| `OP_BeginCast` | 🟢 Verified | | |
| `OP_Bind_Wound` | 🟡 Unverified | | |
| `OP_BlockedBuffs` | 🟢 Verified | | |
| `OP_BoardBoat` | 🟡 Unverified | | |
| `OP_BookButton` | 🟡 Unverified | | |
| `OP_Buff` | 🟡 Unverified | | |
| `OP_BuffCreate` | 🟡 Unverified | | |
| `OP_BuffRemoveRequest` | 🟡 Unverified | | |
| `OP_Bug` | 🟡 Unverified | | |
| `OP_BuyerItems` | 🔴 Not-Set | | |
| `OP_CameraEffect` | 🟡 Unverified | | |
| `OP_Camp` | 🟡 Unverified | | |
| `OP_CancelSneakHide` | 🟡 Unverified | | |
| `OP_CancelTask` | 🔴 Not-Set | | |
| `OP_CancelTrade` | 🟡 Unverified | | |
| `OP_CashReward` | 🟡 Unverified | | |
| `OP_CastSpell` | 🟢 Verified | | |
| `OP_ChangeSize` | 🟢 Verified | | |
| `OP_ChannelMessage` | 🟢 Verified | | |
| `OP_ChangePetName` | 🔴 Not-Set | | |
| `OP_CharacterCreate` | 🟢 Verified | Sends heroic type, can be used for something? | |
| `OP_CharacterCreateRequest` | 🟢 Verified | | |
| `OP_CharInventory` | 🟢 Verified | | |
| `OP_Charm` | 🟡 Unverified | | |
| `OP_ChatMessage` | 🔴 Not-Set | | |
| `OP_ClearAA` | 🟢 Verified | | |
| `OP_ClearBlockedBuffs` | 🟢 Verified | | |
| `OP_ClearLeadershipAbilities` | 🔴 Not-Set | | |
| `OP_ClearNPCMarks` | 🔴 Not-Set | | |
| `OP_ClearObject` | 🟡 Unverified | | |
| `OP_ClearSurname` | 🔴 Not-Set | | |
| `OP_ClickDoor` | 🟡 Unverified | | |
| `OP_ClickObject` | 🟡 Unverified | | |
| `OP_ClickObjectAction` | 🟡 Unverified | | |
| `OP_ClientError` | 🔴 Not-Set | | |
| `OP_ClientReady` | 🟢 Verified | | |
| `OP_ClientTimeStamp` | 🔴 Not-Set | | |
| `OP_ClientUpdate` | 🟢 Verified | | |
| `OP_CloseContainer` | 🔴 Not-Set | | |
| `OP_CloseTributeMaster` | 🔴 Not-Set | | |
| `OP_ColoredText` | 🟢 Verified | | |
| `OP_CombatAbility` | 🟡 Unverified | | |
| `OP_Command` | 🔴 Not-Set | | |
| `OP_CompletedTasks` | 🔴 Not-Set | | |
| `OP_ConfirmDelete` | 🟡 Unverified | | |
| `OP_Consent` | 🟡 Unverified | | |
| `OP_ConsentDeny` | 🟡 Unverified | | |
| `OP_ConsentResponse` | 🟢 Verified | | |
| `OP_Consider` | 🟢 Verified | | |
| `OP_ConsiderCorpse` | 🟡 Unverified | | |
| `OP_Consume` | 🟡 Unverified | | |
| `OP_ControlBoat` | 🟡 Unverified | | |
| `OP_CorpseDrag` | 🟡 Unverified | | |
| `OP_CorpseDrop` | 🟡 Unverified | | |
| `OP_CrashDump` | 🔴 Not-Set | | |
| `OP_CrystalCountUpdate` | 🔴 Not-Set | | |
| `OP_CrystalCreate` | 🔴 Not-Set | | |
| `OP_CrystalReclaim` | 🔴 Not-Set | | |
| `OP_CustomTitles` | 🔴 Not-Set | | |
| `OP_Damage` | 🟡 Unverified | | |
| `OP_Death` | 🟡 Unverified | | |
| `OP_DelegateAbility` | 🔴 Not-Set | | |
| `OP_DeleteCharacter` | 🟢 Verified | | |
| `OP_DeleteCharge` | 🟡 Unverified | | |
| `OP_DeleteItem` | 🟡 Unverified | | |
| `OP_DeletePetition` | 🔴 Not-Set | | |
| `OP_DeleteSpawn` | 🟡 Unverified | | |
| `OP_DeleteSpell` | 🟡 Unverified | | |
| `OP_DenyResponse` | 🟡 Unverified | | |
| `OP_Disarm` | 🟡 Unverified | | |
| `OP_DisarmTraps` | 🟡 Unverified | | |
| `OP_DisciplineTimer` | 🟡 Unverified | | |
| `OP_DisciplineUpdate` | 🟡 Unverified | | |
| `OP_DiscordMerchantInventory` | 🔴 Not-Set | | |
| `OP_DoGroupLeadershipAbility` | 🔴 Not-Set | | |
| `OP_DuelDecline` | 🔴 Not-Set | | |
| `OP_DuelAccept` | 🔴 Not-Set | | |
| `OP_DumpName` | 🔴 Not-Set | | |
| `OP_Dye` | 🔴 Not-Set | | |
| `OP_DynamicWall` | 🔴 Not-Set | | |
| `OP_DzAddPlayer` | 🔴 Not-Set | | |
| `OP_DzChooseZone` | 🔴 Not-Set | | |
| `OP_DzChooseZoneReply` | 🔴 Not-Set | | |
| `OP_DzCompass` | 🔴 Not-Set | | |
| `OP_DzExpeditionEndsWarning` | 🔴 Not-Set | | |
| `OP_DzExpeditionInfo` | 🔴 Not-Set | | |
| `OP_DzExpeditionInvite` | 🔴 Not-Set | | |
| `OP_DzExpeditionInviteResponse` | 🔴 Not-Set | | |
| `OP_DzExpeditionLockoutTimers` | 🔴 Not-Set | | |
| `OP_DzListTimers` | 🔴 Not-Set | | |
| `OP_DzMakeLeader` | 🔴 Not-Set | | |
| `OP_DzMemberList` | 🔴 Not-Set | | |
| `OP_DzMemberListName` | 🔴 Not-Set | | |
| `OP_DzMemberListStatus` | 🔴 Not-Set | | |
| `OP_DzPlayerList` | 🔴 Not-Set | | |
| `OP_DzQuit` | 🔴 Not-Set | | |
| `OP_DzRemovePlayer` | 🔴 Not-Set | | |
| `OP_DzSetLeaderName` | 🔴 Not-Set | | |
| `OP_DzSwapPlayer` | 🔴 Not-Set | | |
| `OP_Emote` | 🔴 Not-Set | | |
| `OP_EndLootRequest` | 🟡 Unverified | | |
| `OP_EnduranceUpdate` | 🔴 Not-Set | | |
| `OP_EnterChat` | 🔴 Not-Set | | |
| `OP_EnterWorld` | 🟢 Verified | | |
| `OP_EnvDamage` | 🟡 Unverified | | |
| `OP_EvolveItem` | 🔴 Not-Set | | |
| `OP_ExpansionInfo` | 🟢 Verified | Updated from u32 to u64 and works now | |
| `OP_ExpUpdate` | 🟢 Verified | | |
| `OP_FaceChange` | 🔴 Not-Set | | |
| `OP_Feedback` | 🔴 Not-Set | | |
| `OP_FeignDeath` | 🟡 Unverified | | |
| `OP_FellowshipUpdate` | 🔴 Not-Set | | |
| `OP_FindPersonReply` | 🔴 Not-Set | | |
| `OP_FindPersonRequest` | 🔴 Not-Set | | |
| `OP_FinishTrade` | 🟡 Unverified | | |
| `OP_FinishWindow` | 🟡 Unverified | | |
| `OP_FinishWindow2` | 🟡 Unverified | | |
| `OP_Fishing` | 🟡 Unverified | | |
| `OP_Fling` | 🟡 Unverified | | |
| `OP_FloatListThing` | 🟢 Verified | Movement History. Sent from client, but emu doesn't use it so setting it as verified. Reference is 0x1402FFAD0 | |
| `OP_Forage` | 🟡 Unverified | | |
| `OP_ForceFindPerson` | 🔴 Not-Set | | |
| `OP_FormattedMessage` | 🟢 Verified | Some major work to do here -- the client now expects a spell link in the packet, will need to refactor the client work to decouple the stream from the internal representation | |
| `OP_FriendsWho` | 🟡 Unverified | | |
| `OP_GetGuildMOTD` | 🔴 Not-Set | | |
| `OP_GetGuildMOTDReply` | 🔴 Not-Set | | |
| `OP_GetGuildsList` | 🔴 Not-Set | | |
| `OP_GiveMoney` | 🔴 Not-Set | | |
| `OP_GMApproval` | 🔴 Not-Set | | |
| `OP_GMBecomeNPC` | 🔴 Not-Set | | |
| `OP_GMDelCorpse` | 🔴 Not-Set | | |
| `OP_GMEmoteZone` | 🔴 Not-Set | | |
| `OP_GMEndTraining` | 🟡 Unverified | | |
| `OP_GMEndTrainingResponse` | 🔴 Not-Set | | |
| `OP_GMFind` | 🔴 Not-Set | | |
| `OP_GMGoto` | 🔴 Not-Set | | |
| `OP_GMHideMe` | 🔴 Not-Set | | |
| `OP_GMKick` | 🔴 Not-Set | | |
| `OP_GMKill` | 🔴 Not-Set | | |
| `OP_GMLastName` | 🔴 Not-Set | | |
| `OP_GMNameChange` | 🔴 Not-Set | | |
| `OP_GMSearchCorpse` | 🔴 Not-Set | | |
| `OP_GMServers` | 🔴 Not-Set | | |
| `OP_GMSummon` | 🔴 Not-Set | | |
| `OP_GMToggle` | 🔴 Not-Set | | |
| `OP_GMTraining` | 🟡 Unverified | | |
| `OP_GMTrainSkill` | 🟡 Unverified | | |
| `OP_GMTrainSkillConfirm` | 🟡 Unverified | | |
| `OP_GMZoneRequest` | 🔴 Not-Set | | |
| `OP_GMZoneRequest2` | 🔴 Not-Set | | |
| `OP_GroundSpawn` | 🟢 Verified | | |
| `OP_GroupAcknowledge` | 🔴 Not-Set | | |
| `OP_GroupCancelInvite` | 🔴 Not-Set | | |
| `OP_GroupDelete` | 🔴 Not-Set | | |
| `OP_GroupDisband` | 🟡 Unverified | | |
| `OP_GroupDisbandOther` | 🔴 Not-Set | | |
| `OP_GroupDisbandYou` | 🔴 Not-Set | | |
| `OP_GroupFollow` | 🔴 Not-Set | | |
| `OP_GroupFollow2` | 🔴 Not-Set | | |
| `OP_GroupInvite` | 🟡 Unverified | | |
| `OP_GroupInvite2` | 🔴 Not-Set | | |
| `OP_GroupLeaderChange` | 🔴 Not-Set | | |
| `OP_GroupLeadershipAAUpdate` | 🔴 Not-Set | | |
| `OP_GroupMakeLeader` | 🔴 Not-Set | | |
| `OP_GroupMentor` | 🔴 Not-Set | | |
| `OP_GroupRoles` | 🔴 Not-Set | | |
| `OP_GroupUpdate` | 🔴 Not-Set | | |
| `OP_GroupUpdateB` | 🔴 Not-Set | | |
| `OP_GroupUpdateLeaderAA` | 🔴 Not-Set | | |
| `OP_GuildBank` | 🔴 Not-Set | | |
| `OP_GuildBankItemList` | 🔴 Not-Set | | |
| `OP_GuildCreate` | 🔴 Not-Set | | |
| `OP_GuildDelete` | 🔴 Not-Set | | |
| `OP_GuildDeleteGuild` | 🔴 Not-Set | | |
| `OP_GuildDemote` | 🔴 Not-Set | | |
| `OP_GuildInvite` | 🔴 Not-Set | | |
| `OP_GuildInviteAccept` | 🔴 Not-Set | | |
| `OP_GuildLeader` | 🔴 Not-Set | | |
| `OP_GuildManageAdd` | 🔴 Not-Set | | |
| `OP_GuildManageBanker` | 🔴 Not-Set | | |
| `OP_GuildManageRemove` | 🔴 Not-Set | | |
| `OP_GuildManageStatus` | 🔴 Not-Set | | |
| `OP_GuildMemberLevelUpdate` | 🔴 Not-Set | | |
| `OP_GuildMemberList` | 🔴 Not-Set | | |
| `OP_GuildMemberUpdate` | 🔴 Not-Set | | |
| `OP_GuildMemberLevel` | 🔴 Not-Set | | |
| `OP_GuildMemberRankAltBanker` | 🔴 Not-Set | | |
| `OP_GuildMemberPublicNote` | 🔴 Not-Set | | |
| `OP_GuildMemberAdd` | 🔴 Not-Set | | |
| `OP_GuildMemberRename` | 🔴 Not-Set | | |
| `OP_GuildMemberDelete` | 🔴 Not-Set | | |
| `OP_GuildMemberDetails` | 🔴 Not-Set | | |
| `OP_GuildRenameGuild` | 🔴 Not-Set | | |
| `OP_GuildMOTD` | 🔴 Not-Set | | |
| `OP_GuildPeace` | 🔴 Not-Set | | |
| `OP_GuildPromote` | 🔴 Not-Set | | |
| `OP_GuildPublicNote` | 🔴 Not-Set | | |
| `OP_GuildRemove` | 🔴 Not-Set | | |
| `OP_GuildSelectTribute` | 🔴 Not-Set | | |
| `OP_GuildModifyBenefits` | 🔴 Not-Set | | |
| `OP_GuildTributeToggleReq` | 🔴 Not-Set | | |
| `OP_GuildTributeToggleReply` | 🔴 Not-Set | | |
| `OP_GuildOptInOut` | 🔴 Not-Set | | |
| `OP_GuildSaveActiveTributes` | 🔴 Not-Set | | |
| `OP_GuildSendActiveTributes` | 🔴 Not-Set | | |
| `OP_GuildTributeFavorAndTimer` | 🔴 Not-Set | | |
| `OP_GuildsList` | 🔴 Not-Set | | |
| `OP_GuildStatus` | 🔴 Not-Set | | |
| `OP_GuildTributeInfo` | 🔴 Not-Set | | |
| `OP_GuildUpdate` | 🔴 Not-Set | | |
| `OP_GuildTributeDonateItem` | 🔴 Not-Set | | |
| `OP_GuildTributeDonatePlat` | 🔴 Not-Set | | |
| `OP_GuildWar` | 🔴 Not-Set | | |
| `OP_Heartbeat` | 🔴 Not-Set | | |
| `OP_Hide` | 🟡 Unverified | | |
| `OP_HideCorpse` | 🟡 Unverified | | |
| `OP_HPUpdate` | 🟢 Verified | | |
| `OP_Illusion` | 🟡 Unverified | | |
| `OP_IncreaseStats` | 🟡 Unverified | | |
| `OP_InitialHPUpdate` | 🔴 Not-Set | | |
| `OP_InitialMobHealth` | 🔴 Not-Set | | |
| `OP_InspectAnswer` | 🔴 Not-Set | | |
| `OP_InspectBuffs` | 🔴 Not-Set | | |
| `OP_InspectMessageUpdate` | 🔴 Not-Set | | |
| `OP_InspectRequest` | 🔴 Not-Set | | |
| `OP_InstillDoubt` | 🟡 Unverified | | |
| `OP_InterruptCast` | 🟢 Verified | Some major work to do here -- the client now expects a spell link in the packet, will need to refactor the client work to decouple the stream from the internal representation | |
| `OP_InvokeChangePetName` | 🔴 Not-Set | | |
| `OP_InvokeChangePetNameImmediate` | 🔴 Not-Set | | |
| `OP_InvokeNameChangeImmediate` | 🔴 Not-Set | | |
| `OP_InvokeNameChangeLazy` | 🔴 Not-Set | | |
| `OP_ItemLinkClick` | 🔴 Not-Set | | |
| `OP_ItemLinkResponse` | 🔴 Not-Set | | |
| `OP_ItemLinkText` | 🔴 Not-Set | | |
| `OP_ItemName` | 🔴 Not-Set | | |
| `OP_ItemPacket` | 🟡 Unverified | | |
| `OP_ItemPreview` | 🔴 Not-Set | | |
| `OP_ItemPreviewRequest` | 🔴 Not-Set | | |
| `OP_ItemRecastDelay` | 🟡 Unverified | | |
| `OP_ItemVerifyReply` | 🟡 Unverified | | |
| `OP_ItemVerifyRequest` | 🟡 Unverified | | |
| `OP_ItemViewUnknown` | 🔴 Not-Set | | |
| `OP_Jump` | 🟡 Unverified | | |
| `OP_KeyRing` | 🔴 Not-Set | | |
| `OP_KickPlayers` | 🟡 Unverified | | |
| `OP_KnowledgeBase` | 🔴 Not-Set | | |
| `OP_LDoNButton` | 🔴 Not-Set | | |
| `OP_LDoNDisarmTraps` | 🔴 Not-Set | | |
| `OP_LDoNInspect` | 🔴 Not-Set | | |
| `OP_LDoNOpen` | 🟡 Unverified | | |
| `OP_LDoNPickLock` | 🟡 Unverified | | |
| `OP_LDoNSenseTraps` | 🟡 Unverified | | |
| `OP_LeadershipExpToggle` | 🔴 Not-Set | | |
| `OP_LeadershipExpUpdate` | 🔴 Not-Set | | |
| `OP_LeaveAdventure` | 🔴 Not-Set | | |
| `OP_LeaveBoat` | 🟡 Unverified | | |
| `OP_LevelAppearance` | 🟡 Unverified | | |
| `OP_LevelUpdate` | 🟢 Verified | | |
| `OP_LFGAppearance` | 🔴 Not-Set | | |
| `OP_LFGCommand` | 🔴 Not-Set | | |
| `OP_LFGGetMatchesRequest` | 🔴 Not-Set | | |
| `OP_LFGGetMatchesResponse` | 🔴 Not-Set | | |
| `OP_LFGResponse` | 🔴 Not-Set | | |
| `OP_LFGuild` | 🔴 Not-Set | | |
| `OP_LFPCommand` | 🔴 Not-Set | | |
| `OP_LFPGetMatchesRequest` | 🔴 Not-Set | | |
| `OP_LFPGetMatchesResponse` | 🔴 Not-Set | | |
| `OP_LinkedReuse` | 🟢 Verified | | |
| `OP_LoadSpellSet` | 🔴 Not-Set | | |
| `OP_LocInfo` | 🔴 Not-Set | | |
| `OP_LockoutTimerInfo` | 🔴 Not-Set | | |
| `OP_Login` | 🔴 Not-Set | | |
| `OP_LoginAccepted` | 🔴 Not-Set | | |
| `OP_LoginComplete` | 🔴 Not-Set | | |
| `OP_LoginExpansionPacketData` | 🔴 Not-Set | | |
| `OP_LoginUnknown1` | 🔴 Not-Set | | |
| `OP_LoginUnknown2` | 🔴 Not-Set | | |
| `OP_Logout` | 🟡 Unverified | | |
| `OP_LogoutReply` | 🔴 Not-Set | | |
| `OP_LogServer` | 🟢 Verified | Mostly unused values | |
| `OP_LootComplete` | 🟡 Unverified | | |
| `OP_LootItem` | 🟡 Unverified | | |
| `OP_LootRequest` | 🟡 Unverified | | |
| `OP_ManaChange` | 🟢 Verified | | |
| `OP_ManaUpdate` | 🔴 Not-Set | | |
| `OP_MarkNPC` | 🔴 Not-Set | | |
| `OP_MarkRaidNPC` | 🔴 Not-Set | | |
| `OP_Marquee` | 🟡 Unverified | | |
| `OP_MemorizeSpell` | 🟢 Verified | | |
| `OP_Mend` | 🟡 Unverified | | |
| `OP_MendHPUpdate` | 🔴 Not-Set | | |
| `OP_MercenaryAssign` | 🔴 Not-Set | | |
| `OP_MercenaryCommand` | 🔴 Not-Set | | |
| `OP_MercenaryDataRequest` | 🔴 Not-Set | | |
| `OP_MercenaryDataResponse` | 🔴 Not-Set | | |
| `OP_MercenaryDataUpdate` | 🔴 Not-Set | | |
| `OP_MercenaryDataUpdateRequest` | 🔴 Not-Set | | |
| `OP_MercenaryDismiss` | 🔴 Not-Set | | |
| `OP_MercenaryHire` | 🔴 Not-Set | | |
| `OP_MercenarySuspendRequest` | 🔴 Not-Set | | |
| `OP_MercenarySuspendResponse` | 🔴 Not-Set | | |
| `OP_MercenaryTimer` | 🔴 Not-Set | | |
| `OP_MercenaryTimerRequest` | 🔴 Not-Set | | |
| `OP_MercenaryUnknown1` | 🔴 Not-Set | | |
| `OP_MercenaryUnsuspendResponse` | 🔴 Not-Set | | |
| `OP_MerchantBulkItems` | 🔴 Not-Set | | |
| `OP_MobEnduranceUpdate` | 🔴 Not-Set | | |
| `OP_MobHealth` | 🟡 Unverified | | |
| `OP_MobManaUpdate` | 🔴 Not-Set | | |
| `OP_MobRename` | 🔴 Not-Set | | |
| `OP_MobUpdate` | 🔴 Not-Set | | |
| `OP_MoneyOnCorpse` | 🟡 Unverified | | |
| `OP_MoneyUpdate` | 🟡 Unverified | | |
| `OP_MOTD` | 🟢 Verified | | |
| `OP_MoveCoin` | 🟡 Unverified | | |
| `OP_MoveDoor` | 🟡 Unverified | | |
| `OP_MoveItem` | 🟢 Verified | | |
| `OP_MoveMultipleItems` | 🟡 Unverified | | |
| `OP_MoveLogDisregard` | 🔴 Not-Set | | |
| `OP_MoveLogRequest` | 🔴 Not-Set | | |
| `OP_MultiLineMsg` | 🔴 Not-Set | | |
| `OP_NewSpawn` | 🟢 Verified | Deprecated in the client, already handled in emu | |
| `OP_NewTitlesAvailable` | 🔴 Not-Set | | |
| `OP_NewZone` | 🟢 Verified | | |
| `OP_NPCMoveUpdate` | 🔴 Not-Set | | |
| `OP_OnLevelMessage` | 🟡 Unverified | | |
| `OP_OpenContainer` | 🟡 Unverified | | |
| `OP_OpenDiscordMerchant` | 🔴 Not-Set | | |
| `OP_OpenGuildTributeMaster` | 🔴 Not-Set | | |
| `OP_OpenInventory` | 🔴 Not-Set | | |
| `OP_OpenTributeMaster` | 🔴 Not-Set | | |
| `OP_PDeletePetition` | 🔴 Not-Set | | |
| `OP_PetBuffWindow` | 🔴 Not-Set | | |
| `OP_PetCommands` | 🔴 Not-Set | | |
| `OP_PetCommandState` | 🔴 Not-Set | | |
| `OP_PetHoTT` | 🔴 Not-Set | | |
| `OP_Petition` | 🔴 Not-Set | | |
| `OP_PetitionBug` | 🔴 Not-Set | | |
| `OP_PetitionCheckIn` | 🔴 Not-Set | | |
| `OP_PetitionCheckout` | 🔴 Not-Set | | |
| `OP_PetitionCheckout2` | 🔴 Not-Set | | |
| `OP_PetitionDelete` | 🔴 Not-Set | | |
| `OP_PetitionQue` | 🔴 Not-Set | | |
| `OP_PetitionRefresh` | 🔴 Not-Set | | |
| `OP_PetitionResolve` | 🔴 Not-Set | | |
| `OP_PetitionSearch` | 🔴 Not-Set | | |
| `OP_PetitionSearchResults` | 🔴 Not-Set | | |
| `OP_PetitionSearchText` | 🔴 Not-Set | | |
| `OP_PetitionUnCheckout` | 🔴 Not-Set | | |
| `OP_PetitionUpdate` | 🔴 Not-Set | | |
| `OP_PickPocket` | 🟡 Unverified | | |
| `OP_PickZone` | 🔴 Not-Set | | |
| `OP_PickZoneWindow` | 🔴 Not-Set | | |
| `OP_PlayerProfile` | 🟢 Verified | | |
| `OP_PlayerStateAdd` | 🟡 Unverified | | |
| `OP_PlayerStateRemove` | 🟡 Unverified | | |
| `OP_PlayEverquestRequest` | 🔴 Not-Set | | |
| `OP_PlayEverquestResponse` | 🔴 Not-Set | | |
| `OP_PlayMP3` | 🟡 Unverified | | |
| `OP_Poll` | 🔴 Not-Set | | |
| `OP_PollResponse` | 🔴 Not-Set | | |
| `OP_PopupResponse` | 🟡 Unverified | | |
| `OP_PostEnterWorld` | 🟢 Verified | | |
| `OP_PotionBelt` | 🔴 Not-Set | | |
| `OP_PreLogoutReply` | 🔴 Not-Set | | |
| `OP_PurchaseLeadershipAA` | 🔴 Not-Set | | |
| `OP_PVPLeaderBoardDetailsReply` | 🔴 Not-Set | | |
| `OP_PVPLeaderBoardDetailsRequest` | 🔴 Not-Set | | |
| `OP_PVPLeaderBoardReply` | 🔴 Not-Set | | |
| `OP_PVPLeaderBoardRequest` | 🔴 Not-Set | | |
| `OP_PVPStats` | 🔴 Not-Set | | |
| `OP_QueryResponseThing` | 🔴 Not-Set | | |
| `OP_QueryUCSServerStatus` | 🟢 Verified | | |
| `OP_RaidDelegateAbility` | 🔴 Not-Set | | |
| `OP_RaidClearNPCMarks` | 🔴 Not-Set | | |
| `OP_RaidInvite` | 🔴 Not-Set | | |
| `OP_RaidJoin` | 🔴 Not-Set | | |
| `OP_RaidUpdate` | 🔴 Not-Set | | |
| `OP_RandomNameGenerator` | 🟢 Verified | The client no longer sends this packet (random name generation is done entirely in the client). The client will still accept this packet to set name (emu doesn't do this, but it's always been supported) | |
| `OP_RandomReply` | 🟡 Unverified | | |
| `OP_RandomReq` | 🟡 Unverified | | |
| `OP_ReadBook` | 🟡 Unverified | | |
| `OP_RecipeAutoCombine` | 🟡 Unverified | | |
| `OP_RecipeDetails` | 🟡 Unverified | | |
| `OP_RecipeReply` | 🟡 Unverified | | |
| `OP_RecipesFavorite` | 🟡 Unverified | | |
| `OP_RecipesSearch` | 🟡 Unverified | | |
| `OP_ReclaimCrystals` | 🔴 Not-Set | | |
| `OP_ReloadUI` | 🔴 Not-Set | | |
| `OP_RemoveAllDoors` | 🟡 Unverified | | |
| `OP_RemoveBlockedBuffs` | 🟢 Verified | | |
| `OP_RemoveNimbusEffect` | 🟡 Unverified | | |
| `OP_RemoveTrap` | 🔴 Not-Set | | |
| `OP_Report` | 🟡 Unverified | | |
| `OP_ReqClientSpawn` | 🟢 Verified | | |
| `OP_ReqNewZone` | 🟢 Verified | Client does not send this (in LS or TOB), but it does receive it. emu does not send it | |
| `OP_RequestClientZoneChange` | 🟢 Verified | parity with RoF2, there's a string that gets passed to teleport at the end that's not known | |
| `OP_RequestDuel` | 🔴 Not-Set | | |
| `OP_RequestGuildTributes` | 🔴 Not-Set | | |
| `OP_RequestKnowledgeBase` | 🔴 Not-Set | | |
| `OP_RequestTitles` | 🔴 Not-Set | | |
| `OP_RespawnWindow` | 🟡 Unverified | | |
| `OP_RespondAA` | 🟢 Verified | | |
| `OP_RestState` | 🟡 Unverified | | |
| `OP_Rewind` | 🟡 Unverified | | |
| `OP_RezzAnswer` | 🔴 Not-Set | | |
| `OP_RezzComplete` | 🔴 Not-Set | | |
| `OP_RezzRequest` | 🔴 Not-Set | | |
| `OP_Sacrifice` | 🟡 Unverified | | |
| `OP_SafeFallSuccess` | 🟡 Unverified | | |
| `OP_SafePoint` | 🔴 Not-Set | | |
| `OP_Save` | 🟡 Unverified | | |
| `OP_SaveOnZoneReq` | 🟡 Unverified | | |
| `OP_SelectTribute` | 🔴 Not-Set | | |
| `OP_SendAAStats` | 🟡 Unverified | | |
| `OP_SendAATable` | 🟢 Verified | | |
| `OP_SendCharInfo` | 🟢 Verified | | |
| `OP_SendExpZonein` | 🟢 Verified | | |
| `OP_SendFindableNPCs` | 🔴 Not-Set | | |
| `OP_SendGuildTributes` | 🔴 Not-Set | | |
| `OP_SendLoginInfo` | 🟢 Verified | | |
| `OP_SendMaxCharacters` | 🟢 Verified | | |
| `OP_SendMembership` | 🟢 Verified | | |
| `OP_SendMembershipDetails` | 🟢 Verified | The struct is correct, will need reversing for actual option keys/values | |
| `OP_SendSystemStats` | 🔴 Not-Set | | |
| `OP_SendTitleList` | 🔴 Not-Set | | |
| `OP_SendTributes` | 🔴 Not-Set | | |
| `OP_SendZonepoints` | 🟢 Verified | | |
| `OP_SenseHeading` | 🟡 Unverified | | |
| `OP_SenseTraps` | 🟡 Unverified | | |
| `OP_ServerListRequest` | 🔴 Not-Set | | |
| `OP_ServerListResponse` | 🔴 Not-Set | | |
| `OP_SessionReady` | 🔴 Not-Set | | |
| `OP_SetChatServer` | 🔴 Not-Set | | |
| `OP_SetChatServer2` | 🟢 Verified | | |
| `OP_SetFace` | 🔴 Not-Set | | |
| `OP_SetGroupTarget` | 🔴 Not-Set | | |
| `OP_SetGuildMOTD` | 🔴 Not-Set | | |
| `OP_SetGuildRank` | 🔴 Not-Set | | |
| `OP_SetRunMode` | 🟡 Unverified | | |
| `OP_SetServerFilter` | 🟢 Verified | | |
| `OP_SetStartCity` | 🔴 Not-Set | | |
| `OP_SetTitle` | 🔴 Not-Set | | |
| `OP_SetTitleReply` | 🔴 Not-Set | | |
| `OP_SharedTaskMemberList` | 🔴 Not-Set | | |
| `OP_SharedTaskAddPlayer` | 🔴 Not-Set | | |
| `OP_SharedTaskRemovePlayer` | 🔴 Not-Set | | |
| `OP_SharedTaskMakeLeader` | 🔴 Not-Set | | |
| `OP_SharedTaskMemberInvite` | 🔴 Not-Set | | |
| `OP_SharedTaskInvite` | 🔴 Not-Set | | |
| `OP_SharedTaskInviteResponse` | 🔴 Not-Set | | |
| `OP_SharedTaskAcceptNew` | 🔴 Not-Set | | |
| `OP_SharedTaskMemberChange` | 🔴 Not-Set | | |
| `OP_SharedTaskPlayerList` | 🔴 Not-Set | | |
| `OP_SharedTaskSelectWindow` | 🔴 Not-Set | | |
| `OP_SharedTaskQuit` | 🔴 Not-Set | | |
| `OP_TaskTimers` | 🔴 Not-Set | | |
| `OP_Shielding` | 🔴 Not-Set | | |
| `OP_ShopDelItem` | 🟡 Unverified | | |
| `OP_ShopEnd` | 🟡 Unverified | | |
| `OP_ShopEndConfirm` | 🟡 Unverified | | |
| `OP_ShopItem` | 🔴 Not-Set | | |
| `OP_ShopPlayerBuy` | 🟡 Unverified | | |
| `OP_ShopPlayerSell` | 🟡 Unverified | | |
| `OP_ShopSendParcel` | 🟡 Unverified | | |
| `OP_ShopDeleteParcel` | 🟡 Unverified | | |
| `OP_ShopRespondParcel` | 🔴 Not-Set | | |
| `OP_ShopRetrieveParcel` | 🟡 Unverified | | |
| `OP_ShopParcelIcon` | 🟡 Unverified | | |
| `OP_ShopRequest` | 🟡 Unverified | | |
| `OP_SimpleMessage` | 🟢 Verified | | |
| `OP_SkillUpdate` | 🟡 Unverified | | |
| `OP_Sneak` | 🟡 Unverified | | |
| `OP_Some3ByteHPUpdate` | 🔴 Not-Set | | |
| `OP_Some6ByteHPUpdate` | 🔴 Not-Set | | |
| `OP_SomeItemPacketMaybe` | 🔴 Not-Set | | |
| `OP_Sound` | 🟡 Unverified | | |
| `OP_SpawnAppearance` | 🟢 Verified | | |
| `OP_SpawnDoor` | 🟢 Verified | | |
| `OP_SpawnPositionUpdate` | 🔴 Not-Set | | |
| `OP_SpecialMesg` | 🟢 Verified | | |
| `OP_SpellEffect` | 🟡 Unverified | | |
| `OP_Split` | 🟡 Unverified | | |
| `OP_Stamina` | 🟢 Verified | These values are 0-32k instead of 0-127 | |
| `OP_Stun` | 🟡 Unverified | | |
| `OP_Surname` | 🔴 Not-Set | | |
| `OP_SwapSpell` | 🟢 Verified | | |
| `OP_SystemFingerprint` | 🔴 Not-Set | | |
| `OP_TargetBuffs` | 🔴 Not-Set | | |
| `OP_TargetCommand` | 🟡 Unverified | | |
| `OP_TargetHoTT` | 🔴 Not-Set | | |
| `OP_TargetMouse` | 🟡 Unverified | | |
| `OP_TargetReject` | 🔴 Not-Set | | |
| `OP_TaskActivity` | 🔴 Not-Set | | |
| `OP_TaskActivityComplete` | 🔴 Not-Set | | |
| `OP_TaskDescription` | 🔴 Not-Set | | |
| `OP_TaskHistoryReply` | 🔴 Not-Set | | |
| `OP_TaskHistoryRequest` | 🔴 Not-Set | | |
| `OP_TaskRequestTimer` | 🔴 Not-Set | | |
| `OP_TaskSelectWindow` | 🔴 Not-Set | | |
| `OP_Taunt` | 🟡 Unverified | | |
| `OP_TestBuff` | 🔴 Not-Set | | |
| `OP_TGB` | 🔴 Not-Set | | |
| `OP_TimeOfDay` | 🟢 Verified | | |
| `OP_Track` | 🟡 Unverified | | |
| `OP_TrackTarget` | 🟡 Unverified | | |
| `OP_TrackUnknown` | 🟡 Unverified | | |
| `OP_TradeAcceptClick` | 🟡 Unverified | | |
| `OP_TradeBusy` | 🟡 Unverified | | |
| `OP_TradeCoins` | 🟡 Unverified | | |
| `OP_TradeMoneyUpdate` | 🟡 Unverified | | |
| `OP_Trader` | 🔴 Not-Set | | |
| `OP_TraderBulkSend` | 🔴 Not-Set | | |
| `OP_TraderBuy` | 🔴 Not-Set | | |
| `OP_TraderDelItem` | 🔴 Not-Set | | |
| `OP_TradeRequest` | 🟡 Unverified | | |
| `OP_TradeRequestAck` | 🟡 Unverified | | |
| `OP_TraderItemUpdate` | 🔴 Not-Set | | |
| `OP_TraderShop` | 🔴 Not-Set | | |
| `OP_TradeSkillCombine` | 🟡 Unverified | | |
| `OP_TradeSkillRecipeInspect` | 🔴 Not-Set | | |
| `OP_Translocate` | 🟡 Unverified | | |
| `OP_TributeInfo` | 🔴 Not-Set | | |
| `OP_TributeItem` | 🔴 Not-Set | | |
| `OP_TributeMoney` | 🔴 Not-Set | | |
| `OP_TributeNPC` | 🔴 Not-Set | | |
| `OP_TributePointUpdate` | 🔴 Not-Set | | |
| `OP_TributeTimer` | 🔴 Not-Set | | |
| `OP_TributeToggle` | 🔴 Not-Set | | |
| `OP_TributeUpdate` | 🔴 Not-Set | | |
| `OP_Untargetable` | 🟡 Unverified | | |
| `OP_UpdateAA` | 🟢 Verified | | |
| `OP_UpdateAura` | 🔴 Not-Set | | |
| `OP_UpdateLeadershipAA` | 🔴 Not-Set | | |
| `OP_VetClaimReply` | 🔴 Not-Set | | |
| `OP_VetClaimRequest` | 🔴 Not-Set | | |
| `OP_VetRewardsAvaliable` | 🔴 Not-Set | | |
| `OP_VoiceMacroIn` | 🟡 Unverified | | |
| `OP_VoiceMacroOut` | 🟡 Unverified | | |
| `OP_WeaponEquip1` | 🔴 Not-Set | | |
| `OP_WearChange` | 🟢 Verified | | |
| `OP_Weather` | 🟢 Verified | | |
| `OP_Weblink` | 🟡 Unverified | | |
| `OP_WhoAllRequest` | 🟡 Unverified | | |
| `OP_WhoAllResponse` | 🟡 Unverified | | |
| `OP_World_Client_CRC1` | 🟢 Verified | | |
| `OP_World_Client_CRC2` | 🟢 Verified | | |
| `OP_World_Client_CRC3` | 🟢 Verified | | |
| `OP_WorldClientReady` | 🟢 Verified | | |
| `OP_WorldComplete` | 🟢 Verified | | |
| `OP_WorldLogout` | 🔴 Not-Set | | |
| `OP_WorldObjectsSent` | 🟢 Verified | | |
| `OP_WorldUnknown001` | 🟢 Verified | SetServerTime. emu doesn't currently send it so setting it to verified, but the reference is 0x140292550 | |
| `OP_XTargetAutoAddHaters` | 🔴 Not-Set | | |
| `OP_XTargetOpen` | 🔴 Not-Set | | |
| `OP_XTargetOpenResponse` | 🔴 Not-Set | | |
| `OP_XTargetRequest` | 🔴 Not-Set | | |
| `OP_XTargetResponse` | 🔴 Not-Set | | |
| `OP_YellForHelp` | 🟡 Unverified | | |
| `OP_ZoneChange` | 🟢 Verified | | |
| `OP_ZoneComplete` | 🔴 Not-Set | | |
| `OP_ZoneEntry` | 🟢 Verified | unknown fields in C->S struct are various CRCs, emu doesn't use them | |
| `OP_ZoneGuildList` | 🔴 Not-Set | | |
| `OP_ZoneInUnknown` | 🔴 Not-Set | | |
| `OP_ZonePlayerToBind` | 🟡 Unverified | | |
| `OP_ZoneServerInfo` | 🟢 Verified | | |
| `OP_ZoneServerReady` | 🔴 Not-Set | | |
| `OP_ZoneSpawns` | 🟢 Verified | This is deprecated in the client (and emu never sends it directly) | |
| `OP_ZoneUnavail` | 🟢 Verified | The client discards all content of this packet | |
| `OP_ResetAA` | 🟡 Unverified | | |
| `OP_UnderWorld` | 🟡 Unverified | | |
+1
View File
@@ -0,0 +1 @@
This is a bunch of ImHex patterns for viewing various Outer Brood packets
@@ -0,0 +1,23 @@
struct BaseResponse
{
u8 success;
u32 error_str_id;
char error_str[];
};
struct Packet {
BaseResponse base;
s8 unk1; //I think this is just padding
s8 unk2; //I think this is just padding
u32 lsid;
char key[];
s32 failed_attempts;
u8 show_player_count;
s32 unk3; // 0
s32 unk4; // 0
char username[];
char password[]; //I'm not sure this is correct, it feels like this might be some internal refresh token
char paddingEnd[2];
};
Packet p @ 0x00;
+19
View File
@@ -0,0 +1,19 @@
// 0x01
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Packet {
LoginBase base;
u8 success;
s32 error_str_id;
char error_msg[];
char other_msg[];
};
Packet packet @0x00;
+22
View File
@@ -0,0 +1,22 @@
// 0x31
struct Expansion
{
u32 index;
u8 owned;
s32 expansion_name_string_id;
s32 order_string_id;
s32 unknown_string_id;
u32 unknown17;
};
struct Packet {
u32 unknown00;
u32 unknown04;
u16 unknown08;
u32 expansion_count;
Expansion expansions[expansion_count];
};
Packet packet @0x00;
+18
View File
@@ -0,0 +1,18 @@
// 0x02
#include <std/mem.pat>
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Packet {
LoginBase base;
u8 payload[std::mem::size() - $];
};
Packet packet @0x00;
+18
View File
@@ -0,0 +1,18 @@
// 0x19
#include <std/mem.pat>
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Packet {
LoginBase base;
u8 payload[std::mem::size() - $];
};
Packet packet @0x00;
@@ -0,0 +1,20 @@
// 0xd
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Packet {
LoginBase base;
u32 server_id;
char fingerprint[];
u32 unknown1;
u8 unknown2;
u32 unknown3;
};
Packet packet @0x00;
@@ -0,0 +1,19 @@
// 0x23
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Packet {
LoginBase base;
u8 success;
u32 login_server_string_id;
char login_server_string;
};
Packet packet @0x00;
@@ -0,0 +1,15 @@
// 0x04
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Packet {
LoginBase base;
};
Packet packet @0x00;
@@ -0,0 +1,44 @@
// 0x1a
// work in progress
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Realm
{
char address[];
u32 port;
u32 server_category;
//not sure yet, seen 289 on a lot of classic servers
//41 fangbreaker, teek, oakwynd, tormax
//31 yelniak
//33 vaniki, mischief
u32 status_code;
u32 server_id;
char name[];
char language[];
char region[];
char server_type_desc[];
char server_desc[];
u32 server_flags;
u32 players_online;
u32 expansion; //I think
u32 truebox_max_clients;
};
struct Packet {
LoginBase base;
u8 success;
u32 login_server_string_id;
char login_server_string[];
u32 realm_count;
Realm realms[realm_count];
};
Packet packet @0x00;
+16
View File
@@ -0,0 +1,16 @@
// 0x01
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Packet {
LoginBase base;
u16 unknown0a;
};
Packet packet @0x00;
@@ -0,0 +1,10 @@
// 0x16
struct Packet {
u32 sequence_id;
u32 unknown04;
u16 unknown08;
char fingerprint_data[];
};
Packet packet @0x00;
+17
View File
@@ -0,0 +1,17 @@
// 0x03
// I'm not sure what this packet is, it sends right after play everquest response it sent client->server
struct LoginBase
{
u32 sequence_id;
u8 compressed;
u8 encrypt_type;
u32 unknown08;
};
struct Packet {
LoginBase base;
};
Packet packet @0x00;
+27
View File
@@ -0,0 +1,27 @@
import argparse
from Crypto.Cipher import DES
def decrypt_hex_string(hex_data):
raw_hex = "".join(hex_data).replace(" ", "")
try:
encrypted_bytes = bytes.fromhex(raw_hex)
except ValueError:
return "Error: Input is not valid hexadecimal."
key = b'\x00' * 8
iv = b'\x00' * 8
cipher = DES.new(key, DES.MODE_CBC, iv)
decrypted_bytes = cipher.decrypt(encrypted_bytes)
return decrypted_bytes
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Decrypt EQ Default Encryption.")
parser.add_argument("data", nargs="+", help="The data hex string to decrypt")
args = parser.parse_args()
result = decrypt_hex_string(args.data)
print("--- Decrypted Data ---")
print(f"Data: {result.hex(' ').upper()}")
+26
View File
@@ -0,0 +1,26 @@
struct CharacterCreateAllocation
{
u32 index;
u32 base_stats[7];
u32 default_allocations[7];
};
struct RaceClassCombo
{
u64 expansion_req;
u32 race;
u32 class;
u32 deity;
u32 allocation_index;
u32 zone;
};
struct Packet {
u8 padding1;
u32 allocation_count;
CharacterCreateAllocation allocations[allocation_count];
u32 race_class_combo_count;
RaceClassCombo race_class_combos[race_class_combo_count];
};
Packet p @ 0x00;
+26
View File
@@ -0,0 +1,26 @@
struct MembershipEntry
{
u32 purchase_id;
u32 bitwise_entry;
};
struct MembershipSetting
{
s8 setting_index;
s32 setting_id; // 0 to 23 actually seen but the OP_Membership packet has up to 32
s32 setting_value;
};
struct Membership
{
u32 membership_setting_count;
MembershipSetting membership_settings[membership_setting_count];
u32 race_entry_count;
MembershipEntry membership_races[race_entry_count];
u32 class_entry_count;
MembershipEntry membership_classes[class_entry_count];
u32 exit_url_length;
char exit_url[exit_url_length];
};
Membership m @ 0x00;
+434
View File
@@ -0,0 +1,434 @@
struct Bind {
u32 zoneid;
float x;
float y;
float z;
float heading;
};
struct ArmorProperty
{
s32 type;
s32 variation;
s32 material;
s32 newArmorId;
s32 newArmorType;
};
struct AA
{
s32 index;
s32 points_spent;
s32 charges_spent;
};
struct EQGuid
{
u32 entity_id;
u32 realm_id;
};
struct SlotData
{
s32 slot_id;
s64 value;
};
struct EQAffect
{
float modifier;
EQGuid caster_id;
u32 duration;
u32 max_duration;
u8 level;
s32 spell_id;
s32 hitcount;
u32 flags;
u32 viral_timer;
u8 type;
SlotData slots[6];
};
struct Coin
{
u32 platinum;
u32 gold;
u32 silver;
u32 copper;
};
struct BandolierItemInfo {
char name[];
s32 item_id;
s32 icon;
};
struct BandolierSet
{
char name[];
BandolierItemInfo items[4];
};
struct ItemIndex
{
s16 slot1;
s16 slot2;
s16 slot3;
};
struct Claim
{
s32 feature_id;
s32 count;
};
struct Tribute {
u32 BenefitTimer;
s32 unknown1;
s32 current_favor;
s32 unknown2;
s32 all_time_favor;
s32 unknown3; //some of these are probably the bools on the pcclient;
u16 unknown4;
};
struct TributeBenefit
{
s32 benefit_id;
s32 benefit_tier;
};
struct RaidData
{
u32 main_assist1;
u32 main_assist2;
u32 main_assist3;
char main_assist_name1[];
char main_assist_name2[];
char main_assist_name3[];
u32 main_marker1;
u32 main_marker2;
u32 main_marker3;
u32 master_looter;
};
struct LdonData
{
u32 count;
u32 ldon_categories[count];
u32 ldon_points_available;
};
struct PvPData
{
u32 kills;
u32 deaths;
u32 current_points;
u32 career_points;
u32 best_kill_streak;
u32 worst_death_streak;
u32 current_kill_streak;
};
struct PvPKill
{
char name[];
u32 level;
u32 unknown1; //not sure
u32 unknown2; //not sure
u32 race;
u32 class;
u32 zone;
u32 time;
u32 points;
};
struct PvPDeath
{
char name[];
u32 level;
u32 race;
u32 class;
u32 zone;
u32 time;
u32 points;
};
struct AltCurrency
{
u32 alt_currency_str_length;
u32 unknown1;
char alt_currency_string[alt_currency_str_length];
};
struct AchivementSubComponentData
{
s32 achievement_id;
s32 component_id;
s32 requirement_id;
s32 requirement_type;
s32 count;
};
struct AlchemyBonusSkillData
{
s32 skill_id;
s32 bonus;
};
struct PersonaItemSlot
{
u32 item_id;
u32 slot_id;
};
struct PersonaEquipment
{
PersonaItemSlot item;
u32 augment_count;
PersonaItemSlot augments[augment_count];
};
struct PersonaEquipmentSet
{
u32 class_id;
u32 equipment_count;
PersonaEquipment equipment[equipment_count];
};
struct PcProfile
{
u32 profile_type;
u32 profile_id;
u32 shroud_template_id;
u8 gender;
u32 race;
u32 class;
u8 level;
u8 level1;
u32 bind_count;
Bind binds[bind_count];
u32 deity;
u32 intoxication;
u32 property_count;
u32 properties[property_count];
u32 armor_prop_count;
ArmorProperty armor_props[armor_prop_count];
u32 base_armor_prop_count;
ArmorProperty base_armor_props[base_armor_prop_count];
u32 body_tint_count;
u32 body_tints[body_tint_count];
u32 equip_tint_count;
u32 equip_tints[equip_tint_count];
u8 hair_color;
u8 facial_hair_color;
u32 npc_tint_index;
u8 eye_color1;
u8 eye_color2;
u8 hair_style;
u8 facial_hair;
u8 face;
u8 old_face;
u32 heritage;
u32 tattoo;
u32 details;
u8 texture_type;
u8 material;
u8 variation;
float height;
float width;
float length;
float view_height;
u32 primary;
u32 secondary;
u32 practices;
u32 base_mana;
u32 base_hp;
u32 base_str;
u32 base_sta;
u32 base_cha;
u32 base_dex;
u32 base_int;
u32 base_agi;
u32 base_wis;
u32 base_heroic_str;
u32 base_heroic_sta;
u32 base_heroic_cha;
u32 base_heroic_dex;
u32 base_heroic_int;
u32 base_heroic_agi;
u32 base_heroic_wis;
u32 aa_count;
AA aas[aa_count];
u32 skill_count;
s32 skills[skill_count];
u32 innate_skill_count;
s32 innate_skills[innate_skill_count];
u32 combat_ability_count;
s32 combat_abilities[combat_ability_count];
u32 combat_ability_timer_count;
s32 combat_ability_timers[combat_ability_timer_count];
u32 unk_ability_count;
u32 linked_spell_timer_count;
s32 linked_spell_timers[linked_spell_timer_count];
u32 item_recast_timer_count;
s32 item_recast_timers[item_recast_timer_count];
u32 spell_book_slot_count;
s32 spell_book_slots[spell_book_slot_count];
u32 spell_gem_count;
s32 spell_gems[spell_gem_count];
u32 spell_recast_timer_count;
s32 spell_recast_timers[spell_recast_timer_count];
u8 max_allowed_spell_slots;
u32 buff_count;
EQAffect buffs[buff_count];
Coin coin;
Coin cursor_coin;
u32 disc_timer;
u32 mend_timer;
u32 forage_timer;
u32 thirst;
u32 hunger;
u32 aa_spent;
u32 aa_window_count;
u32 aa_window_stats[aa_window_count];
u32 aa_points_unspent;
u8 sneak;
u8 hide;
u32 bandolier_count;
BandolierSet bandolier_sets[bandolier_count];
u32 invslot_bitmask;
u32 basedata_hp;
u32 basedata_mana;
u32 basedata_endur;
u32 basedata_mr;
u32 basedata_fr;
u32 basedata_cr;
u32 basedata_pr;
u32 basedata_dr;
u32 basedata_corrupt;
u32 basedata_phr;
float basedata_walkspeed;
float basedata_runspeed;
u32 basedata_hpregen;
u32 basedata_manaregen;
u32 basedata_mountmanaregen;
u32 basedata_endurregen;
u32 basedata_ac;
u32 basedata_atk;
u32 basedata_dmg;
u32 basedata_delay;
u32 endurance;
u32 heroic_type;
ItemIndex keyring_item_index[5];
u64 exp;
u64 aa_exp; //this is a guess, used to be 32 upped to 64
u16 unknown1;
EQGuid character_id;
u32 name_length;
char name[name_length];
u32 last_name_length;
char last_name[last_name_length];
u32 creation_time;
u32 account_creation_time;
u32 last_played_time;
u32 played_minutes;
u32 entitled_days;
u32 expansion_flags;
u32 unknown2; //new field from laurion to obrood
u32 language_count;
u8 languages[language_count];
u32 current_zone;
float current_x;
float current_y;
float current_z;
float current_heading;
u8 animation;
u8 pvp;
u8 anon;
u8 gm;
u64 guild_id;
u8 guild_show_sprite;
u8 status;
Coin coin2;
Coin bank2;
u32 bank_shared_plat;
u32 claim_count;
Claim claims[claim_count];
Tribute tribute;
u32 tribute_benefit_count;
TributeBenefit tribute_benefits[tribute_benefit_count];
u32 trophy_tribute_benefit_count;
TributeBenefit trophy_tribute_benefit[trophy_tribute_benefit_count];
u8 tasks[137]; //honestly not sure what this is, was just a guess
u32 good_points_available;
u32 good_points_earned;
u32 bad_points_available;
u32 bad_points_earned;
u32 momentum_balance;
u32 loyalty_reward_balance;
u32 parcel_status;
u32 vehicle_name_length;
char vehicle_name[vehicle_name_length];
u8 super_pkill;
u8 unclone;
u8 dead;
u32 ld_timer;
u32 spell_interrupt_count;
u8 autosplit;
u8 tells_off;
u8 gm_invis;
u32 kill_me;
u8 cheater_ld_flag;
u8 norent;
u8 corpse;
u8 client_gm_flag_set;
u32 mentor_pct;
RaidData raid;
u32 unique_player_id;
LdonData ldon_data;
u32 air_supply;
PvPData pvp_data;
PvPKill last_kill;
PvPDeath last_death;
u32 kills_in_past_24_hours;
u32 kill_list_count;
PvPKill kill_list[kill_list_count];
u32 pvp_infamy_level;
u32 pvp_vitality;
u32 cursor_krono;
u32 krono;
u8 autoconsent_group;
u8 autoconsent_raid;
u8 autoconsent_guild;
u8 autoconsent_fellowship;
u8 private_for_eq_players;
u32 main_level;
u8 show_helm;
u32 downtime;
AltCurrency alt_currency;
u32 completed_event_subcomponent_count;
AchivementSubComponentData completed_event_subcomponents[completed_event_subcomponent_count];
u32 inprogress_event_subcomponent_count;
AchivementSubComponentData inprogress_event_subcomponents[inprogress_event_subcomponent_count];
u64 merc_aa_exp;
u32 merc_aa_points;
u32 merc_aa_spent;
u32 starting_city_zone_id;
u8 use_advanced_looting;
u8 is_master_loot_candidate;
u32 alchemy_bonus_list_count;
AlchemyBonusSkillData alchemy_bonus_list[alchemy_bonus_list_count];
u32 persona_count;
PersonaEquipmentSet persona_equipment_set[persona_count];
u8 term;
};
struct Packet
{
u32 crc;
u32 length;
PcProfile profile;
};
Packet p @ 0x00;
+28
View File
@@ -0,0 +1,28 @@
def patch_template(template_path, opcodes_path, output_path):
try:
with open(opcodes_path, 'r') as f:
opcodes = [line.strip() for line in f if line.strip()]
with open(template_path, 'r') as f:
content = f.read()
for index, value in enumerate(opcodes):
placeholder = f"{{{{{index}}}}}"
content = content.replace(placeholder, value)
with open(output_path, 'w') as f:
f.write(content)
print(f"Successfully transformed: {output_path}")
except FileNotFoundError as e:
print(f"Error: File Not Found - {e}")
except Exception as e:
print(f"Error: Exception - {e}")
if __name__ == "__main__":
patch_template(
template_path='opcode.template',
opcodes_path='opcodes.csv',
output_path='patch_TOB.conf'
)
View File
File diff suppressed because it is too large Load Diff
+699
View File
@@ -0,0 +1,699 @@
# ShowEQ Import Notes:
# ZERO THE FILE first
# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf
# Unknown Mapping:
# OP_Action2 -> OP_Damage
# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake
# Name Differences:
# OP_CancelInvite -> OP_GroupCancelInvite
# OP_GMFind -> OP_FindPersonRequest
# OP_CommonMessage -> OP_ChannelMessage
OP_Unknown=0x0000
OP_ExploreUnknown=0x0000 # used for unknown explorer
# world packets
# Required to reach Char Select:
OP_SendLoginInfo=0x722a
OP_ApproveWorld=0x0000
OP_LogServer=0x2cae
OP_SendCharInfo=0x5d1a
OP_ExpansionInfo=0x393a
OP_EnterWorld=0x7fb8
OP_PostEnterWorld=0x1945 # unused
OP_World_Client_CRC1=0x777f # This is OP_SendExeChecksum
OP_World_Client_CRC2=0x0492 # This is OP_SendBaseDataChecksum
OP_World_Client_CRC3=0x0690 # This is OP_SendSkillCapsChecksum
OP_SendSpellChecksum=0x0000 # There is no spell checksum in TOB
OP_SendSkillCapsChecksum=0x0690
# Character Select Related:
OP_SendMaxCharacters=0x25eb
OP_SendMembership=0x5789
OP_SendMembershipDetails=0x2373
OP_CharacterCreateRequest=0x6b67
OP_CharacterCreate=0x1859
OP_DeleteCharacter=0x71ca
OP_RandomNameGenerator=0x7f4a # TOB client no longer sends this, and the S->C packet isn't used by emu
OP_ApproveName=0x5306
OP_MOTD=0x1eef
OP_SetChatServer=0x0000
OP_SetChatServer2=0x34c1
OP_ZoneServerInfo=0x2323
OP_WorldComplete=0x1223
OP_WorldUnknown001=0x7723 # SetServerTime, S->C
OP_FloatListThing=0x504f # Movement History, C->S
# Reasons for Disconnect:
OP_ZoneUnavail=0x29a6
OP_WorldClientReady=0x538f
OP_CharacterStillInZone=0x0000
OP_WorldChecksumFailure=0x0000
OP_WorldLoginFailed=0x0000
OP_WorldLogout=0x0000
OP_WorldLevelTooHigh=0x0000
OP_CharInacessable=0x0000
OP_UserCompInfo=0x0000
OP_SendExeChecksum=0x777f
OP_SendBaseDataChecksum=0x0492
# Zone in opcodes
OP_AckPacket=0x776d
OP_ZoneEntry=0x713d
OP_ReqNewZone=0x1ccc
OP_NewZone=0x5ec0
OP_ZoneSpawns=0x6cd9
OP_PlayerProfile=0x08bf
OP_TimeOfDay=0x5503
OP_LevelUpdate=0x54bf
OP_Stamina=0x3b01
OP_RequestClientZoneChange=0x7a59
OP_ZoneChange=0x4816
OP_LockoutTimerInfo=0x0000
OP_ZoneServerReady=0x0000
OP_ZoneInUnknown=0x0000
OP_LogoutReply=0x0000
OP_PreLogoutReply=0x0000
# Required to fully log in
OP_SpawnAppearance=0x513a
OP_ChangeSize=0x1ed0
OP_Weather=0x1e6d
OP_ReqClientSpawn=0x6a27
OP_SpawnDoor=0x532b
OP_GroundSpawn=0x14c7
OP_SendZonepoints=0x21a2
OP_BlockedBuffs=0x0f6f
OP_RemoveBlockedBuffs=0x4471
OP_ClearBlockedBuffs=0x27ce
OP_WorldObjectsSent=0x4f32
OP_SendExpZonein=0x7267
OP_SendAATable=0x22bd
OP_ClearAA=0x6093
OP_ClearLeadershipAbilities=0x0000 #removed; leadership abilities are baked in and always on
OP_RespondAA=0x4449
OP_UpdateAA=0x1655
OP_SendAAStats=0x7416 # Removed in TOB
OP_AAExpUpdate=0x04c3 #need to look into whether this has changed; exp did
OP_ExpUpdate=0x0e55
OP_HPUpdate=0x2723
OP_ManaChange=0x08f6
OP_TGB=0x0000 #removed; tgb is baked in and always on
OP_SpecialMesg=0x08bd
OP_CharInventory=0x15b4
OP_WearChange=0x5897
OP_ClientUpdate=0x1615
OP_ClientReady=0x666f
OP_SetServerFilter=0x5a35
# Guild Opcodes
OP_GuildsList=0x0000
OP_GuildMemberList=0x0000
OP_GuildMOTD=0x0000
OP_GetGuildMOTD=0x0000
OP_GetGuildMOTDReply=0x0000
OP_GuildMemberUpdate=0x0000
OP_GuildInvite=0x0000
OP_GuildRemove=0x0000
OP_GuildPeace=0x0000
OP_SetGuildMOTD=0x0000
OP_GuildWar=0x0000
OP_GuildLeader=0x0000
OP_GuildDelete=0x0000
OP_GuildInviteAccept=0x0000
OP_GuildDemote=0x0000
OP_GuildPromote=0x0000
OP_GuildPublicNote=0x0000
OP_GuildManageBanker=0x0000
OP_GuildBank=0x0000
OP_GuildBankItemList=0x0000
OP_SetGuildRank=0x0000
OP_GuildUpdate=0x0000
OP_GuildStatus=0x0000
OP_GuildCreate=0x0000
OP_GuildOpenGuildWindow=0x0000
OP_GuildMemberLevel=0x0000
OP_GuildMemberRankAltBanker=0x0000
OP_GuildMemberPublicNote=0x0000
OP_GuildMemberAdd=0x0000
OP_GuildMemberRename=0x0000
OP_GuildMemberDelete=0x0000
OP_GuildMemberDetails=0x0000
OP_GuildRenameGuild=0x0000
OP_LFGuild=0x0000
OP_GuildDeleteGuild=0x0000
# GM/Guide Opcodes
OP_GMServers=0x0000
OP_GMBecomeNPC=0x0000
OP_GMZoneRequest=0x0000
OP_GMZoneRequest2=0x0000
OP_GMGoto=0x0000
OP_GMSearchCorpse=0x0000
OP_GMHideMe=0x0000
OP_GMDelCorpse=0x0000
OP_GMApproval=0x0000
OP_GMToggle=0x0000
OP_GMSummon=0x0000
OP_GMEmoteZone=0x0000
OP_GMEmoteWorld=0x0000
OP_GMFind=0x0000
OP_GMKick=0x0000
OP_GMKill=0x0000
OP_GMNameChange=0x0000
OP_GMLastName=0x0000
# Misc Opcodes
OP_QueryUCSServerStatus=0x7093
OP_InspectRequest=0x0000
OP_InspectAnswer=0x0000
OP_InspectMessageUpdate=0x0000
OP_BeginCast=0x34b1
OP_ColoredText=0x1743
OP_ConsentResponse=0x74b3
OP_MemorizeSpell=0x6af0
OP_LinkedReuse=0x5683
OP_SwapSpell=0x32cc
OP_CastSpell=0x1d63
OP_Consider=0x4568
OP_FormattedMessage=0x29b4
OP_SimpleMessage=0x5b2d
OP_Buff=0x2427
OP_Illusion=0x7fb0
OP_MoneyOnCorpse=0x6f63
OP_RandomReply=0x1234
OP_DenyResponse=0x339b
OP_SkillUpdate=0x0149
OP_GMTrainSkillConfirm=0x3365
OP_RandomReq=0x0313
OP_Death=0x1e90
OP_GMTraining=0x3d75
OP_GMEndTraining=0x1bf2
OP_GMTrainSkill=0x1525
OP_Animation=0x3807
OP_Begging=0x217b
OP_Consent=0x6c66
OP_ConsentDeny=0x5343
OP_AutoFire=0x2583
OP_PetCommands=0x0000
OP_PetCommandState=0x0000
OP_PetHoTT=0x0000
OP_DeleteSpell=0x5d53
OP_Surname=0x0000
OP_ClearSurname=0x0000
OP_FaceChange=0x0000
OP_SetFace=0x0000
OP_SenseHeading=0x2ff2
OP_Action=0x7d28
OP_ConsiderCorpse=0x2f98
OP_HideCorpse=0x2623
OP_CorpseDrag=0x7200
OP_CorpseDrop=0x2a53
OP_Bug=0x2846
OP_Feedback=0x0000
OP_Report=0x6b73
OP_Damage=0x5b42
OP_ChannelMessage=0x7622
OP_Assist=0x51a0
OP_AssistGroup=0x4879
OP_MoveCoin=0x1987
OP_ZonePlayerToBind=0x1860
OP_KeyRing=0x0000
OP_WhoAllRequest=0x3328
OP_WhoAllResponse=0x4dfd
OP_FriendsWho=0x3547
OP_ConfirmDelete=0x14a8 # This is sent fromt the client after a movement update (with just spawn ID as the content)
OP_Logout=0x46f8
OP_Rewind=0x898a
OP_TargetCommand=0x46bf
OP_Hide=0x4f10
OP_Jump=0x2b69
OP_Camp=0x4fe0
OP_Emote=0x0000
OP_SetRunMode=0x3b78
OP_BankerChange=0x0fa6
OP_TargetMouse=0x7f48
OP_MobHealth=0x445e
OP_InitialMobHealth=0x0000 # Unused?
OP_TargetHoTT=0x0000
OP_TargetBuffs=0x0000
OP_XTargetResponse=0x0000
OP_XTargetRequest=0x0000
OP_XTargetAutoAddHaters=0x0000
OP_XTargetOpen=0x0000
OP_XTargetOpenResponse=0x0000
OP_BuffCreate=0x754c
OP_BuffRemoveRequest=0x0c06
OP_DeleteSpawn=0x33fa
OP_AutoAttack=0x3ced
OP_AutoAttack2=0x1824
OP_Consume=0x2d2d
OP_MoveItem=0x121c
OP_MoveMultipleItems=0x6bf7
OP_DeleteItem=0x29e6
OP_DeleteCharge=0x4bef
OP_ItemPacket=0x0fb6
OP_ItemLinkResponse=0x0000
OP_ItemLinkClick=0x0000
OP_ItemPreview=0x0000
OP_NewSpawn=0x053d
OP_Track=0x24c2
OP_TrackTarget=0x0941
OP_TrackUnknown=0x0f55
OP_ClickDoor=0x1678
OP_MoveDoor=0x6e1d
OP_RemoveAllDoors=0x01df
OP_EnvDamage=0x2dbe
OP_BoardBoat=0x0c52
OP_LeaveBoat=0x6097
OP_ControlBoat=0x79ad
OP_Forage=0x5b12
OP_SafeFallSuccess=0x6341
OP_RezzComplete=0x0000
OP_RezzRequest=0x0000
OP_RezzAnswer=0x0000
OP_Shielding=0x0000
OP_RequestDuel=0x0000
OP_MobRename=0x0000
OP_AugmentItem=0x322a
OP_WeaponEquip1=0x0000
OP_PlayerStateAdd=0x0e05
OP_PlayerStateRemove=0x0ec0
OP_ApplyPoison=0x7216
OP_Save=0x667f
OP_TestBuff=0x0000
OP_CustomTitles=0x0000
OP_Split=0x3ea6
OP_YellForHelp=0x1330
OP_LoadSpellSet=0x0000
OP_Bandolier=0x0000
OP_PotionBelt=0x0000
OP_DuelDecline=0x0000
OP_DuelAccept=0x0000
OP_SaveOnZoneReq=0x16e3
OP_ReadBook=0x165a
OP_Dye=0x0000
OP_InterruptCast=0x5313
OP_AAAction=0x48fb
OP_LeadershipExpToggle=0x0000 #removed, these act as if all purchased now
OP_LeadershipExpUpdate=0x0000 #removed, these act as if all purchased now
OP_PurchaseLeadershipAA=0x0000 #removed, these act as if all purchased now
OP_UpdateLeadershipAA=0x0000 #removed, these act as if all purchased now
OP_MarkNPC=0x0000
OP_ClearNPCMarks=0x0000
OP_DelegateAbility=0x0000
OP_SetGroupTarget=0x0000
OP_Charm=0x2509
OP_Stun=0x7f1d
OP_SendFindableNPCs=0x0000
OP_FindPersonRequest=0x0000
OP_FindPersonReply=0x0000
OP_Sound=0x5949
OP_CashReward=0x3237
OP_PetBuffWindow=0x0000
OP_LevelAppearance=0x5eb5
OP_Translocate=0x0611
OP_Sacrifice=0x4b76
OP_PopupResponse=0x4032
OP_OnLevelMessage=0x552e
OP_AugmentInfo=0x19eb
OP_Petition=0x0000
OP_SomeItemPacketMaybe=0x0000
OP_PVPStats=0x0000
OP_PVPLeaderBoardRequest=0x0000
OP_PVPLeaderBoardReply=0x0000
OP_PVPLeaderBoardDetailsRequest=0x0000
OP_PVPLeaderBoardDetailsReply=0x0000
OP_RestState=0x1930
OP_RespawnWindow=0x5c
OP_LDoNButton=0x0000
OP_SetStartCity=0x0000
OP_VoiceMacroIn=0x2963
OP_VoiceMacroOut=0x028d
OP_ItemViewUnknown=0x0000
OP_VetRewardsAvaliable=0x0000
OP_VetClaimRequest=0x0000
OP_VetClaimReply=0x0000
OP_DisciplineUpdate=0x0a2f
OP_DisciplineTimer=0x2782
OP_BecomeCorpse=0x0000 # Unused?
OP_Action2=0x0000 # Unused?
OP_MobUpdate=0x0000
OP_NPCMoveUpdate=0x0000
OP_CameraEffect=0x3352
OP_SpellEffect=0x7ea2
OP_AddNimbusEffect=0x54b1
OP_RemoveNimbusEffect=0x4e88
OP_AltCurrency=0x0000
OP_AltCurrencyMerchantRequest=0x0000
OP_AltCurrencyMerchantReply=0x0000
OP_AltCurrencyPurchase=0x0000
OP_AltCurrencySell=0x0000
OP_AltCurrencySellSelection=0x0000
OP_AltCurrencyReclaim=0x0000
OP_CrystalCountUpdate=0x0000
OP_CrystalCreate=0x0000
OP_CrystalReclaim=0x0000
OP_Untargetable=0x6205
OP_IncreaseStats=0x1312
OP_Weblink=0x41f7
OP_OpenContainer=0x1e8a
OP_Marquee=0x257c
OP_ItemRecastDelay=0x0fe4
#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U
OP_ResetAA=0x3126
OP_Fling=0x1101
OP_CancelSneakHide=0x14b0
OP_AggroMeterLockTarget=0x0000
OP_AggroMeterTargetInfo=0x0000
OP_AggroMeterUpdate=0x0000
OP_UnderWorld=0x156c # clients sends up when they detect an underworld issue, might be useful for cheat detection
OP_KickPlayers=0x1257
OP_BookButton=0x4e78
# Expeditions
OP_DzQuit=0x0000
OP_DzListTimers=0x0000
OP_DzAddPlayer=0x0000
OP_DzRemovePlayer=0x0000
OP_DzSwapPlayer=0x0000
OP_DzMakeLeader=0x0000
OP_DzPlayerList=0x0000
OP_DzExpeditionInvite=0x0000
OP_DzExpeditionInviteResponse=0x0000
OP_DzExpeditionInfo=0x0000
OP_DzExpeditionLockoutTimers=0x0000
OP_DzMemberList=0x0000
OP_DzMemberListName=0x0000
OP_DzMemberListStatus=0x0000
OP_DzSetLeaderName=0x0000
OP_DzExpeditionEndsWarning=0x0000
OP_DzCompass=0x0000
OP_DzChooseZone=0x0000
OP_DzChooseZoneReply=0x0000
# New Opcodes
OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ?
OP_ManaUpdate=0x0000
OP_EnduranceUpdate=0x0000
OP_MobManaUpdate=0x0000
OP_MobEnduranceUpdate=0x0000
# Mercenary Opcodes
OP_MercenaryDataUpdateRequest=0x0000
OP_MercenaryDataUpdate=0x0000
OP_MercenaryDataRequest=0x0000
OP_MercenaryDataResponse=0x0000
OP_MercenaryHire=0x0000
OP_MercenaryDismiss=0x0000
OP_MercenaryTimerRequest=0x0000
OP_MercenaryTimer=0x0000
OP_MercenaryUnknown1=0x0000
OP_MercenaryCommand=0x0000
OP_MercenarySuspendRequest=0x0000
OP_MercenarySuspendResponse=0x0000
OP_MercenaryUnsuspendResponse=0x0000
# Looting
OP_LootRequest=0x2239
OP_EndLootRequest=0x173c
OP_LootItem=0x5241
OP_LootComplete=0x5470
# bazaar trader stuff:
OP_BazaarSearch=0x0000
OP_TraderDelItem=0x0000
OP_BecomeTrader=0x0000
OP_TraderShop=0x0000
OP_TraderBulkSend=0x0000
OP_Trader=0x0000
OP_Barter=0x0000
OP_BuyerItems=0x0000
OP_TraderBuy=0x0000
OP_ShopItem=0x0000
OP_BazaarInspect=0x0000
OP_Bazaar=0x0000
OP_TraderItemUpdate=0x0000
# pc/npc trading
OP_TradeRequest=0x1f5d
OP_TradeAcceptClick=0x1d90
OP_TradeRequestAck=0x2737
OP_TradeCoins=0x63d8
OP_FinishTrade=0x0875
OP_CancelTrade=0x5fb5
OP_TradeMoneyUpdate=0x544d
OP_MoneyUpdate=0x578a
OP_TradeBusy=0x43b8
# Sent after canceling trade or after closing tradeskill object
OP_FinishWindow=0x1935
OP_FinishWindow2=0x5458
# Sent on Live for what seems to be item existance verification
# Ex. Before Right Click Effect happens from items
OP_ItemVerifyRequest=0x4d8e
OP_ItemVerifyReply=0x0bce
OP_ItemAdvancedLoreText=0x0000
# merchant stuff
OP_ShopPlayerSell=0x5d8e
OP_ShopRequest=0x25d0
OP_ShopEnd=0x3e98
OP_ShopEndConfirm=0x493d
OP_ShopPlayerBuy=0x0696
OP_ShopDelItem=0x0672
OP_ShopSendParcel=0x3d05
OP_ShopDeleteParcel=0x109b
OP_ShopRetrieveParcel=0x5d5b
OP_ShopParcelIcon=0x1936
# tradeskill stuff:
OP_ClickObject=0x6693
OP_ClickObjectAction=0x3dbe
OP_ClearObject=0x3df2
OP_RecipeDetails=0x400f
OP_RecipesFavorite=0x2d6b
OP_RecipesSearch=0x1a3a
OP_RecipeReply=0x3e33
OP_RecipeAutoCombine=0x5257
OP_TradeSkillCombine=0x40af
# Tribute Packets:
OP_TributeUpdate=0x0000
OP_TributeTimer=0x0000
OP_SendTributes=0x0000
OP_RequestGuildTributes=0x0000
OP_TributeInfo=0x0000
OP_OpenTributeMaster=0x0000
OP_SelectTribute=0x0000
OP_TributeItem=0x0000
OP_TributeMoney=0x0000
OP_TributeToggle=0x0000
OP_TributePointUpdate=0x0000
OP_TributeNPC=0x0000
OP_GuildTributeInfo=0x0000
OP_OpenTributeReply=0x0000
OP_GuildTributeStatus=0x0000
OP_GuildSaveActiveTributes=0x0000
OP_GuildSendActiveTributes=0x0000
OP_GuildTributeToggleReq=0x0000
OP_GuildTributeToggleReply=0x0000
OP_GuildTributeFavorAndTimer=0x0000
OP_GuildTributeDonateItem=0x0000
OP_GuildTributeDonatePlat=0x0000
OP_GuildSelectTribute=0x0000
OP_GuildModifyBenefits=0x0000
OP_GuildOptInOut=0x0000
OP_SendGuildTributes=0x0000
OP_OpenGuildTributeMaster=0x0000
# Adventure packets:
OP_LeaveAdventure=0x0000
OP_AdventureFinish=0x0000
OP_AdventureInfoRequest=0x0000
OP_AdventureInfo=0x0000
OP_AdventureRequest=0x0000
OP_AdventureDetails=0x0000
OP_AdventureData=0x0000
OP_AdventureUpdate=0x0000
OP_AdventureMerchantRequest=0x0000
OP_AdventureMerchantResponse=0x0000
OP_AdventureMerchantPurchase=0x0000
OP_AdventureMerchantSell=0x0000
OP_AdventurePointsUpdate=0x0000
OP_AdventureStatsRequest=0x0000
OP_AdventureStatsReply=0x0000
OP_AdventureLeaderboardRequest=0x0000
OP_AdventureLeaderboardReply=0x0000
# Group Opcodes
OP_GroupDisband=0x0573
OP_GroupInvite=0x7e79
OP_GroupFollow=0x0000
OP_GroupUpdate=0x0000
OP_GroupUpdateB=0x0000
OP_GroupCancelInvite=0x0000
OP_GroupAcknowledge=0x0000
OP_GroupDelete=0x0000
OP_CancelInvite=0x0000
OP_GroupFollow2=0x0000
OP_GroupInvite2=0x43b1
OP_GroupDisbandYou=0x0000
OP_GroupDisbandOther=0x0000
OP_GroupLeaderChange=0x0000
OP_GroupRoles=0x0000
OP_GroupMakeLeader=0x0000
OP_DoGroupLeadershipAbility=0x0000
OP_GroupLeadershipAAUpdate=0x0000 # removed these act as if you have always purchased them
OP_GroupMentor=0x0000
OP_InspectBuffs=0x0000
# LFG/LFP Opcodes
OP_LFGCommand=0x0000
OP_LFGGetMatchesRequest=0x0000
OP_LFGGetMatchesResponse=0x0000
OP_LFPGetMatchesRequest=0x0000
OP_LFPGetMatchesResponse=0x0000
OP_LFPCommand=0x0000
OP_LFGAppearance=0x0000
OP_LFGResponse=0x0000
# Raid Opcodes
OP_RaidInvite=0x0000
OP_RaidUpdate=0x0000
OP_RaidJoin=0x0000
OP_RaidDelegateAbility=0x0000
OP_MarkRaidNPC=0x0000
OP_RaidClearNPCMarks=0x0000
# Button-push commands
OP_Taunt=0x6ad9
OP_CombatAbility=0x50e2
OP_SenseTraps=0x235e
OP_PickPocket=0x2c63
OP_DisarmTraps=0x7362
OP_Disarm=0x5a91
OP_Sneak=0x7f05
OP_Fishing=0x3cdb
OP_InstillDoubt=0x3cdb
OP_FeignDeath=0x3d9f
OP_Mend=0x3bac
OP_Bind_Wound=0x580f
OP_LDoNOpen=0x7a62
OP_LDoNPickLock=0x36ea
OP_LDoNInspect=0x256a
# Task packets
OP_TaskDescription=0x0000
OP_TaskActivity=0x0000
OP_CompletedTasks=0x0000
OP_TaskActivityComplete=0x0000
OP_AcceptNewTask=0x0000
OP_CancelTask=0x0000
OP_AvaliableTask=0x0000
OP_TaskHistoryRequest=0x0000
OP_TaskHistoryReply=0x0000
OP_DeclineAllTasks=0x0000
OP_TaskRequestTimer=0x0000
OP_TaskSelectWindow=0x0000
# Shared Tasks
OP_SharedTaskMemberList=0x0000 #
OP_SharedTaskRemovePlayer=0x0000 # /taskremoveplayer
OP_SharedTaskAddPlayer=0x0000 # /taskaddplayer
OP_SharedTaskMakeLeader=0x0000 # /taskmakeleader
OP_SharedTaskInvite=0x0000 # Dialog window
OP_SharedTaskInviteResponse=0x0000 # Dialog window response
OP_SharedTaskAcceptNew=0x0000 #
OP_SharedTaskMemberChange=0x0000 #
OP_TaskTimers=0x0000 # /tasktimers
OP_SharedTaskQuit=0x0000 # /taskquit
OP_SharedTaskSelectWindow=0x0000
OP_SharedTaskPlayerList=0x0000 # /taskplayerlist
# Title opcodes
OP_NewTitlesAvailable=0x0000
OP_RequestTitles=0x0000
OP_SendTitleList=0x0000
OP_SetTitle=0x0000
OP_SetTitleReply=0x0000
# mail opcodes
OP_Command=0x0000
OP_MailboxHeader=0x0000
OP_MailHeader=0x0000
OP_MailBody=0x0000
OP_NewMail=0x0000
OP_SentConfirm=0x0000
########### Below this point should not be needed ###########
# This section are all unknown in Titanium
OP_ForceFindPerson=0x0000
OP_LocInfo=0x0000
OP_ReloadUI=0x0000
OP_ItemName=0x0000
OP_ItemLinkText=0x0000
OP_MultiLineMsg=0x0000
OP_MendHPUpdate=0x0000
OP_TargetReject=0x0000
OP_SafePoint=0x0000
OP_ApproveZone=0x0000
OP_ZoneComplete=0x0000
OP_ClientError=0x0000
OP_DumpName=0x0000
OP_Heartbeat=0x0000
OP_CrashDump=0x0000
OP_LoginComplete=0x0000
# discovered opcodes not yet used:
OP_PickLockSuccess=0x0000
OP_PlayMP3=0x2e09
OP_ReclaimCrystals=0x0000
OP_DynamicWall=0x0000
OP_OpenDiscordMerchant=0x0000
OP_DiscordMerchantInventory=0x0000
OP_GiveMoney=0x0000
OP_RequestKnowledgeBase=0x0000
OP_KnowledgeBase=0x0000
OP_SlashAdventure=0x0000 # /adventure
OP_BecomePVPPrompt=0x0000
OP_MoveLogRequest=0x0000 # gone I think
OP_MoveLogDisregard=0x0000 # gone I think
# named unknowns, to make looking for real unknown easier
OP_AnnoyingZoneUnknown=0x0000
OP_Some6ByteHPUpdate=0x0000 #seems to happen when you target group members
OP_QueryResponseThing=0x0000
# realityincarnate: these are just here to stop annoying several thousand byte packet dumps
#OP_LoginUnknown1=0x0000 # OP_SendSpellChecksum
#OP_LoginUnknown2=0x0000 # OP_SendSkillCapsChecksum
# Petition Opcodes
OP_PetitionSearch=0x0000 #search term for petition
OP_PetitionSearchResults=0x0000 #(list of?) matches from search
OP_PetitionSearchText=0x0000 #text results of search
OP_PetitionUpdate=0x0000
OP_PetitionCheckout=0x0000
OP_PetitionCheckIn=0x0000
OP_PetitionQue=0x0000
OP_PetitionUnCheckout=0x0000
OP_PetitionDelete=0x0000
OP_DeletePetition=0x0000
OP_PetitionResolve=0x0000
OP_PDeletePetition=0x0000
OP_PetitionBug=0x0000
OP_PetitionRefresh=0x0000
OP_PetitionCheckout2=0x0000
OP_PetitionViewPetition=0x0000
#aura related
OP_UpdateAura=0x0000
OP_RemoveTrap=0x0000
OP_SystemFingerprint=0x52ef
+4 -2
View File
@@ -1223,8 +1223,10 @@ bool Client::Process() {
}
if(connect.Check()){
SendGuildList();// Send OPCode: OP_GuildsList
SendApproveWorld();
if (!(m_ClientVersionBit & EQ::versions::maskTOBAndLater)) {
SendGuildList();// Send OPCode: OP_GuildsList
SendApproveWorld();
}
connect.Disable();
}
+4
View File
@@ -28,6 +28,7 @@ set(zone_sources
client_mods.cpp
client_packet.cpp
client_process.cpp
client_version.cpp
combat_record.cpp
corpse.cpp
dialogue_window.cpp
@@ -130,6 +131,7 @@ set(zone_headers
cheat_manager.h
client.h
client_packet.h
client_version.h
combat_record.h
command.h
common.h
@@ -673,6 +675,8 @@ set_property(TARGET gm_commands_zone PROPERTY FOLDER libraries)
add_executable(zone ${zone_sources} ${zone_headers})
target_include_directories(zone PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
if(EQEMU_BUILD_PCH)
+2 -2
View File
@@ -5922,13 +5922,13 @@ bool Bot::CastSpell(
if (DivineAura()) {
LogSpellsDetail("Spell casting canceled: cannot cast while Divine Aura is in effect");
InterruptSpell(SPELL_FIZZLE, 0x121, false);
InterruptSpell(SPELL_FIZZLE, Chat::SpellFailure, false);
return false;
}
if (slot < EQ::spells::CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
InterruptSpell(fizzle_msg, 0x121, spell_id);
InterruptSpell(fizzle_msg, Chat::SpellFailure, spell_id);
uint32 use_mana = ((spells[spell_id].mana) / 4);
LogSpellsDetail("Spell casting canceled: fizzled. [{}] mana has been consumed", use_mana);
+91 -112
View File
@@ -46,6 +46,7 @@
#include "common/zone_store.h"
#include "zone/bot_command.h"
#include "zone/cheat_manager.h"
#include "zone/client_version.h"
#include "zone/command.h"
#include "zone/dialogue_window.h"
#include "zone/dynamic_zone.h"
@@ -1205,7 +1206,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
LogDebug("Client::ChannelMessageReceived() Channel:[{}] message:[{}]", chan_num, message);
if (RuleB(Chat, AlwaysCaptureCommandText)) {
if (message[0] == COMMAND_CHAR) {
if (message[0] == COMMAND_CHAR || message[0] == COMMAND_CHAR_NON_HASH) {
if (command_dispatch(this, message, false) == -2) {
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
@@ -1538,7 +1539,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
}
}
if (message[0] == COMMAND_CHAR) {
if (message[0] == COMMAND_CHAR || message[0] == COMMAND_CHAR_NON_HASH) {
if (command_dispatch(this, message, false) == -2) {
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
@@ -1792,7 +1793,7 @@ void Client::Message(uint32 type, const char* message, ...) {
}
void Client::FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...) {
if (!FilteredMessageCheck(sender, filter))
if (!ShouldGetPacket(sender, filter))
return;
va_list argptr;
@@ -3808,18 +3809,12 @@ void Client::MessageString(uint32 type, uint32 string_id, uint32 distance)
return;
if (GetFilter(FilterSpellCrits) == FilterHide && type == Chat::SpellCrit)
return;
auto outapp = new EQApplicationPacket(OP_SimpleMessage, 12);
SimpleMessage_Struct* sms = (SimpleMessage_Struct*)outapp->pBuffer;
sms->color=type;
sms->string_id=string_id;
sms->unknown8=0;
if(distance>0)
entity_list.QueueCloseClients(this,outapp,false,distance);
if (distance > 0)
Message::CloseMessageString(this, false, static_cast<float>(distance))(
type, string_id);
else
QueuePacket(outapp);
safe_delete(outapp);
Message::MessageString(this, type, string_id);
}
//
@@ -3829,9 +3824,9 @@ void Client::MessageString(uint32 type, uint32 string_id, uint32 distance)
// This hack sucks but it's gonna work for now.
//
void Client::MessageString(uint32 type, uint32 string_id, const char* message1,
const char* message2,const char* message3,const char* message4,
const char* message5,const char* message6,const char* message7,
const char* message8,const char* message9, uint32 distance)
const char* message2, const char* message3, const char* message4,
const char* message5, const char* message6, const char* message7,
const char* message8, const char* message9, uint32 distance)
{
if (GetFilter(FilterSpellDamage) == FilterHide && type == Chat::NonMelee)
return;
@@ -3847,34 +3842,12 @@ void Client::MessageString(uint32 type, uint32 string_id, const char* message1,
if (type == Chat::Emote)
type = 4;
if (!message1) {
MessageString(type, string_id); // use the simple message instead
return;
}
const char *message_arg[] = {
message1, message2, message3, message4, message5,
message6, message7, message8, message9
};
SerializeBuffer buf(20);
buf.WriteInt32(0); // unknown
buf.WriteInt32(string_id);
buf.WriteInt32(type);
for (auto &m : message_arg) {
if (m == nullptr)
break;
buf.WriteString(m);
}
buf.WriteInt8(0); // prevent oob in packet translation, maybe clean that up sometime
auto outapp = std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
if (distance > 0)
entity_list.QueueCloseClients(this, outapp.get(), false, distance);
Message::CloseMessageString(this, false, static_cast<float>(distance))(type, string_id, message1,
message2, message3, message4, message5, message6, message7, message8, message9);
else
QueuePacket(outapp.get());
Message::MessageString(this, type, string_id, message1, message2, message3, message4, message5,
message6, message7, message8, message9);
}
void Client::MessageString(const CZClientMessageString_Struct* msg)
@@ -3898,60 +3871,49 @@ void Client::MessageString(const CZClientMessageString_Struct* msg)
}
}
// helper function, returns true if we should see the message
bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter)
// helper function, returns true if the client should get the packet based on the filter and sender
bool Client::ShouldGetPacket(Mob *sender, eqFilterType filter)
{
eqFilterMode mode = GetFilter(filter);
// easy ones first
if (mode == FilterShow) {
return true;
} else if (mode == FilterHide) {
return false;
}
if (sender != this && mode == FilterShowSelfOnly) {
// easy ones first
if (mode == FilterShow)
return true;
if (mode == FilterHide)
return false;
} else if (sender) {
if (mode == FilterShowGroupOnly) {
auto g = GetGroup();
auto r = GetRaid();
if (g) {
if (g->IsGroupMember(sender)) {
return true;
}
} else if (r && sender->IsClient()) {
auto rgid1 = r->GetGroup(this);
auto rgid2 = r->GetGroup(sender->CastToClient());
if (rgid1 != RAID_GROUPLESS && rgid1 == rgid2) {
return true;
}
} else {
return false;
}
if (sender != this && mode == FilterShowSelfOnly)
return false;
if (sender != nullptr && mode == FilterShowGroupOnly) {
if (sender == this)
return true;
Group* g = GetGroup();
if (g && g->IsGroupMember(sender))
return true;
Raid* r = GetRaid();
if (r && sender->IsClient()) {
uint32 rgid1 = r->GetGroup(this);
uint32 rgid2 = r->GetGroup(sender->CastToClient());
if (rgid1 != RAID_GROUPLESS && rgid1 == rgid2)
return true;
} else {
return false;
}
}
// we passed our checks
// fallback case (send by default)
return true;
}
void Client::FilteredMessageString(Mob *sender, uint32 type,
eqFilterType filter, uint32 string_id)
{
if (!FilteredMessageCheck(sender, filter))
return;
auto outapp = new EQApplicationPacket(OP_SimpleMessage, 12);
SimpleMessage_Struct *sms = (SimpleMessage_Struct *)outapp->pBuffer;
sms->color = type;
sms->string_id = string_id;
sms->unknown8 = 0;
QueuePacket(outapp);
safe_delete(outapp);
return;
if (ShouldGetPacket(sender, filter))
MessageString(type, string_id);
}
void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id,
@@ -3959,37 +3921,16 @@ void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter
const char *message4, const char *message5, const char *message6,
const char *message7, const char *message8, const char *message9)
{
if (!FilteredMessageCheck(sender, filter))
return;
if (type == Chat::Emote)
type = 4;
if (!message1) {
FilteredMessageString(sender, type, filter, string_id); // use the simple message instead
return;
} else if (ShouldGetPacket(sender, filter)) {
if (type == Chat::Emote)
type = 4;
MessageString(
type, string_id, message1, message2, message3, message4,
message5, message6, message7, message8, message9);
}
const char *message_arg[] = {
message1, message2, message3, message4, message5,
message6, message7, message8, message9
};
SerializeBuffer buf(20);
buf.WriteInt32(0); // unknown
buf.WriteInt32(string_id);
buf.WriteInt32(type);
for (auto &m : message_arg) {
if (m == nullptr)
break;
buf.WriteString(m);
}
buf.WriteInt8(0); // prevent oob in packet translation, maybe clean that up sometime
auto outapp = std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
QueuePacket(outapp.get());
}
void Client::Tell_StringID(uint32 string_id, const char *who, const char *message)
@@ -8133,7 +8074,7 @@ void Client::GarbleMessage(char *message, uint8 variance)
int delimiter_count = 0;
// Don't garble # commands
if (message[0] == COMMAND_CHAR || message[0] == BOT_COMMAND_CHAR) {
if (message[0] == COMMAND_CHAR || message[0] == COMMAND_CHAR_NON_HASH || message[0] == BOT_COMMAND_CHAR) {
return;
}
@@ -9208,6 +9149,44 @@ void Client::SendHPUpdateMarquee(){
SendMarqueeMessage(Chat::Yellow, 510, 0, 3000, 3000, health_update_notification);
}
void Client::SendMembership() {
if (m_ClientVersion >= EQ::versions::ClientVersion::TOB) {
auto outapp = new EQApplicationPacket(OP_SendMembership, sizeof(Membership_Struct));
Membership_Struct* mc = (Membership_Struct*)outapp->pBuffer;
mc->membership = 2; //Hardcode to gold for now. We don't use anything else.
mc->races = 0x1ffff; // Available Races (4110 for silver)
mc->classes = 0x1ffff; // Available Classes (4614 for silver) - Was 0x101ffff
mc->entrysize = 21; // Number of membership setting entries below
mc->entries[0] = 0xffffffff; // Max AA Restriction
mc->entries[1] = 0xffffffff; // Max Level Restriction
mc->entries[2] = 0xffffffff; // Max Char Slots per Account (not used by client?)
mc->entries[3] = 0xffffffff; // 1 for Silver
mc->entries[4] = 0xffffffff; // Main Inventory Size (0xffffffff on Live for Gold, but limiting to 8 until 10 is supported)
mc->entries[5] = 0xffffffff; // Max Platinum per level
mc->entries[6] = 1; // 0 for Silver
mc->entries[7] = 1; // 0 for Silver
mc->entries[8] = 1; // 1 for Silver
mc->entries[9] = 0xffffffff; // Unknown - Maybe Loyalty Points every 12 hours? 60 per week for Silver
mc->entries[10] = 1; // 1 for Silver
mc->entries[11] = 0xffffffff; // Shared Bank Slots
mc->entries[12] = 0xffffffff; // Unknown - Maybe Max Active Tasks?
mc->entries[13] = 1; // 1 for Silver
mc->entries[14] = 1; // 0 for Silver
mc->entries[15] = 1; // 0 for Silver
mc->entries[16] = 1; // 1 for Silver
mc->entries[17] = 1; // 0 for Silver
mc->entries[18] = 1; // 0 for Silver
mc->entries[19] = 0xffffffff; // 0 for Silver
mc->entries[20] = 0xffffffff; // 0 for Silver
mc->exit_url_length = 0;
//mc->exit_url = 0; // Used on Live: "http://www.everquest.com/free-to-play/exit-silver"
QueuePacket(outapp);
safe_delete(outapp);
}
}
uint32 Client::GetMoney(uint8 type, uint8 subtype) {
uint32 value = 0;
+6 -4
View File
@@ -343,10 +343,10 @@ public:
void DyeArmor(EQ::TintProfile* dye);
void DyeArmorBySlot(uint8 slot, uint8 red, uint8 green, uint8 blue, uint8 use_tint = 0x00);
uint8 SlotConvert(uint8 slot,bool bracer=false);
void MessageString(uint32 type, uint32 string_id, uint32 distance = 0);
void MessageString(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0);
void MessageString(uint32 type, uint32 string_id, uint32 distance = 0) override;
void MessageString(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0) override;
void MessageString(const CZClientMessageString_Struct* msg);
bool FilteredMessageCheck(Mob *sender, eqFilterType filter);
bool ShouldGetPacket(Mob *sender, eqFilterType filter);
void FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id);
void FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter,
uint32 string_id, const char *message1, const char *message2 = nullptr,
@@ -1550,7 +1550,8 @@ public:
inline const EQ::versions::ClientVersion ClientVersion() const { return m_ClientVersion; }
inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; }
inline void SetClientVersion(EQ::versions::ClientVersion client_version) { m_ClientVersion = client_version; }
void SetClientVersion(EQ::versions::ClientVersion client_version);
EQ::versions::ClientVersion GetClientVersion() const;
/** Adventure Stuff **/
void SendAdventureError(const char *error);
@@ -1856,6 +1857,7 @@ public:
void ResetHPUpdateTimer() { hpupdate_timer.Start(); }
void SendHPUpdateMarquee();
void SendMembership();
void CheckRegionTypeChanges();
+42 -54
View File
@@ -105,7 +105,6 @@ void MapOpcodes()
ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry;
// connected opcode handler assignments:
ConnectedOpcodes[OP_0x0193] = &Client::Handle_0x0193;
ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction;
ConnectedOpcodes[OP_AcceptNewTask] = &Client::Handle_OP_AcceptNewTask;
ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest;
@@ -1240,9 +1239,6 @@ void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app)
void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app)
{
auto outapp = new EQApplicationPacket(OP_0x0347, 0);
QueuePacket(outapp);
safe_delete(outapp);
return;
}
@@ -1705,10 +1701,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate)))
m_pp.RestTimer = 0;
/* This checksum should disappear once dynamic structs are in... each struct strategy will do it */ // looks to be in place now
//CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct) - sizeof(m_pp.m_player_profile_version) - 4);
// m_pp.checksum = 0; // All server out-bound player profile packets are now translated - no need to waste cycles calculating this...
SendMembership();
outapp = new EQApplicationPacket(OP_PlayerProfile, sizeof(PlayerProfile_Struct));
/* The entityid field in the Player Profile is used by the Client in relation to Group Leadership AA */
@@ -1882,16 +1876,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
return;
}
// connected opcode handlers
void Client::Handle_0x0193(const EQApplicationPacket *app)
{
// Not sure what this opcode does. It started being sent when OP_ClientUpdate was
// changed to pump OP_ClientUpdate back out instead of OP_MobUpdate
// 2 bytes: 00 00
return;
}
void Client::Handle_OP_AAAction(const EQApplicationPacket *app)
{
LogAA("Received OP_AAAction");
@@ -4277,21 +4261,30 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app)
if (IsLFP())
worldserver.StopLFP(CharacterID());
if (GetGM())
{
if (RuleB(Character, EnableHackedFastCampForGM))
{
camp_timer.Start(100, true);
}
else {
OnDisconnect(true);
if (ClientVersion() >= EQ::versions::ClientVersion::TOB) {
if (!GetGM()) {
camp_timer.Start(29000, true);
}
return;
auto outapp = new EQApplicationPacket(OP_Camp, 1);
FastQueuePacket(&outapp);
}
else {
if (GetGM())
{
if (RuleB(Character, EnableHackedFastCampForGM))
{
camp_timer.Start(100, true);
}
else {
OnDisconnect(true);
}
return;
}
camp_timer.Start(29000, true);
camp_timer.Start(29000, true);
}
if (RuleB(Bots, Enabled)) {
bot_camp_timer.Start((RuleI(Bots, CampTimer) * 1000), true);
@@ -8919,31 +8912,20 @@ void Client::Handle_OP_Hide(const EQApplicationPacket *app)
tmHidden = Timer::GetCurrentTime();
}
if (GetClass() == Class::Rogue) {
auto outapp = new EQApplicationPacket(OP_SimpleMessage, sizeof(SimpleMessage_Struct));
SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer;
msg->color = 0x010E;
Mob *evadetar = GetTarget();
if (!auto_attack && (evadetar && evadetar->CheckAggro(this)
&& evadetar->IsNPC())) {
uint32 string_id = HIDE_FAIL;
Mob* evadetar = GetTarget();
if (!auto_attack && evadetar && evadetar->CheckAggro(this) && evadetar->IsNPC()) {
if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) {
msg->string_id = EVADE_SUCCESS;
string_id = EVADE_SUCCESS;
RogueEvade(evadetar);
}
else {
msg->string_id = EVADE_FAIL;
}
}
else {
if (hidden) {
msg->string_id = HIDE_SUCCESS;
}
else {
msg->string_id = HIDE_FAIL;
}
}
FastQueuePacket(&outapp);
} else
string_id = EVADE_FAIL;
} else if (hidden)
string_id = HIDE_SUCCESS;
MessageString(Chat::Skills, string_id);
}
return;
}
void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app)
@@ -10332,10 +10314,10 @@ void Client::Handle_OP_ManaChange(const EQApplicationPacket *app)
if (app->size == 0) {
// i think thats the sign to stop the songs
if (IsBardSong(casting_spell_id) || HasActiveSong()) {
InterruptSpell(SONG_ENDS, 0x121); //Live doesn't send song end message anymore (~Kayen 1/26/22)
InterruptSpell(SONG_ENDS, Chat::SpellFailure); //Live doesn't send song end message anymore (~Kayen 1/26/22)
}
else {
InterruptSpell(INTERRUPT_SPELL, 0x121);
InterruptSpell(INTERRUPT_SPELL, Chat::SpellFailure);
}
return;
}
@@ -12346,6 +12328,9 @@ void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app)
case EQ::versions::ClientVersion::RoF2:
ConnectionType = EQ::versions::ucsRoF2Combined;
break;
case EQ::versions::ClientVersion::TOB:
ConnectionType = EQ::versions::ucsTOBCombined;
break;
default:
ConnectionType = EQ::versions::ucsUnknown;
break;
@@ -14668,7 +14653,10 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
mco->rate = 1 / buy_cost_mod;
}
outapp->priority = 6;
if (m_ClientVersion >= EQ::versions::ClientVersion::TOB) {
mco->player_id = GetID();
}
QueuePacket(outapp);
safe_delete(outapp);
-2
View File
@@ -38,8 +38,6 @@
void Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app);
void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app);
/* Connected opcode handlers*/
void Handle_0x0193(const EQApplicationPacket *app);
void Handle_0x01e7(const EQApplicationPacket *app);
void Handle_OP_AAAction(const EQApplicationPacket *app);
void Handle_OP_AcceptNewTask(const EQApplicationPacket *app);
void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app);
+2 -2
View File
@@ -229,11 +229,11 @@ bool Client::Process() {
}
if (song_target == nullptr) {
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
InterruptSpell(SONG_ENDS_ABRUPTLY, Chat::SpellFailure, bardsong);
}
else {
if (!ApplyBardPulse(bardsong, song_target, bardsong_slot)) {
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
InterruptSpell(SONG_ENDS_ABRUPTLY, Chat::SpellFailure, bardsong);
}
}
}
+29
View File
@@ -0,0 +1,29 @@
/* EQEmu: EQEmulator
Copyright (C) 2001-2026 EQEmu Development Team
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; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; 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, see <http://www.gnu.org/licenses/>.
*/
#include "client_version.h"
using Version = EQ::versions::ClientVersion;
void Client::SetClientVersion(Version client_version)
{
m_ClientVersion = client_version;
m_ClientVersionBit = EQ::versions::ConvertClientVersionToClientVersionBit(client_version);
}
Version Client::GetClientVersion() const { return m_ClientVersion; }
+150
View File
@@ -0,0 +1,150 @@
//
// Created by dannu on 4/21/2026.
//
#pragma once
#include "common/emu_versions.h"
#include "common/patches/client_version.h"
#include "common/patches/IMessage.h"
#include "zone/client.h"
#include "zone/mob.h"
// store all _generic_ static functions for the different patches here
namespace ClientPatch {
using ClientList = std::unordered_map<uint16, Client*>;
template<typename Obj> using ComponentGetter = std::function<Obj*(const Client*)>;
template <typename Fun, typename Obj, typename... Args>
requires std::is_member_function_pointer_v<Fun>
static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args)
{
if (obj != nullptr) {
std::unique_ptr<EQApplicationPacket> app = std::invoke(fun, obj, std::forward<Args>(args)...);
if (app)
c->QueuePacket(app.get());
}
}
// packet generator queue functions
static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = true)
{
return [=]<typename Fun, typename Obj, typename... Args>(Fun fun, const ComponentGetter<Obj>& component, Args&&... args)
requires std::is_member_function_pointer_v<Fun>
{
std::array<std::unique_ptr<EQApplicationPacket>, EQ::versions::ClientVersionCount> build_packets;
std::unordered_map<uint16, Client*> client_list = entity_list.GetClientList();
for (auto [_, ent] : client_list) {
if (!ignore_sender || ent != sender) {
auto& packet = build_packets.at(static_cast<uint32_t>(ent->ClientVersion()));
if (!packet)
if (auto comp = component(ent); comp != nullptr)
packet = std::invoke(fun, comp, std::forward<Args>(args)...);
if (packet)
ent->QueuePacket(packet.get(), ackreq, Client::CLIENT_CONNECTED);
}
}
};
}
static auto QueueCloseClients(
Mob* sender, bool ignore_sender = false, float distance = 200,
Mob* skipped_mob = nullptr, bool is_ack_required = true,
eqFilterType filter = FilterNone)
{
if (distance <= 0) distance = static_cast<float>(zone->GetClientUpdateRange());
return [=]<typename Fun, typename Obj, typename... Args>(Fun fun, const ComponentGetter<Obj>& component, Args&&... args)
requires std::is_member_function_pointer_v<Fun>
{
if (sender == nullptr) {
QueueClients(sender, ignore_sender, is_ack_required)(fun, component, std::forward<Args>(args)...);
} else {
float distance_squared = distance * distance;
std::array<std::unique_ptr<EQApplicationPacket>, EQ::versions::ClientVersionCount> build_packets;
for (auto& [_, mob] : sender->GetCloseMobList(distance)) {
if (mob && mob->IsClient()) {
Client* client = mob->CastToClient();
if ((!ignore_sender || client != sender)
&& client != skipped_mob
&& DistanceSquared(client->GetPosition(), sender->GetPosition()) < distance_squared
&& client->Connected()
&& client->ShouldGetPacket(sender, filter))
{
auto& packet = build_packets.at(static_cast<uint32_t>(client->ClientVersion()));
if (!packet)
if (auto comp = component(client); comp != nullptr)
packet = std::invoke(fun, comp, std::forward<Args>(args)...);
if (packet)
client->QueuePacket(packet.get(), is_ack_required, Client::CLIENT_CONNECTED);
}
}
}
}
};
}
} // namespace ClientPatch
// Helpers for the Message interface to send message packets
namespace Message {
// this can return nullptr when the component doesn't exist for the version
static std::function GetComponent = [](const Client* c) -> IMessage* {
return GetMessageComponent(c->GetClientVersion()).get();
};
// Helper functions to wrap the packet construction in sends
template <AllConstChar... Args>
requires (sizeof...(Args) <= 9)
void MessageString(Client* c, uint32_t type, uint32_t id, Args&&... args)
{
if constexpr (sizeof...(Args) == 0) {
ClientPatch::QueuePacket(c, &IMessage::Simple, GetComponent(c), type, id);
} else {
std::array<const char*, 9> a = {args...};
ClientPatch::QueuePacket(c, &IMessage::Formatted, GetComponent(c), type, id, a);
}
}
static auto CloseMessageString(
Mob* sender, bool ignore_sender = false, float distance = 200.f,
Mob* skipped_mob = nullptr, bool is_ack_required = true,
eqFilterType filter = FilterNone)
{
return [=]<AllConstChar... Args>(uint32_t type, uint32_t id, Args&&... args)
requires (sizeof...(Args) <= 9)
{
auto queue_close_clients = ClientPatch::QueueCloseClients(sender, ignore_sender, distance, skipped_mob,
is_ack_required, filter);
if constexpr (sizeof...(Args) == 0) {
return queue_close_clients(&IMessage::Simple, GetComponent, type, id);
} else {
std::array<const char*, 9> a = {args...};
return queue_close_clients(&IMessage::Formatted, GetComponent, type, id, a);
}
};
}
inline void InterruptSpell(Client* c, uint32_t message, uint32_t spawn_id, const char* spell_link)
{
ClientPatch::QueuePacket(c, &IMessage::InterruptSpell, GetComponent(c), message, spawn_id, spell_link);
}
inline void InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name,
const char* spell_link)
{
ClientPatch::QueueCloseClients(sender, true, RuleI(Range, SongMessages), nullptr, true,
sender->IsClient() ? FilterPCSpells : FilterNPCSpells)(
&IMessage::InterruptSpellOther, GetComponent, sender, message, spawn_id, name, spell_link);
}
} // namespace Message
+1
View File
@@ -25,6 +25,7 @@ class Client;
class Seperator;
#define COMMAND_CHAR '#'
#define COMMAND_CHAR_NON_HASH '$'
typedef void (*CmdFuncPtr)(Client *, const Seperator *);
-2
View File
@@ -693,8 +693,6 @@ luabind::scope lua_register_packet_opcodes() {
luabind::value("ShopEndConfirm", static_cast<int>(OP_ShopEndConfirm)),
luabind::value("AdventureMerchantRequest", static_cast<int>(OP_AdventureMerchantRequest)),
luabind::value("Sound", static_cast<int>(OP_Sound)),
luabind::value("0x0193", static_cast<int>(OP_0x0193)),
luabind::value("0x0347", static_cast<int>(OP_0x0347)),
luabind::value("WorldComplete", static_cast<int>(OP_WorldComplete)),
luabind::value("MobRename", static_cast<int>(OP_MobRename)),
luabind::value("TaskDescription", static_cast<int>(OP_TaskDescription)),
+8
View File
@@ -1284,6 +1284,14 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
strcpy(ns->spawn.name, name);
if(IsClient()) {
strn0cpy(ns->spawn.lastName, lastname, sizeof(ns->spawn.lastName));
ns->spawn.CharacterGuid.Id = CastToClient()->CharacterID();
ns->spawn.CharacterGuid.WorldId = RuleI(World, Id);
ns->spawn.CharacterGuid.Reserved = 0;
}
else {
ns->spawn.CharacterGuid.Id = 0;
ns->spawn.CharacterGuid.WorldId = 0;
ns->spawn.CharacterGuid.Reserved = 0;
}
ns->spawn.heading = FloatToEQ12(m_Position.w);
+26 -43
View File
@@ -82,6 +82,7 @@
#include "common/strings.h"
#include "zone/bot.h"
#include "zone/client.h"
#include "zone/client_version.h"
#include "zone/fastmath.h"
#include "zone/lua_parser.h"
#include "zone/mob_movement_manager.h"
@@ -94,6 +95,8 @@
#include <algorithm>
#include <cassert>
#include "common/links.h"
extern Zone *zone;
extern volatile bool is_zone_loaded;
extern WorldServer worldserver;
@@ -319,6 +322,10 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
// note that CheckFizzle itself doesn't let NPCs fizzle,
// but this code allows for it.
if (slot < CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
/*
MessageFormat: You miss a note, bringing your song to a close! (TOB: You miss a note, bringing your %1 to a close!)
MessageFormat: Your spell fizzles! (TOB: Your %1 spell fizzles!)
*/
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
uint32 use_mana = ((spells[spell_id].mana) / 4);
@@ -328,29 +335,21 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
Mob::SetMana(GetMana() - use_mana); // We send StopCasting which will update mana
StopCasting();
MessageString(Chat::SpellFailure, fizzle_msg);
char spell_link[Links::MAX_LINK_SIZE];
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id);
if (IsClient())
Message::MessageString(CastToClient(), Chat::SpellFailure, fizzle_msg, spell_link);
/**
* Song Failure message
*/
entity_list.FilteredMessageCloseString(
this,
true,
RuleI(Range, SpellMessages),
Chat::SpellFailure,
(IsClient() ? FilterPCSpells : FilterNPCSpells),
(fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER),
0,
/*
MessageFormat: You miss a note, bringing your song to a close! (if missed note)
MessageFormat: A missed note brings %1's song to a close!
MessageFormat: %1's spell fizzles!
*/
GetName()
);
Message::CloseMessageString(this, true, RuleI(Range, SpellMessages),
nullptr, true, IsClient() ? FilterPCSpells : FilterNPCSpells)(
Chat::SpellFailure, fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER, GetName(), spell_link);
TryTriggerOnCastRequirement();
return(false);
return false;
}
SaveSpellLoc();
@@ -1241,7 +1240,6 @@ void Mob::InterruptSpell(uint16 spellid)
// color not used right now
void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
{
EQApplicationPacket *outapp = nullptr;
uint16 message_other;
bool bard_song_mode = false; //has the bard song gone to auto repeat mode
@@ -1303,14 +1301,9 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
if (IsClient() && message != SONG_ENDS)
{
// the interrupt message
outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct));
InterruptCast_Struct* ic = (InterruptCast_Struct*) outapp->pBuffer;
ic->messageid = message;
ic->spawnid = GetID();
outapp->priority = 5;
CastToClient()->QueuePacket(outapp);
safe_delete(outapp);
char spell_link[Links::MAX_LINK_SIZE];
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spellid);
Message::InterruptSpell(CastToClient(), message, GetID(), spell_link);
SendSpellBarEnable(spellid);
}
@@ -1336,14 +1329,9 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
}
// this is the actual message, it works the same as a formatted message
outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(GetCleanName()) + 1);
InterruptCast_Struct* ic = (InterruptCast_Struct*) outapp->pBuffer;
ic->messageid = message_other;
ic->spawnid = GetID();
strcpy(ic->message, GetCleanName());
entity_list.QueueCloseClients(this, outapp, true, RuleI(Range, SongMessages), 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells);
safe_delete(outapp);
char spell_link[Links::MAX_LINK_SIZE];
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spellid);
Message::InterruptSpellOther(this, message_other, GetID(), GetCleanName(), spell_link);
}
// this is like interrupt, just it doesn't spam interrupt packets to everyone
@@ -7279,16 +7267,11 @@ void Mob::DoBardCastingFromItemClick(bool is_casting_bard_song, uint32 cast_time
Known bug: When a bard uses an augment with a clicky that has a cast time, the cast won't display. This issue only affects bards.
*/
if (is_casting_bard_song) {
//For spells with cast times. Cancel song cast, stop pusling and start item cast.
//For spells with cast times. Cancel song cast, stop pulsing and start item cast.
if (cast_time != 0) {
EQApplicationPacket *outapp = nullptr;
outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct));
InterruptCast_Struct* ic = (InterruptCast_Struct*)outapp->pBuffer;
ic->messageid = SONG_ENDS;
ic->spawnid = GetID();
outapp->priority = 5;
CastToClient()->QueuePacket(outapp);
safe_delete(outapp);
char spell_link[Links::MAX_LINK_SIZE];
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id);
Message::InterruptSpell(CastToClient(), SONG_ENDS, GetID(), spell_link);
ZeroCastingVars();
ZeroBardPulseVars();
+58 -58
View File
@@ -41,8 +41,8 @@
#define DOORS_INSUFFICIENT_SKILL 132 //You are not sufficiently skilled to pick this lock.
#define DOORS_GM 133 //You opened the locked door with your magic GM key.
#define ITEMS_INSUFFICIENT_LEVEL 136 //You are not sufficient level to use this item.
#define GAIN_XP 138 //You gain experience!!
#define GAIN_GROUPXP 139 //You gain party experience!!
#define GAIN_XP 138 //You gain experience!! // TODO: TOB added experience value - You gain experience!%1
#define GAIN_GROUPXP 139 //You gain party experience!! // TODO: TOB added experience value - You gain party experience!%1
#define BOW_DOUBLE_DAMAGE 143 //Your bow shot did double dmg.
#define YOU_ARE_BEING_BANDAGED 147 //Someone is bandaging you.
#define FORAGE_GRUBS 150 //You have scrounged up some fishing grubs.
@@ -69,7 +69,7 @@
#define SPELL_FIZZLE 173 //Your spell fizzles!
#define MUST_EQUIP_ITEM 179 //You cannot use this item unless it is equipped.
#define MISS_NOTE 180 //You miss a note, bringing your song to a close!
#define CANNOT_USE_ITEM 181 //Your race, class, or deity cannot use this item.
#define CANNOT_USE_ITEM 181 //Your race, class, or deity cannot use this item. // TODO: TOB moved this: 6611 Your class, deity, and/or race may not equip %1.
#define ITEM_OUT_OF_CHARGES 182 //Item is out of charges.
#define ALREADY_ON_A_MOUNT 189 //You are already on a mount.
#define TARGET_NO_MANA 191 //Your target has no mana to affect
@@ -106,7 +106,7 @@
#define NO_PET 255 //You do not have a pet.
#define GATE_FAIL 260 //Your gate is too unstable, and collapses.
#define CORPSE_CANT_SENSE 262 //You cannot sense any corpses for this PC in this zone.
#define SPELL_NO_HOLD 263 //Your spell did not take hold.
#define SPELL_NO_HOLD 263 //Your spell did not take hold. // TODO: This one is complex. There are 4 replacement strings, all taking arguments 9164, 9209, 9210, 9211
#define CANNOT_CHARM 267 //This NPC cannot be charmed.
#define SPELL_NO_EFFECT 268 //Your target looks unaffected.
#define NO_INSTRUMENT_SKILL 269 //Stick to singing until you learn to play this instrument.
@@ -123,9 +123,9 @@
#define DISARMED_TRAP 305 //You have disarmed the trap.
#define LDON_SENSE_TRAP1 306 //You do not Sense any traps.
#define TRADESKILL_NOCOMBINE 334 //You cannot combine these items in this container type!
#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together.
#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together. // TODO: TOB - 336 You lacked the skills to fashion %1.
#define TRADESKILL_TRIVIAL 338 //You can no longer advance your skill from making this item.
#define TRADESKILL_SUCCEED 339 //You have fashioned the items together to create something new!
#define TRADESKILL_SUCCEED 339 //You have fashioned the items together to create something new! // TODO: TOB - 339 You have %2fashioned the items together to create something new: %1.
#define EVADE_SUCCESS 343 //You have momentarily ducked away from the main combat.
#define EVADE_FAIL 344 //Your attempts at ducking clear of combat fail.
#define HIDE_FAIL 345 //You failed to hide yourself.
@@ -136,11 +136,11 @@
#define MEND_SUCCESS 350 //You mend your wounds and heal some damage.
#define MEND_WORSEN 351 //You have worsened your wounds!
#define MEND_FAIL 352 //You have failed to mend your wounds.
#define LDON_SENSE_TRAP2 367 //You have not detected any traps.
#define TRAP_TOO_FAR 368 //You are too far away from that trap to affect it.
#define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap.
#define LDON_SENSE_TRAP2 367 //You have not detected any traps. // TODO: TOB - unk
#define TRAP_TOO_FAR 368 //You are too far away from that trap to affect it. // TODO: TOB - unk
#define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap. // TODO: TOB - unk
#define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one.
#define PICK_LORE 379 //You cannot pick up a lore item you already possess.
#define PICK_LORE 379 //You cannot pick up a lore item you already possess. // TODO: TOB - 379 You cannot pick up %1 because it is a lore item you already possess.
#define POISON_TOO_HIGH 382 //This poison is too high level for you to apply.
#define TAUNT_TOO_FAR 386 //You are too far away from your target to taunt.
#define CORPSE_TOO_FAR 389 //The corpse is too far away to summon.
@@ -154,17 +154,17 @@
#define SONG_NEEDS_WIND 406 //You need to play a wind instrument for this song
#define SONG_NEEDS_STRINGS 407 //You need to play a stringed instrument for this song
#define SONG_NEEDS_BRASS 408 //You need to play a brass instrument for this song
#define AA_GAIN_ABILITY 410 //You have gained the ability "%T1" at a cost of %2 ability %T3.
#define AA_IMPROVE 411 //You have improved %T1 %2 at a cost of %3 ability %T4.
#define TAUNT_SUCCESS 412 //You taunt %1 to ignore others and attack you!
#define AA_GAIN_ABILITY 410 //You have gained the ability "%T1" at a cost of %2 ability %T3. // TODO: TOB - You have gained the ability "%B1(1)" at a cost of %2 ability %T3.
#define AA_IMPROVE 411 //You have improved %T1 %2 at a cost of %3 ability %T4. // TODO: TOB - You have improved %B1(1) %2 at a cost of %3 ability %T4.
#define TAUNT_SUCCESS 412 //You taunt %1 to ignore others and attack you! // TODO: TOB - unk
#define AA_REUSE_MSG 413 //You can use the ability %B1(1) again in %2 hour(s) %3 minute(s) %4 seconds.
#define AA_REUSE_MSG2 414 //You can use the ability %B1(1) again in %2 minute(s) %3 seconds.
#define YOU_HEALED 419 //%1 has healed you for %2 points of damage.
#define YOU_HEALED 419 //%1 has healed you for %2 points of damage. // TODO: TOB - 12998 %1 healed you for %2 hit points by %3.
#define BEGINS_TO_GLOW 422 //Your %1 begins to glow.
#define ALREADY_INVIS 423 //%1 tries to cast an invisibility spell on you, but you are already invisible.
#define YOU_ARE_PROTECTED 424 //%1 tries to cast a spell on you, but you are protected.
#define TARGET_RESISTED 425 //Your target resisted the %1 spell.
#define YOU_RESIST 426 //You resist the %1 spell!
#define TARGET_RESISTED 425 //Your target resisted the %1 spell. // TODO: TOB - 425 %1 resisted your %2!
#define YOU_RESIST 426 //You resist the %1 spell! // TODO: TOB - 426 You resist %1!
#define YOU_CRIT_HEAL 427 //You perform an exceptional heal! (%1)
#define YOU_CRIT_BLAST 428 //You deliver a critical blast! (%1)
#define SUMMONING_CORPSE 429 //Summoning your corpse.
@@ -176,15 +176,15 @@
#define PET_TAUNTING 438 //Taunting attacker, Master.
#define INTERRUPT_SPELL 439 //Your spell is interrupted.
#define LOSE_LEVEL 442 //You LOST a level! You are now level %1!
#define GAIN_ABILITY_POINT 446 //You have gained an ability point! You now have %1 ability point%2.
#define GAIN_ABILITY_POINT 446 //You have gained an ability point! You now have %1 ability point%2. // TODO: TOB - 446 You have gained an ability point! You now have %1 ability points. (Actual system moved to 8019-8021 and can handle multiples)
#define GAIN_LEVEL 447 //You have gained a level! Welcome to level %1!
#define LANG_SKILL_IMPROVED 449 //Your language skills have improved.
#define OTHER_LOOTED_MESSAGE 466 //--%1 has looted a %2--
#define LOOTED_MESSAGE 467 //--You have looted a %1--
#define FACTION_WORST 469 //Your faction standing with %1 could not possibly get any worse.
#define FACTION_WORSE 470 //Your faction standing with %1 got worse.
#define FACTION_BEST 471 //Your faction standing with %1 could not possibly get any better.
#define FACTION_BETTER 472 //Your faction standing with %1 got better.
#define OTHER_LOOTED_MESSAGE 466 //--%1 has looted a %2-- // TODO: TOB - 466 --%1 has looted %2 %3 from %4.--
#define LOOTED_MESSAGE 467 //--You have looted a %1-- // TODO: TOB - 467 --You have looted %1 %2 from %3.--
#define FACTION_WORST 469 //Your faction standing with %1 could not possibly get any worse. // TODO: TOB - 469 Your faction standing with %B1(45) could not possibly get any worse.
#define FACTION_WORSE 470 //Your faction standing with %1 got worse. // TODO: TOB - 14261 Your faction standing with %B1(45) has been adjusted by %2.
#define FACTION_BEST 471 //Your faction standing with %1 could not possibly get any better. // TODO: TOB - 471 Your faction standing with %B1(45) could not possibly get any better.
#define FACTION_BETTER 472 //Your faction standing with %1 got better. // TODO: TOB - 14261 Your faction standing with %B1(45) has been adjusted by %2.
#define PET_REPORT_HP 488 //I have %1 percent of my hit points left.
#define PET_NO_TAUNT 489 //No longer taunting attackers, Master.
#define PET_DO_TAUNT 490 //Taunting attackers as normal, Master.
@@ -241,7 +241,7 @@
#define NPC_RAMPAGE 1044 //%1 goes on a RAMPAGE!
#define NPC_FLURRY 1045 //%1 executes a FLURRY of attacks on %2!
#define DISCIPLINE_FEARLESS 1076 //%1 becomes fearless.
#define DUEL_FINISHED 1088 //dont know text
#define DUEL_FINISHED 1088 //%1 has defeated %2 in a duel to the death!
#define EATING_MESSAGE 1091 //Chomp, chomp, chomp... %1 takes a bite from a %2.
#define DRINKING_MESSAGE 1093 //Glug, glug, glug... %1 takes a drink from a %2.
#define SUCCESSFUL_TAUNT 1095 //I'll teach you to interfere with me %3.
@@ -285,8 +285,8 @@
#define MERCHANT_CLOSED_TWO 1200 //Can't you see I'm doing something here?
#define MERCHANT_CLOSED_THREE 1201 //I am not open for business right now.
#define AA_POINTS 1215 //points
#define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles!
#define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close!
#define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles! // TODO: TOB - 1218 %1's %2 spell fizzles!
#define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close! // TODO: TOB - 1219 A missed note brings %1's %2 to a close!
#define SPELL_LEVEL_REQ 1226 //This spell only works on people who are level %1 and under.
#define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire.
#define CORPSE_ITEM_LOST 1228 //Your items will no longer stay with you when you respawn on death. You will now need to return to your corpse for your items.
@@ -327,27 +327,27 @@
#define SENSE_CORPSE_DIRECTION 1563 //You sense a corpse in this direction.
#define DUPE_LORE_MERCHANT 1573 //%1 tells you, 'You already have the lore item, %2, on your person, on your shroud, in the bank, in a real estate, or as an augment in another item. You cannot have more than one of a particular lore item at a time.'
#define QUEUED_TELL 2458 //[queued]
#define QUEUE_TELL_FULL 2459 //[zoing and queue is full]
#define QUEUE_TELL_FULL 2459 //[zoning and queue is full]
#define TRADER_BUSY_TWO 3192 //Sorry, that action cannot be performed while trading.
#define SUSPEND_MINION_UNSUSPEND 3267 //%1 tells you, 'I live again...'
#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
#define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets.
#define ONLY_SUMMONED_PETS 3269 //This effect only works with summoned pets.
#define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first.
#define SHIELD_TARGET_NPC 3278 //You must first target a living Player Character.
#define ALREADY_SHIELDED 3279 //Either you or your target is already being shielded.
#define ALREADY_SHIELDING 3280 //Either you or your target is already shielding another.
#define START_SHIELDING 3281 //%1 begins to use %2 as a living shield!
#define END_SHIELDING 3282 //%1 ceases protecting %2.
#define OVER_AA_CAP 3374 //Warning: You are currently over the earned Advancement point limit of %1. Please spend some of your stored AA points. The NEXT time you zone all of your AA points over %2 will be deleted. You MUST spend the extra points now.
#define OVER_AA_CAP 3374 //Warning: You are currently over the earned Advancement point limit of %1. Please spend some of your stored AA points. The NEXT time you zone all of your AA points over %2 will be deleted. You MUST spend the extra points now. // TODO: TOB - 3374 You are currently over the earned Advancement point limit of %1. You must spend some of your ability points before you can earn more.
#define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1.
#define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory.
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!
#define TASK_UPDATED 3471 //Your task '%1' has been updated.
#define YOU_ASSIGNED_TASK 3472 //You have been assigned the task '%1'.
#define DZ_YOU_BELONG 3500 //You cannot create this expedition since you already belong to another.
#define DZ_REPLAY_YOU 3501 //You cannot create this expedition for another %1d:%2h:%3m since you have recently played here.
#define DZ_PLAYER_COUNT 3503 //You do not meet the player count requirement. You have %1 players. You must have at least %2 and no more than %3.
#define DZ_REPLAY_OTHER 3504 //%1 cannot be added to this expedition for another %2D:%3H:%4M since they have recently played in this area.
#define DZ_REPLAY_YOU 3501 //You cannot create this expedition for another %1d:%2h:%3m since you have recently played here. // TODO: TOB - 3501 You cannot create this expedition for another %1d:%2h:%3m:%4s since you have recently played here.
#define DZ_PLAYER_COUNT 3503 //You do not meet the player count requirement. You have %1 players. You must have at least %2 and no more than %3. // TODO: TOB - 3503 You do not meet the player count requirement. You have %1 players. You must have at least %2.
#define DZ_REPLAY_OTHER 3504 //%1 cannot be added to this expedition for another %2D:%3H:%4M since they have recently played in this area. // TODO: TOB - 3504 %1 cannot be added to this expedition for another %2d:%3h:%4m:%5s since they have recently played in this area.
#define DZ_AVAILABLE 3507 //%1 is now available to you.
#define DZADD_INVITE 3508 //Sending an invitation to: %1.
#define DZ_PREVENT_ENTERING 3510 //A strange magical presence prevents you from entering. It's too dangerous to enter at the moment.
@@ -358,7 +358,7 @@
#define DZ_REMOVED 3516 //%1 has been removed from %2.
#define DZSWAP_INVITE 3517 //Sending an invitation to: %1. They must accept in order to swap party members.
#define DZ_LEADER_OFFLINE 3518 //%1 is not currently online. You can only transfer leadership to an online member of the expedition you are in.
#define DZ_TIMER 3519 //You have %1d:%2h:%3m remaining until you may enter %4.
#define DZ_TIMER 3519 //You have %1d:%2h:%3m remaining until you may enter %4. // TODO: TOB - 3519 You have %1d:%2h:%3m:%4s remaining until you may enter %5.
#define DZ_LEADER_NAME 3520 //%1 has been made the leader for this expedition.
#define DZ_LEADER_YOU 3521 //You have been made the leader of this expedition.
#define DZ_INVITE_ACCEPTED 3522 //%1 has accepted your offer to join your expedition.
@@ -371,8 +371,8 @@
#define DZ_MINUTES_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end.
#define DZ_LEADER 3552 //Expedition Leader: %1
#define DZ_MEMBERS 3553 //Expedition Members: %1
#define DZ_EVENT_TIMER 3561 //%1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3D:%4H:%5M until they can experience it again. They may be added to the expedition later, once %2 has been completed.
#define LOOT_NOT_ALLOWED 3562 //You are not allowed to loot the item: %1.
#define DZ_EVENT_TIMER 3561 //%1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3D:%4H:%5M until they can experience it again. They may be added to the expedition later, once %2 has been completed. // TODO: TOB - 3561 %1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3d:%4h:%5m:%6s until they can experience it again. They may be added to the expedition later, once %2 has been completed.
#define LOOT_NOT_ALLOWED 3562 //You are not allowed to loot the item: %1. // TODO: TOB - 8337 & 8338 (has a reason now)
#define DZ_UNABLE_RETRIEVE_LEADER 3583 //Unable to retrieve dynamic zone leader to check permissions.
#define DZADD_NOT_ALLOWING 3585 //The expedition is not allowing players to be added.
#define DZADD_NOT_ONLINE 3586 //%1 is not currently online. A player needs to be online to be added to a Dynamic Zone
@@ -380,8 +380,8 @@
#define DZADD_ALREADY_PART 3588 //You can not add %1 since they are already part of this zone.
#define DZADD_LEAVE_ZONE 3589 //You can not add %1 since they first need to leave the zone before being allowed back in.
#define DZADD_ALREADY_OTHER 3590 //%1 can not be added to this dynamic zone since they are already assigned to another dynamic zone.
#define DZADD_REPLAY_TIMER 3591 //%1 can not be added to this dynamic zone for another %2D:%3H:%4M since they have recently played this zone.
#define DZADD_EVENT_TIMER 3592 //%1 can not be added to this dynamic zone since they have recently experienced %2. They must wait for another %3D:%4H:%5M, or until event %2 has occurred.
#define DZADD_REPLAY_TIMER 3591 //%1 can not be added to this dynamic zone for another %2D:%3H:%4M since they have recently played this zone. // TODO: TOB - 3591 %1 can not be added to this dynamic zone for another %2d:%3h:%4m:%5s since they have recently played this zone.
#define DZADD_EVENT_TIMER 3592 //%1 can not be added to this dynamic zone since they have recently experienced %2. They must wait for another %3D:%4H:%5M, or until event %2 has occurred. // TODO: TOB - 3592 %1 can not be added to this dynamic zone since they have recently experienced %2. They must wait for another %3d:%4h:%5m:%6s, or until event %2 has occurred.
#define DZADD_PENDING 3593 //%1 currently has an outstanding invitation to join this Dynamic Zone.
#define DZADD_PENDING_OTHER 3594 //%1 currently has an outstanding invitation to join another Dynamic Zone. Players may only have one invitation outstanding.
#define DZSWAP_CANNOT_REMOVE 3595 //%1 can not be removed from this dynamic zone since they are not assigned to it.
@@ -405,9 +405,9 @@
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
#define ALREADY_IN_RAID 5060 //%1 is already in a raid.
#define ALREADY_IN_YOUR_RAID 5077 //%1 is already in your raid.
#define ALREADY_IN_YOUR_RAID 5077 //%1 is already in your raid. // TODO: TOB - 5077 That person is already in your raid.
#define NOT_IN_YOUR_RAID 5082 //%1 is not in your raid.
#define GAIN_RAIDEXP 5085 //You gained raid experience!
#define GAIN_RAIDEXP 5085 //You gained raid experience! // TODO: TOB - 5085 You gained raid experience!%1
#define ALREADY_IN_GRP_RAID 5088 //% 1 rejects your invite because they are in a raid and you are not in theirs, or they are a raid group leader
#define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there.
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
@@ -423,8 +423,8 @@
#define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia!
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
#define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds.
#define FAILED_TAUNT 5811 //You have failed to taunt your target.
#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability.
#define FAILED_TAUNT 5811 //You have failed to taunt your target. // TODO: TOB - 5811 You have failed to taunt %1.
#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability. // TODO: TOB - 5817 %1 avoided your %2!
#define AA_NO_TARGET 5825 //You must first select a target for this ability!
#define YOU_RECEIVE 5941 //You receive %1.
#define NO_TASK_OFFERS 6009 //Sorry %3, I don't have anything for someone with your abilities.
@@ -447,7 +447,7 @@
#define TRANSFORM_COMPLETE 6327 //You have successfully transformed your %1.
#define DETRANSFORM_FAILED 6341 //%1 has no transformation that can be removed.
#define GUILD_PERMISSION_FAILED 6418 //You do not have permission to change access options.
#define PARCEL_DELIVERY_ARRIVED 6465 //You have received a new parcel delivery!
#define PARCEL_DELIVERY_ARRIVED 6465 //You have received a new parcel delivery! // TODO: TOB - 6465 You have received a new parcel delivery %1!
#define PARCEL_DELIVERY 6466 //%1 tells you, 'I will deliver the %2 to %3 as soon as possible!'
#define PARCEL_UNKNOWN_NAME 6467 //%1 tells you, 'Unfortunately, I don't know anyone by the name of %2. Here is your %3 back.''
#define PARCEL_DELIVERED 6471 //%1 hands you the %2 that was sent from %3.
@@ -474,7 +474,7 @@
#define LDON_CERTAIN_TRAP 7557 //You are certain that %1 is trapped.
#define LDON_CERTAIN_NOT_TRAP 7558 //You are certain that %1 is not trapped.
#define LDON_CANT_DETERMINE_TRAP 7559 //You are unable to determine if %1 is trapped.
#define LDON_PICKLOCK_SUCCESS 7560 //You have successfully picked %1!
#define LDON_PICKLOCK_SUCCESS 7560 //You have successfully picked %1! // TODO: TOB - 7560 You have successfully picked %1%2!
#define LDON_PICKLOCK_FAILURE 7561 //You have failed to pick %1.
#define LDON_STILL_LOCKED 7562 //You cannot open %1, it is locked.
#define LDON_BASH_CHEST 7563 //%1 try to %2 %3, but do no damage.
@@ -497,7 +497,7 @@
#define NOT_DELEGATED_MARKER 8794 //You have not been delegated Raid Mark
#define GAIN_GROUP_LEADERSHIP_EXP 8788 //
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining)
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining) // TODO: TOB - 8799 --You sense %1%2 on %3 has %4.--
#define RAID_NO_LONGER_MARKED 8801 //%1 is no longer marked
#define YOU_HAVE_BEEN_GIVEN 8994 //You have been given: %1
#define NO_MORE_TRAPS 9002 //You have already placed your maximum number of traps.
@@ -508,13 +508,13 @@
#define SPELL_OPPOSITE_EFFECT 9032 //Your spell may have had the opposite effect of what you desired.
#define HAS_BEEN_AWAKENED 9037 //%1 has been awakened by %2.
#define YOU_HEAL 9068 //You have healed %1 for %2 points of damage.
#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3.
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage.
#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3. // TODO: TOB - 9072 %1 has taken %2 damage from your %3.%4
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage. // TODO: TOB - 9073 %1 hit %2 for %3 points of %4 damage by %5.%6
#define GLOWS_BLUE 9074 //Your %1 glows blue.
#define GLOWS_RED 9075 //Your %1 glows red.
#define SHAKE_OFF_STUN 9077 //You shake off the stun effect!
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2.
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2. // TODO: TOB - 9082 %1's %2 spell has been reflected by %3.
#define NO_MORE_AURAS 9160 //You do not have sufficient focus to maintain that ability.
#define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master.
#define FD_CAST_ON_NO_BREAK 9174 //The strength of your will allows you to resume feigning death.
@@ -527,17 +527,17 @@
#define NO_CAST_OUT_OF_COMBAT 9191 //You can not cast this spell while out of combat.
#define NO_ABILITY_IN_COMBAT 9192 //You can not use this ability while in combat.
#define NO_ABILITY_OUT_OF_COMBAT 9194 //You can not use this ability while out of combat.
#define GAIN_GROUPXP_BONUS 9298 //You gain party experience (with a bonus)!
#define GAIN_GROUPXP_PENALTY 9301 //You gain party experience (with a penalty)!
#define GAIN_RAIDXP_BONUS 9302 //You gained raid experience (with a bonus)!
#define GAIN_RAIDXP_PENALTY 9303 //You gained raid experience (with a penalty)!
#define GAIN_GROUPXP_BONUS 9298 //You gain party experience (with a bonus)! // TODO: TOB - 9298 You gain party experience (with a bonus)!%1
#define GAIN_GROUPXP_PENALTY 9301 //You gain party experience (with a penalty)! // TODO: TOB - 9301 You gain party experience (with a penalty)!%1
#define GAIN_RAIDXP_BONUS 9302 //You gained raid experience (with a bonus)! // TODO: TOB - 9302 You gained raid experience (with a bonus)!%1
#define GAIN_RAIDXP_PENALTY 9303 //You gained raid experience (with a penalty)! // TODO: TOB - 9303 You gained raid experience (with a penalty)!%1
#define LESSER_SPELL_VERSION 11004 //%1 is a lesser version of %2 and cannot be scribed
#define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE!
#define GROUP_IS_FULL 12000 //You cannot join that group, it is full.
#define FACE_ACCEPTED 12028 //Facial features accepted.
#define TRACKING_BEGIN 12040 //You begin tracking %1.
#define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2.
#define YOU_RECEIVE_AS_SPLIT 12071 //You receive %1 as your split.
#define YOU_RECEIVE_AS_SPLIT 12071 //You receive %1 as your split. // TODO: TOB - 12072 You receive %1 from the corpse%2.
#define ATTACKFAILED 12158 //%1 try to %2 %3, but %4!
#define HIT_STRING 12183 //hit
#define CRUSH_STRING 12191 //crush
@@ -554,7 +554,7 @@
#define TARGET_PLAYER_FOR_GUILD_STATUS 12260
#define TARGET_ALREADY_IN_GROUP 12265 //% 1 is already in another group.
#define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite <name> to invite someone to your group.
#define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself.
#define GROUP_INVITEE_SELF 12270 //You cannot invite yourself.
#define ALREADY_IN_PARTY 12272 //That person is already in your party.
#define TALKING_TO_SELF 12323 //Talking to yourself again?
#define SPLIT_NO_GROUP 12328 //You are not in a group! Keep it all.
@@ -593,7 +593,7 @@
#define RANGED_TOO_CLOSE 12698 //Your target is too close to use a ranged weapon!
#define BACKSTAB_WEAPON 12874 //You need a piercing weapon as your primary weapon in order to backstab
#define DISARMED 12889 //You have been disarmed!
#define DISARM_SUCCESS 12890 //You disarmed %1!
#define DISARM_SUCCESS 12890 //You disarmed %1! // TODO: TOB - 12890 You %2disarmed %1!
#define DISARM_FAILED 12891 //Your attempt to disarm failed.
#define MORE_SKILLED_THAN_I 12931 //%1 tells you, 'You are more skilled than I! What could I possibly teach you?'
#define SURNAME_EXISTS 12939 //You already have a surname. Operation failed.
@@ -602,7 +602,7 @@
#define REPORT_ONCE 12945 //You may only submit a report once per time that you zone. Thank you.
#define NOW_INVISIBLE 12950 //%1 is now Invisible.
#define NOW_VISIBLE 12951 //%1 is now Visible.
#define YOU_TAKE_DOT 12954 //You have taken %1 damage from %2 by %3
#define YOU_TAKE_DOT 12954 //You have taken %1 damage from %2 by %3 // TODO: TOB - 12954 You have taken %1 damage from %2 by %3.%4
#define GUILD_NOT_MEMBER2 12966 //You are not in a guild.
#define HOT_HEAL_SELF 12976 //You have been healed for %1 hit points by your %2.
#define HOT_HEAL_OTHER 12997 //You have healed %1 for %2 hit points with your %3.
@@ -612,7 +612,7 @@
#define TOGGLE_ON 13172 //Asking server to turn ON your incoming tells.
#define TOGGLE_OFF 13173 //Asking server to turn OFF all incoming tells for you.
#define DUEL_INPROGRESS 13251 //You have already accepted a duel with someone else cowardly dog.
#define OTHER_HIT_DOT 13327 //%1 has taken %2 damage from %3 by %4.
#define GAIN_XP_BONUS 14541 //You gain experience (with a bonus)!
#define GAIN_XP_PENALTY 14542 //You gain experience (with a penalty)!
#define OTHER_HIT_DOT 13327 //%1 has taken %2 damage from %3 by %4. // TODO: TOB - 13327 %1 has taken %2 damage from %3 by %4.%5
#define GAIN_XP_BONUS 14541 //You gain experience (with a bonus)! // TODO: TOB - 14541 You gain experience (with a bonus)!%1
#define GAIN_XP_PENALTY 14542 //You gain experience (with a penalty)! // TODO: TOB - 14542 You gain experience (with a penalty)!%1
#define GENERIC_MISS 15041 //%1 missed %2