|
From: <cn...@us...> - 2026-07-11 20:32:56
|
Revision: 1661
http://sourceforge.net/p/seq/svn/1661
Author: cn187
Date: 2026-07-11 20:32:53 +0000 (Sat, 11 Jul 2026)
Log Message:
-----------
Update for July 7 EQLegends patch.
Thanks Xerces.
Modified Paths:
--------------
showeq/branches/showeqlegends/conf/worldopcodes.xml
showeq/branches/showeqlegends/conf/zoneopcodes.xml
showeq/branches/showeqlegends/src/eqstr.cpp
showeq/branches/showeqlegends/src/everquest.h
showeq/branches/showeqlegends/src/group.cpp
showeq/branches/showeqlegends/src/group.h
showeq/branches/showeqlegends/src/guildshell.cpp
showeq/branches/showeqlegends/src/interface.cpp
showeq/branches/showeqlegends/src/messageshell.cpp
showeq/branches/showeqlegends/src/packetcapture.cpp
showeq/branches/showeqlegends/src/packetcommon.h
showeq/branches/showeqlegends/src/packetformat.cpp
showeq/branches/showeqlegends/src/packetformat.h
showeq/branches/showeqlegends/src/packetstream.cpp
showeq/branches/showeqlegends/src/player.cpp
showeq/branches/showeqlegends/src/player.h
showeq/branches/showeqlegends/src/s_everquest.h
showeq/branches/showeqlegends/src/spawn.cpp
showeq/branches/showeqlegends/src/spawn.h
showeq/branches/showeqlegends/src/spawnshell.cpp
showeq/branches/showeqlegends/src/spawnshell.h
showeq/branches/showeqlegends/src/spelllist.cpp
showeq/branches/showeqlegends/src/spellshell.cpp
showeq/branches/showeqlegends/src/spellshell.h
showeq/branches/showeqlegends/src/zonemgr.cpp
showeq/branches/showeqlegends/src/zonemgr.h
Modified: showeq/branches/showeqlegends/conf/worldopcodes.xml
===================================================================
--- showeq/branches/showeqlegends/conf/worldopcodes.xml 2026-07-11 20:22:48 UTC (rev 1660)
+++ showeq/branches/showeqlegends/conf/worldopcodes.xml 2026-07-11 20:32:53 UTC (rev 1661)
@@ -11,68 +11,68 @@
</opcode>
-->
- <opcode id="3ad6" name="OP_GuildList" updated="11/16/16">
+ <opcode id="bc01" name="OP_GuildList" updated="07/09/26">
<comment>old GuildListCode</comment>
<payload dir="server" typename="worldGuildListStruct" sizechecktype="none"/>
</opcode>
- <opcode id="08B3" name="OP_MOTD" updated="12/12/12">
+ <opcode id="f8e0" name="OP_MOTD" updated="07/09/26">
<comment>old MOTDCode Server message of the day</comment>
<payload dir="server" typename="worldMOTDStruct" sizechecktype="none"/>
</opcode>
<!-- Not necessary for SEQ to run but here to name packets in logs -->
- <opcode id="6893" name="OP_SendLoginInfo" updated="11/07/10">
+ <opcode id="7b6a" name="OP_SendLoginInfo" updated="07/09/26">
<comment>Client starting world session by asking for login information</comment>
</opcode>
- <opcode id="3288" name="OP_LogServer" updated="11/07/10">
+ <opcode id="b537" name="OP_LogServer" updated="07/09/26">
<comment></comment>
</opcode>
- <opcode id="115A" name="OP_ApproveWorld" updated="11/07/08">
+ <opcode id="b8cc" name="OP_ApproveWorld" updated="07/09/26">
<comment></comment>
</opcode>
- <opcode id="3288" name="OP_EnterWorld" updated="11/07/10">
+ <opcode id="26bf" name="OP_EnterWorld" updated="07/09/26">
<comment>Server approval for client to enter world</comment>
</opcode>
- <opcode id="1771" name="OP_ExpansionInfo" updated="11/07/10">
+ <opcode id="6bcf" name="OP_ExpansionInfo" updated="07/09/26">
<comment>Which expansions user has</comment>
</opcode>
- <opcode id="6685" name="OP_SendCharInfo" updated="07/18/12">
+ <opcode id="84f6" name="OP_SendCharInfo" updated="07/09/26">
<comment>Send all chars visible @ char select</comment>
</opcode>
- <opcode id="3C2D" name="OP_SendSpellChecksum" updated="11/07/10">
+ <opcode id="2de1" name="OP_SendSpellChecksum" updated="07/09/26">
<comment>Contains a snippet of spell data</comment>
</opcode>
- <opcode id="30E2" name="OP_SendExeChecksum" updated="11/07/10">
+ <opcode id="44d9" name="OP_SendExeChecksum" updated="07/09/26">
<comment>Second client verification packet</comment>
</opcode>
- <opcode id="2B5F" name="OP_SendBaseDataChecksum" updated="11/07/10">
+ <opcode id="289c" name="OP_SendBaseDataChecksum" updated="07/09/26">
<comment>Second client verification packet</comment>
</opcode>
- <opcode id="2874" name="OP_SendSkillCapsChecksum" updated="11/07/10">
+ <opcode id="a958" name="OP_SendSkillCapsChecksum" updated="07/09/26">
<comment>Third client verification packet</comment>
</opcode>
- <opcode id="28d4" name="OP_SetChatServer" updated="06/27/12">
+ <opcode id="bb67" name="OP_SetChatServer" updated="07/09/26">
<comment>Chatserver? IP,Port,servername.Charname,password(?)</comment>
</opcode>
- <opcode id="75f5" name="OP_SetChatServer2" updated="06/27/12">
+ <opcode id="f22b" name="OP_SetChatServer2" updated="07/09/26">
<comment>Chatserver? IP,Port,servername.Charname,password(?)</comment>
</opcode>
- <opcode id="777c" name="OP_ZoneServerInfo" updated="06/27/12">
+ <opcode id="35d4" name="OP_ZoneServerInfo" updated="07/09/26">
<comment>old ZoneServerInfo</comment>
</opcode>
- <opcode id="72F3" name="OP_WorldComplete" updated="11/07/10">
+ <opcode id="fc46" name="OP_WorldComplete" updated="07/09/26">
<comment>Client telling world server it is done. World replies by disconnecting.</comment>
</opcode>
- <opcode id="2730" name="OP_DeleteCharacter" updated="11/07/10">
+ <opcode id="48f9" name="OP_DeleteCharacter" updated="07/09/26">
<comment>Delete character @ char select</comment>
</opcode>
- <opcode id="49B6" name="OP_CharacterCreate" updated="11/07/10">
+ <opcode id="0634" name="OP_CharacterCreate" updated="07/09/26">
<comment>Create character @ char select</comment>
</opcode>
- <opcode id="6E97" name="OP_ApproveName" updated="11/07/10">
+ <opcode id="a24a" name="OP_ApproveName" updated="07/09/26">
<comment>Approving new character name @ char creation</comment>
</opcode>
- <opcode id="0005" name="OP_RandomNameGenerator" updated="11/07/10">
+ <opcode id="ffff" name="OP_RandomNameGenerator" updated="07/09/26">
<comment>Returns a random name</comment>
</opcode>
</seqopcodes>
Modified: showeq/branches/showeqlegends/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-11 20:22:48 UTC (rev 1660)
+++ showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-11 20:32:53 UTC (rev 1661)
@@ -3,112 +3,103 @@
<seqopcodes>
<!-- Critical opcodes used directly by ShowEQ -->
- <opcode id="e284" name="OP_PlayerProfile" updated="05/20/26">
- <comment>CharProfileCode</comment>
+ <opcode id="62f0" name="OP_PlayerProfile" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified 42485B</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="a5bf" name="OP_ZoneEntry" updated="05/20/26">
- <comment>ZoneEntryCode</comment>
+ <opcode id="4606" name="OP_ZoneEntry" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified both dirs, c2s 92B</comment>
<payload dir="client" typename="ClientZoneEntryStruct" sizechecktype="match"/>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="7e22" name="OP_TimeOfDay" updated="05/20/26">
- <comment>TimeOfDayCode</comment>
+ <opcode id="34c7" name="OP_TimeOfDay" updated="07/09/26">
+ <comment>07/07 rotation; 8B, stock layout byte-identical (u16 year 3172 + u16 pad); wire-verified across 6 sessions, advances at EQ game rate with correct day rollover</comment>
<payload dir="server" typename="timeOfDayStruct" sizechecktype="match"/>
</opcode>
- <opcode id="a923" name="OP_NewZone" updated="05/20/26">
- <comment>NewZoneCode</comment>
+ <opcode id="1dbf" name="OP_NewZone" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified 336B</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="33ec" name="OP_SpawnDoor" updated="05/20/26">
- <comment>DoorSpawnsCode</comment>
+ <opcode id="71ca" name="OP_SpawnDoor" updated="07/08/26">
+ <comment>07/07/26 rotation, wire-verified door defs; payload matched to code registration (modulus) to clear connect2 warning</comment>
<payload dir="server" typename="doorStruct" sizechecktype="modulus"/>
</opcode>
- <opcode id="92dc" name="OP_GroundSpawn" updated="05/20/26">
- <comment>MakeDropCode</comment>
+ <opcode id="0def" name="OP_GroundSpawn" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified IT defs</comment>
<payload dir="server" typename="makeDropStruct" sizechecktype="none"/>
- <payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="ad5c" name="OP_SendZonePoints" updated="05/20/26">
- <comment>Coords in a zone that will port you to another zone</comment>
+ <opcode id="069f" name="OP_SendZonePoints" updated="07/09/26">
+ <comment>07/07 rotation; u32 count + count*32B rows (stock 24B zonePoint fields + 8 zero bytes) + 32B ff trailer; wire-verified befallen/felwithea vs known zone lines and neighbor zone ids</comment>
<payload dir="server" typename="zonePointsStruct" sizechecktype="none"/>
</opcode>
- <opcode id="0c97" name="OP_AAExpUpdate" updated="05/20/26">
- <comment>Receiving AA experience. Also when percent to AA changes.</comment>
- <payload dir="server" typename="altExpUpdateStruct" sizechecktype="match"/>
+ <opcode id="42d1" name="OP_AAExpUpdate" updated="07/09/26">
+ <comment>EQL 12B: u32 AA per-point progress permille*1000 (in-game-calibrated against the AA bar), u32 unassigned AA, u32 varying suffix. Was cross-wired to 6801 (which is regular exp)</comment>
+ <payload dir="server" typename="altExpUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_ExpUpdate" updated="07/19/23">
- <comment>ExpUpdateCode</comment>
+ <opcode id="6801" name="OP_ExpUpdate" updated="07/09/26">
+ <comment>EQL 16B: u32 exp permille*1000 (in-game-calibrated against the exp bar), u32 0, u32 unassigned AA, u32 0. Was cross-wired to 42d1 (which is the AA bar)</comment>
<payload dir="server" typename="expUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="9f2b" name="OP_GuildMOTD" updated="05/20/26">
- <comment>GuildMOTD</comment>
+ <opcode id="46df" name="OP_GuildMOTD" updated="07/09/26">
+ <comment>07/07 rotation; stock guildMOTDStruct byte-perfect (target@8, sender@72, message@140; 656B = header + msg buffer); wire-verified vs known MOTD text at zone-in</comment>
<payload dir="server" typename="guildMOTDStruct" sizechecktype="none"/>
</opcode>
- <opcode id="f8d1" name="OP_ClientUpdate" updated="05/20/26">
- <comment>Position updates</comment>
- <payload dir="server" typename="playerSpawnPosStruct" sizechecktype="match"/>
- <payload dir="both" typename="playerSelfPosStruct" sizechecktype="match"/>
+ <opcode id="7171" name="OP_ClientUpdate" updated="07/08/26">
+ <comment>07/08/26 server payload sizecheck none->match to satisfy connect2 (playerPosUpdate was unwired; other PCs stuck at safe point)</comment>
+ <payload dir="client" typename="uint8_t" sizechecktype="none"/>
+ <payload dir="server" typename="playerPosUpdateEQLStruct" sizechecktype="match"/>
</opcode>
- <opcode id="917c" name="OP_NpcMoveUpdate" updated="05/20/26">
- <comment>Position updates</comment>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ <opcode id="67e0" name="OP_MobUpdate" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified 14B</comment>
+ <payload dir="both" typename="spawnPositionUpdate" sizechecktype="none"/>
</opcode>
- <opcode id="4a4f" name="OP_MobUpdate" updated="05/20/26">
- <comment>MobUpdateCode</comment>
- <payload dir="both" typename="spawnPositionUpdate" sizechecktype="match"/>
- </opcode>
- <opcode id="94d4" name="OP_DeleteSpawn" updated="05/20/26">
- <comment>DeleteSpawnCode</comment>
+ <opcode id="59a1" name="OP_DeleteSpawn" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified 4B, death trio</comment>
<payload dir="both" typename="deleteSpawnStruct" sizechecktype="match"/>
</opcode>
- <opcode id="eb88" name="OP_RemoveSpawn" updated="05/20/26">
- <comment>Remove spawn from zone</comment>
- <payload dir="both" typename="removeSpawnStruct" sizechecktype="none"/>
+ <opcode id="71ad" name="OP_RemoveSpawn" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified 5B s2c + 4B c2s</comment>
+ <payload dir="both" typename="removeSpawnStruct" sizechecktype="none"/>
</opcode>
- <opcode id="1eb2" name="OP_Death" updated="05/20/26">
- <comment>old NewCorpseCode</comment>
- <payload dir="server" typename="newCorpseStruct" sizechecktype="match"/>
- </opcode>
- <opcode id="ffff" name="OP_WearChange" updated="07/19/23">
- <comment>SpawnUpdateCode</comment>
+ <opcode id="5c62" name="OP_WearChange" updated="07/09/26">
+ <comment>07/09/26 EQL wire-verified via marked equip/unequip capture: 32B {u32 spawnId, u32 wearSlot(7=pri,8=sec), u32 material, 20B zero}; client-sent for self, server-broadcast for others, preceded by 2575/2a0a value-0x40 trigger. Stock handler is a wear no-op (subcommand 17 HP only) so the 27B match never firing = stock-equivalent; id mapped for log identification</comment>
<payload dir="both" typename="SpawnUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="9826" name="OP_SpawnAppearance" updated="05/20/26">
- <comment>SpawnAppearanceCode</comment>
- <payload dir="both" typename="spawnAppearanceStruct" sizechecktype="match"/>
+ <opcode id="1bdc" name="OP_SpawnAppearance" updated="07/09/26">
+ <comment>EQL u32-widened stock struct, fixed 24B (3748/3748 pcap-library packets); type 6=position (110 sit/100 stand/111 duck), 13=anon, 36=LFG, 22=tick; decoded by SpawnShell::spawnEventEQL</comment>
+ <payload dir="both" typename="spawnEventEQLStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_Stamina" updated="07/19/23">
<comment>Server updating on hunger/thirst</comment>
<payload dir="server" typename="staminaStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_HPUpdate" updated="07/19/23">
- <comment>NpcHpUpdateCode Update HP % of a PC or NPC</comment>
- <payload dir="both" typename="hpNpcUpdateStruct" sizechecktype="match"/>
+ <opcode id="2735" name="OP_HPUpdate" updated="07/09/26">
+ <comment>EQL stat-sync channel, fully decoded: u32 spawnId, u8 flags (bit0 wide-i64/narrow-percent, bit1 HP, bit2 mana, bit3 endurance, bits4-5 reason), per-stat {i64 cur,max} or u8 percent, optional trailing u32; flags 0x31 = 6s keepalive. Decoder SpawnShell::spawnStatEQL feeds spawn HP cur/max and player mana (stock manaChanged); endurance consumed (no stock display); food/water never ride this channel</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="9abe" name="OP_GuildMemberUpdate" updated="05/20/26">
- <comment>Info regarding guild members</comment>
+ <opcode id="51ed" name="OP_GuildMemberUpdate" updated="07/09/26">
+ <comment>07/07 rotation; stock struct + u32 rank inserted @8 (name->12, 88B total unchanged); wire-verified across 6 promote/demote clicks (rank 7..2)</comment>
<payload dir="server" typename="GuildMemberUpdate" sizechecktype="match"/>
</opcode>
- <opcode id="ab5e" name="OP_ClickObject" updated="05/20/26">
- <comment>Items dropped on the ground</comment>
+ <opcode id="04d1" name="OP_ClickObject" updated="07/09/26">
+ <comment>07/07 rotation; stock remDropStruct byte-perfect (u16 dropId matches ground spawn id, u16 spawnId=player); wire-verified drop+pickup</comment>
<payload dir="both" typename="remDropStruct" sizechecktype="match"/>
</opcode>
- <opcode id="049e" name="OP_Action" updated="05/20/26">
- <comment>Spells cast etc</comment>
+ <opcode id="73de" name="OP_Action" updated="07/09/26">
+ <comment>EQL 0x73de = stock actionStruct (64B; spell@4 verified vs known cast 434, type@56=231 spell-action on all 1040 corpus records); 88B alt form = actionAltStruct</comment>
<payload dir="both" typename="actionStruct" sizechecktype="match"/>
<payload dir="both" typename="actionAltStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_Action2" updated="04/17/24">
- <comment>Combat actions i.e. bash, kick etc</comment>
+ <opcode id="1734" name="OP_Action2" updated="07/08/26">
+ <comment>EQL per-hit combat: tgt u16, src u16, dmg@8, spell@20 (ffffffff=melee), type u32@24; wire-verified vs 8 known hits + Envenomed Breath 434</comment>
<payload dir="both" typename="action2Struct" sizechecktype="match"/>
</opcode>
- <opcode id="a1e7" name="OP_Consider" updated="05/20/26">
- <comment>ConsiderCode</comment>
+ <opcode id="4212" name="OP_Consider" updated="07/09/26">
+ <comment>fixed 24B both dirs (98/98 pcap-library packets); response: selfId, targetId, faction, level; struct trimmed 32->24 to restore size check</comment>
<payload dir="both" typename="considerStruct" sizechecktype="match"/>
</opcode>
- <opcode id="1994" name="OP_TargetMouse" updated="05/20/26">
- <comment>Targeting a person - old ClientTargetCode</comment>
+ <opcode id="2867" name="OP_TargetMouse" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified 4B</comment>
<payload dir="both" typename="clientTargetStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_SpawnRename" updated="07/19/23">
@@ -123,13 +114,13 @@
<comment>Server putting players into shroud form</comment>
<payload dir="server" typename="spawnShroudSelf" sizechecktype="none"/>
</opcode>
- <opcode id="9148" name="OP_ZoneChange" updated="05/20/26">
- <comment>old ZoneChangeCode</comment>
- <payload dir="both" typename="zoneChangeStruct" sizechecktype="match"/>
+ <opcode id="76d3" name="OP_ZoneChange" updated="07/09/26">
+ <comment>EQL c2s zone-change request, fixed 484B (32/32 pcap-library packets), carries position but no zone id; server never sends it (zone change = session teardown + new session). Handler only sets m_zoning</comment>
+ <payload dir="both" typename="zoneChangeEQLStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_GroupInvite" updated="02/17/21">
- <payload dir="both" typename="groupInviteStruct" sizechecktype="none"/>
- <comment>You invite someone while ungrouped or get invited by someone ungrouped </comment>
+ <opcode id="6e69" name="OP_GroupInvite" updated="07/09/26">
+ <comment>EQL group invite: fixed 168B (invitee@0, inviter@64); struct padded 152->168 to restore size check; wire-verified /say-marked invite</comment>
+ <payload dir="both" typename="groupInviteStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_GroupInvite2" updated="02/17/21">
<payload dir="both" typename="groupInviteStruct" sizechecktype="none"/>
@@ -139,20 +130,20 @@
<payload dir="both" typename="groupDeclineStruct" sizechecktype="match"/>
<comment>Declining to join a group</comment>
</opcode>
- <opcode id="ffff" name="OP_GroupFollow" updated="02/17/21">
+ <opcode id="1512" name="OP_GroupFollow" updated="07/09/26">
+ <comment>EQL group member-add: invitee name@64 matches stock groupFollowStruct; fixed 168B (8/8 pcap-library packets), struct padded 152->168; stock addGroupMember + groupFollow</comment>
<payload dir="server" typename="groupFollowStruct" sizechecktype="match"/>
- <comment>You join a group or player joins group</comment>
</opcode>
<opcode id="ffff" name="OP_GroupFollow2" updated="02/17/21">
<payload dir="server" typename="groupFollowStruct" sizechecktype="match"/>
<comment>Player joins your group</comment>
</opcode>
- <opcode id="ffff" name="OP_GroupUpdate" updated="06/19/19">
- <comment>Group updates</comment>
- <payload dir="both" typename="uint8_t" sizechecktype="none"/>
+ <opcode id="397d" name="OP_GroupUpdate" updated="07/09/26">
+ <comment>EQL full group roster: u32 groupId, u32 count, leader name@8, then count 44B member records from @18 (name NUL-term); resent at zone-in; count=1=solo; decoded by GroupMgr::groupRosterEQL</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_GroupDisband" updated="02/17/21">
- <comment>You disband from group</comment>
+ <opcode id="09aa" name="OP_GroupDisband" updated="07/09/26">
+ <comment>EQL group member-remove/leave: membername@64 matches stock groupDisbandStruct; fixed 168B (4/4 pcap-library packets), struct padded 152->168; stock removeGroupMember + groupDisband</comment>
<payload dir="server" typename="groupDisbandStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_GroupDisband2" updated="02/17/21">
@@ -163,20 +154,20 @@
<comment>Group leader change</comment>
<payload dir="server" typename="groupLeaderChangeStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_Buff" updated="07/19/23">
- <comment>old BuffDropCode</comment>
- <payload dir="both" typename="buffStruct" sizechecktype="match"/>
+ <opcode id="3ada" name="OP_Buff" updated="07/09/26">
+ <comment>07/07 rotation; stock buffStruct padding-stripped, fixed 24B (264/264 pcap-library packets): u32 buffSlot/spellId/1/changeType(4=apply,1=fade)/0/unused; decoded by SpellShell::buffEQL</comment>
+ <payload dir="both" typename="buffEQLStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_BeginCast" updated="07/19/23">
- <comment>BeginCastCode</comment>
- <payload dir="both" typename="beginCastStruct" sizechecktype="match"/>
+ <opcode id="5591" name="OP_BeginCast" updated="07/09/26">
+ <comment>07/07 rotation; stock beginCastStruct (spellId u16@0, spawnId u16@4=caster); 19B EQL vs 11B stock; wire-verified vs known casts</comment>
+ <payload dir="both" typename="beginCastStruct" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_CastSpell" updated="12/05/23">
- <comment>StartCastCode</comment>
+ <opcode id="10b5" name="OP_CastSpell" updated="07/09/26">
+ <comment>07/07 rotation; stock startCastStruct (slot@0, spellId@4, targetId@18) padded 39->40 to the fixed EQL size (76/76 pcap-library packets)</comment>
<payload dir="both" typename="startCastStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_SwapSpell" updated="07/19/23">
- <comment>TradeSpellBookSlotsCode</comment>
+ <opcode id="0fa0" name="OP_SwapSpell" updated="07/09/26">
+ <comment>EQL 07/07 rotation; stock tradeSpellBookSlotsStruct (slot1 u32@0, slot2 u32@4) 8B exact; wire-verified swapping spellbook slots 6/7</comment>
<payload dir="both" typename="tradeSpellBookSlotsStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_MemorizeSpell" updated="07/19/23">
@@ -183,12 +174,12 @@
<comment>MemSpellCode</comment>
<payload dir="both" typename="memSpellStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_InspectAnswer" updated="07/19/23">
- <comment>InspectDataCode</comment>
- <payload dir="both" typename="inspectDataStruct" sizechecktype="match"/>
+ <opcode id="17af" name="OP_InspectAnswer" updated="07/09/26">
+ <comment>07/07 rotation; stock inspectDataStruct byte-perfect incl. 1956B total size (3/3 captured inspects); size check restored</comment>
+ <payload dir="server" typename="inspectDataStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_Emote" updated="07/19/23">
- <comment>EmoteTextCode</comment>
+ <opcode id="1cde" name="OP_Emote" updated="07/09/26">
+ <comment>07/09/26 EQL: wire-verified via marked capture; 4B header + emote text, 1028B fixed buffer so SZC_None</comment>
<payload dir="both" typename="emoteTextStruct" sizechecktype="none"/>
</opcode>
<opcode id="44a0" name="OP_SimpleMessage" updated="05/20/26">
@@ -195,24 +186,24 @@
<comment>SimpleMessageCode</comment>
<payload dir="server" typename="simpleMessageStruct" sizechecktype="match"/>
</opcode>
- <opcode id="5b40" name="OP_FormattedMessage" updated="05/20/26">
- <comment>FormattedMessageCode i.e. pet dismissed etc</comment>
+ <opcode id="3c0a" name="OP_FormattedMessage" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified caret args</comment>
<payload dir="server" typename="formattedMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="498b" name="OP_CommonMessage" updated="05/20/26">
- <comment>ChannelMessageCode i.e. /tell /ooc /shout etc</comment>
+ <opcode id="55eb" name="OP_CommonMessage" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified /say both dirs</comment>
<payload dir="both" typename="channelMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="7162" name="OP_SpecialMesg" updated="05/20/26">
- <comment>Communicate textual info to client including hail responses etc</comment>
+ <opcode id="22e1" name="OP_SpecialMesg" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified NPC speech</comment>
<payload dir="server" typename="specialMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_RandomReq" updated="07/19/23">
- <comment>RandomReqCode</comment>
- <payload dir="client" typename="randomReqStruct" sizechecktype="match"/>
+ <opcode id="08cb" name="OP_RandomReq" updated="07/09/26">
+ <comment>EQL 07/07 rotation; stock randomReqStruct with 8B AND 9B wire forms observed (trailing byte optional) so no size check; wire-verified /random</comment>
+ <payload dir="client" typename="randomReqStruct" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_RandomReply" updated="07/19/23">
- <comment>RandomCode</comment>
+ <opcode id="6589" name="OP_RandomReply" updated="07/09/26">
+ <comment>EQL 07/07 rotation; stock randomStruct (bottom,top,result,name[64]) 76B exact; wire-verified /random 1 100 = 63</comment>
<payload dir="server" typename="randomStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_ConsentResponse" updated="02/17/21">
@@ -240,8 +231,8 @@
<comment>MoneyOnCorpseCode</comment>
<payload dir="server" typename="moneyOnCorpseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_SkillUpdate" updated="07/19/23">
- <comment>Skill up code</comment>
+ <opcode id="6982" name="OP_SkillUpdate" updated="07/09/26">
+ <comment>07/07 rotation; stock skillIncStruct byte-perfect (12B: skillId u32, value i32, +4); wire-verified Abjuration(4)=150</comment>
<payload dir="server" typename="skillIncStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_LevelUpdate" updated="07/19/23">
@@ -307,10 +298,6 @@
<comment>List of group members - Variable length</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="4058" name="OP_GuildMemberList" updated="05/20/26">
- <comment>List of guild members - Variable length</comment>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
- </opcode>
<opcode id="ffff" name="OP_ManaUpdate" updated="07/19/23">
<comment>Mana Update opcode - 10 bytes</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
@@ -367,8 +354,8 @@
<comment>Fellowship information - 2576 bytes</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="8d86" name="OP_ExpandedGuildInfo" updated="05/20/26">
- <comment>Guild ranks and other misc guild data - Variable length</comment>
+ <opcode id="3aac" name="OP_ExpandedGuildInfo" updated="07/09/26">
+ <comment>07/07 rotation; guild ranks/misc data channel (subtyped: 192B self-status w/ rank title + MOTD, 104/352/4184B rank-and-permission blobs at zone-in); not consumed</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
<opcode id="ffff" name="OP_GuildBank" updated="11/20/15">
@@ -415,8 +402,8 @@
<comment>List of available titles - 1520 bytes</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_Animation" updated="11/28/12">
- <comment>Combat animation, emote animations etc - 4 bytes</comment>
+ <opcode id="1293" name="OP_Animation" updated="07/09/26">
+ <comment>EQL 0x1293 4B combat/emote animation tick (u16 spawnId + u8 + u8); community-corroborated</comment>
<payload dir="both" typename="uint8_t" sizechecktype="none"/>
</opcode>
<opcode id="ffff" name="OP_WhoAllRequest" updated="11/28/12">
@@ -427,8 +414,8 @@
<comment>WhoAllOutputCode - Variable length</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_InspectRequest" updated="11/28/12">
- <comment>InspectRequestCode - 8 bytes</comment>
+ <opcode id="2cc0" name="OP_InspectRequest" updated="07/09/26">
+ <comment>07/09/26 EQL wire-verified via marked inspects: 8B {u32 targetId, u32 selfId}; no stock consumer - identification-only</comment>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
<opcode id="ffff" name="OP_GetGuildMOTD" updated="11/28/12">
@@ -535,12 +522,12 @@
<comment>Listing of all guilds. Can be triggered by /lfg search?</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="d5e8" name="OP_GuildsInZoneList" updated="05/20/26">
- <comment>Listing of guild names present in the current zone. Generally seen on zoning</comment>
+ <opcode id="6070" name="OP_GuildsInZoneList" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified</comment>
<payload dir="server" typename="guildsInZoneListStruct" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_NewGuildInZone" updated="07/19/23">
- <comment>An additional guild name has appeared in the current zone. Seen when a guilded player enters the current zone</comment>
+ <opcode id="4411" name="OP_NewGuildInZone" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified 26B</comment>
<payload dir="server" typename="newGuildInZoneStruct" sizechecktype="none"/>
</opcode>
<opcode id="ffff" name="OP_LFGGetMatchesRequest" updated="11/28/12">
@@ -595,8 +582,8 @@
<comment>Free to play nags and other data - Variable length</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="695f" name="OP_Find" updated="05/20/26">
- <comment>Find window data - variable length, 24 min</comment>
+ <opcode id="56f9" name="OP_Find" updated="07/09/26">
+ <comment>07/07 rotation; Find window NPC/services table (variable ~2KB, pushed at zone-in); name-only, no consumer</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
<opcode id="ffff" name="OP_ConsiderCorpse" updated="11/28/12">
@@ -1007,4 +994,36 @@
<comment></comment>
</opcode>
-->
+ <opcode id="1ede" name="OP_HideCorpse" updated="07/07/26">
+ <comment>07/07/26 rotation, wire-verified isolated press, 4B</comment>
+ <payload dir="client" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="0201" name="OP_SelfPosEQL" updated="07/08/26">
+ <comment>c2s self-state; floats mapY/mapX/Z at 0/4/8; no facing word on wire</comment>
+ <payload dir="client" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="7352" name="OP_NpcMoveUpdate" updated="07/08/26">
+ <comment>07/08/26 relocated live-rate NPC pathing channel (was 2824 pre-patch); EQL bit layout: id16 pad16 spec6, sign+mag 19b x@38 y@57 z@76, h12@95; decoder npcMoveUpdateEQL</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="66cb" name="OP_Death" updated="07/09/26">
+ <comment>EQL 0x66cb = stock newCorpseStruct byte-perfect (corpseid always 0, corpses never spawn); fires once per death; community-corroborated</comment>
+ <payload dir="server" typename="newCorpseStruct" sizechecktype="match"/>
+ </opcode>
+ <opcode id="295d" name="OP_DeathEQL" updated="07/09/26">
+ <comment>EQL 0x295d multiplexed death/combat subcode channel (was named OP_Death until stock OP_Death relocated to 66cb); report-only decoder SpawnShell::deathRecordEQL</comment>
+ <payload dir="both" typename="deathRecordEQLStruct" sizechecktype="none"/>
+ </opcode>
+ <opcode id="31ed" name="OP_GuildMemberList" updated="07/09/26">
+ <comment>07/07 rotation; count-prefixed roster, near-stock entries (LP name, level, banker, classMask, rank, lastOn, tribute block, fullmember, LP note, u16 zoneId + u16 + u16 online); header is 1 byte wider than live</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="77ae" name="OP_BuffList" updated="07/09/26">
+ <comment>07/07 rotation; per-spawn buff list: u32 spawnId, u32, u8 1, u8 count, 5pad, then count 26B records {spellId, 1, i32 remainingTicks, 0, casterName NUL, u32 slot}; loads active buffs at zone-in w/ real remaining durations; decoded by SpellShell::buffListEQL</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="7477" name="OP_LoadoutSwapEQL" updated="07/09/26">
+ <comment>EQL-only: loadout-swap (class-switch) self refresh, ~118KB: u32 spawnId, u8 1, u16 innerLen, ZoneEntry-format self record (new classMask + level), serialized inventory tail. Preceded by c2s 6a67 request/ack + 7820 item burst. Decoder SpawnShell::loadoutSwapEQL -> Player::loadoutSwapEQL (level/class only). Live analog is OP_Shroud; stock handler cannot parse this layout</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
</seqopcodes>
Modified: showeq/branches/showeqlegends/src/eqstr.cpp
===================================================================
--- showeq/branches/showeqlegends/src/eqstr.cpp 2026-07-11 20:22:48 UTC (rev 1660)
+++ showeq/branches/showeqlegends/src/eqstr.cpp 2026-07-11 20:32:53 UTC (rev 1661)
@@ -182,12 +182,18 @@
size_t totalArgsLen = 0;
const char* curArg;
uint32_t curSize = 0;
- while (totalArgsLen < argsLen)
+ while (totalArgsLen + 4 <= argsLen)
{
curArg = arguments + totalArgsLen;
curSize = eqtohuint32((const uint8_t*) curArg);
curArg += 4;
+ // a wire length beyond the remaining payload means the
+ // arguments are not in this length-prefixed layout (EQL
+ // messages) - stop rather than allocate off garbage
+ if (curSize > argsLen - totalArgsLen - 4)
+ break;
+
if (curSize > 0) {
// insert argument into the argument list
argList.push_back(QString::fromUtf8(curArg, curSize));
Modified: showeq/branches/showeqlegends/src/everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/everquest.h 2026-07-11 20:22:48 UTC (rev 1660)
+++ showeq/branches/showeqlegends/src/everquest.h 2026-07-11 20:32:53 UTC (rev 1661)
@@ -694,6 +694,7 @@
/*00004*/ uint16_t gender; // Player Gender - 0 Male, 1 Female
/*00008*/ uint32_t race; // Player race
/*00012*/ uint32_t class_; // Player class
+ uint32_t classMask; // EQ Legends multiclass bitmask (bit N = class N)
/*00016*/ uint8_t unknown00016[44]; // ***Placeholder
/*00056*/ uint8_t level; // Level of player
/*00057*/ uint8_t level1; // Level of player (again?)
@@ -1112,6 +1113,7 @@
/*0000*/ uint32_t guildServerID;
/*0000*/ uint32_t guildstatus; // 0=member, 1=officer, 2=leader, -1=not guilded
/*0000*/ uint8_t class_;
+/*0000*/ uint32_t classMask; // EQ Legends multiclass bitmask (bit N = class N)
/*0000*/ uint8_t state; // stand state
/*0000*/ uint8_t light;
/*0000*/ char lastName[32];
@@ -1121,23 +1123,29 @@
struct
{
- signed animation:10; // current animation
- unsigned pitch:12; // pitch (up/down heading)
- unsigned padding00:10;
+ // 2026-07-07 EQL layout: z, x, y are the low 19 bits of
+ // three consecutive words, world value = field >> 3 (live
+ // 19-bit convention). x/y keep the EQ Legends transposed
+ // naming so setPos receives the same world axes as before.
+ // The high 13 bits of each coord word are presumed deltas
+ // but are unvalidated; heading/animation/pitch positions
+ // in words 3-4 are unmapped (0x6000/0 observed) - all are
+ // zeroed at consumption in spawn.cpp until wire-mapped.
+ signed z:19; // z coord (loc value 1)
+ signed deltaZ:13; // presumed change in z (unvalidated)
- signed deltaZ:13; // change in z
- signed deltaHeading:10; // change in heading
- unsigned padding01:9;
+ signed x:19; // x coord (EQ Legends x/y transposed)
+ signed deltaX:13; // presumed change in x (unvalidated)
- signed z:19; // z coord (3rd loc value)
- unsigned heading:12; // heading
- unsigned padding02:1;
+ signed y:19; // y coord (EQ Legends x/y transposed)
+ signed deltaY:13; // presumed change in y (unvalidated)
- signed deltaX:13; // change in x
- signed y:19; // y coord (2nd loc value)
+ unsigned heading:12; // candidate heading word (unvalidated)
+ unsigned padding00:20;
- signed deltaY:13; // change in y
- signed x:19; // x coord (1st loc value)
+ signed deltaHeading:10; // unmapped
+ signed animation:10; // unmapped
+ unsigned pitch:12; // unmapped
};
int32_t posData[5];
@@ -1390,7 +1398,12 @@
/*0083*/ uint8_t invertstate; // invert state
/*0084*/ uint32_t zonePoint;
/*0088*/ uint8_t unknown068[28]; // ***Placeholder
-/*0116*/ uint8_t unknown096[20]; // ***Placeholder
+/*0116*/ uint8_t unknown096[16]; // ***Placeholder
+ // (was 20: struct summed
+ // to 136 vs its declared
+ // 132; EQL wire rows are
+ // a true 132 - 31 and 51
+ // door tables verified)
/*0132*/
};
@@ -1516,6 +1529,94 @@
};
/*
+** EQ Legends OP_MobUpdate (opcode 0x67e0, 14 bytes, 2026-07-07 wire).
+** Coordinate block starts at payload offset 4 and packs, low bit first:
+** x : bits 0..18 z : bits 19..37 (7-bit gap) y : bits 45..63
+** each signed 19-bit, world value = field >> 3 (live convention).
+** Byte-packed as two u32 words to avoid int64 bitfield alignment; the
+** 7-bit gap places y in the high 19 bits of the second word.
+** Verified against spawn-struct positions on 21/25 wire packets.
+*/
+struct spawnPositionUpdateEQL
+{
+/*0000*/ int16_t spawnId;
+/*0002*/ uint8_t unk1[2];
+/*0004*/ signed x:19; // bits 0..18
+ signed z:19 __attribute__((packed)); // bits 19..37 (crosses word)
+ unsigned u3:7; // bits 38..44 (gap)
+ signed y:19; // bits 45..63
+/*0012*/ unsigned heading:12;
+ signed unused2:4;
+/*0014*/
+};
+
+/*
+** EQ Legends player waypoint position update
+** Wire: one-byte-netop 0x03 family, LE opcode 0x0b03, fixed 24 bytes.
+** posData words use the same bit layout as the spawnStruct union.
+*/
+struct playerPosUpdateEQLStruct
+{
+/*0000*/ uint32_t spawnId;
+/*0004*/ uint32_t flags; // 0x00010842 observed constant
+/*0008*/ uint32_t unknown0008; // zero observed
+/*0012*/ signed z:19, deltaZ:13; // coord block, same 19-bit
+/*0016*/ signed x:19; // >>3 packing as spawnStruct
+ unsigned heading:13; // h2048 heading (0..2047) in x-word high bits
+/*0020*/ signed y:19, deltaY:13; // deltas unvalidated, unused
+/*0024*/ uint32_t tail; // zero observed
+/*0028*/
+};
+
+/*
+** EQ Legends spawn appearance event (OP_SpawnAppearance = 0x1bdc)
+** Stock 8B spawnAppearanceStruct u32-widened; fixed 24 bytes on the
+** wire (3748/3748 captured packets, both directions).
+** Decoded by SpawnShell::spawnEventEQL.
+*/
+struct spawnEventEQLStruct
+{
+/*0000*/ uint32_t spawnId;
+/*0004*/ uint32_t type; // 6=position, 13=anon,
+ // 22=tick, 36=LFG, 41=time
+/*0008*/ uint32_t value;
+/*0012*/ uint32_t params[3]; // zeros observed
+/*0024*/
+};
+
+/*
+** EQ Legends buff change (OP_Buff = 0x3ada)
+** Stock 168B buffStruct padding-stripped; fixed 24 bytes on the wire
+** (264/264 captured packets, both directions).
+** Decoded by SpellShell::buffEQL.
+*/
+struct buffEQLStruct
+{
+/*0000*/ uint32_t buffSlot; // 0-14 buff window; 15 =
+ // bar refresh; >=190 scribe
+/*0004*/ uint32_t spellId;
+/*0008*/ uint32_t unknown0008; // 1 observed
+/*0012*/ uint32_t changeType; // 4=applied, 1=faded
+/*0016*/ uint32_t unknown0016; // 0 observed
+/*0020*/ uint32_t unknown0020; // NOT a duration (verified
+ // non-linear, revG)
+/*0024*/
+};
+
+/*
+** EQ Legends client zone-change request (OP_ZoneChange = 0x76d3)
+** Fixed 484 bytes on the wire (32/32 captured packets). Carries
+** position but no zone id; the server never sends this opcode (a
+** zone change is a session teardown + new session). Layout unmapped;
+** the struct exists to give SZC_Match a size to gate on.
+*/
+struct zoneChangeEQLStruct
+{
+/*0000*/ uint8_t unknown0000[484];
+/*0484*/
+};
+
+/*
** Rename a spawn
** Length: 195 Octets
** OpCode: SpawnRename
@@ -1823,8 +1924,9 @@
{
/*0000*/ char invitee[64]; // Invitee's Name
/*0064*/ char inviter[64]; // Inviter's Name
-/*0128*/ uint8_t unknown0128[24]; // ***Placeholder
-/*0152*/
+/*0128*/ uint8_t unknown0128[40]; // ***Placeholder (EQL wire
+ // is fixed 168B; stock 152)
+/*0168*/
};
/*
@@ -1855,8 +1957,9 @@
/*0064*/ char invitee[64]; // Invitee's Member Name
/*0128*/ uint8_t unknown0128[4]; // ***Placeholder
/*0132*/ uint32_t level; // Invitee's level
-/*0136*/ uint8_t unknown0136[16]; // ***Placeholder (zeros)
-/*0152*/
+/*0136*/ uint8_t unknown0136[32]; // ***Placeholder (EQL wire
+ // is fixed 168B; stock 152)
+/*0168*/
};
/*
@@ -1869,8 +1972,9 @@
{
/*0000*/ char yourname[64]; // Player Name
/*0064*/ char membername[64]; // Invited Member Name
-/*0128*/ uint8_t unknown0128[24]; // ***Placeholder
-/*0152*/
+/*0128*/ uint8_t unknown0128[40]; // ***Placeholder (EQL wire
+ // is fixed 168B; stock 152)
+/*0168*/
};
/*
@@ -1930,7 +2034,8 @@
/*
** Consider Struct
-** Length: 32 Octets
+** Length: 24 Octets (EQL wire; stock was 32 - trailing unknowns
+** trimmed so SZC_Match can gate the fixed 24B Legends payload)
** OpCode: considerCode
*/
@@ -1942,9 +2047,7 @@
/*0012*/ int32_t level; // Level
/*0016*/ int32_t unknown0016; // unknown
/*0020*/ int32_t unknown0020; // unknown (rarity?)
-/*0024*/ int32_t unknown0024; // unknown
-/*0028*/ int32_t unknown0028; // unknown
-/*0032*/
+/*0024*/
};
/*
@@ -1985,6 +2088,26 @@
/*0040*/
};
+/*
+** EQ Legends death/combat record (opcode 0x295d, subcode 0x07, 36 bytes)
+** A combat damage event; the fatal hit is not flagged - spawn removal
+** at death is carried by OP_RemoveSpawn/OP_DeleteSpawn, so consumers
+** must treat this as informational only.
+*/
+struct deathRecordEQLStruct
+{
+/*0000*/ uint16_t subcode; // 0x07 for the combat record
+/*0002*/ uint16_t unknown0002; // always 1 observed
+/*0004*/ uint32_t sourceId; // attacker or spell/skill source
+/*0008*/ uint32_t skillOrDamage; // small values (0, 8, 0xc) observed
+/*0012*/ uint32_t targetId; // spawn id of the victim
+/*0016*/ uint32_t unknown0016; // always 1 observed
+/*0020*/ uint32_t combatSeq; // global monotonic combat counter
+/*0024*/ uint32_t unknown0024; // 0, 3, 4 observed
+/*0028*/ uint8_t unknown0028[8]; // zero observed
+/*0036*/
+};
+
/**
** Environmental damage (lava, falls)
** Length: 46 Octets
@@ -2042,12 +2165,14 @@
/*0000*/ uint16_t target; // Target ID
/*0002*/ uint16_t source; // Source ID
/*0004*/ uint8_t unknown0004[4];
-/*0008*/ int32_t damage;
+/*0008*/ int32_t damage; // 0 = miss (EQL wire)
/*0012*/ int8_t unknown0012[8];
-/*0020*/ int32_t spell; // SpellID
-/*0024*/ uint8_t uknown0024[16];
-/*0040*/ uint8_t type; // Bash, kick, cast, etc.
-/*0041*/ uint8_t unknown0042[7];
+/*0020*/ int32_t spell; // SpellID, -1 = melee
+/*0024*/ uint32_t type; // Bash, kick, cast, etc.
+ // EQL 0x1734 carries this
+ // as u32 here; stock had
+ // u8 at offset 0040
+/*0028*/ uint8_t unknown0028[20];
/*0048*/
};
@@ -2107,8 +2232,10 @@
/*0018*/ uint32_t targetId; // The current selected target
/*0022*/ uint8_t unknown0022[2]; // ***Placeholder
/*0024*/ uint8_t unknown0024[2]; // ***Placeholder
-/*0026*/ uint8_t unknown0026[13]; // ***Placeholder (4/7/2009)
-/*0039*/
+/*0026*/ uint8_t unknown0026[14]; // ***Placeholder (EQL wire
+ // is fixed 40B, 76/76
+ // packets; stock was 39)
+/*0040*/
};
/*
@@ -2250,10 +2377,18 @@
struct expUpdateStruct
{
-/*0000*/ uint32_t exp; // experience value x/330
-/*0004*/ uint32_t unknown0004; // unknown
-/*0008*/ uint32_t type; // 0=set, 2=update
-/*0012*/ uint32_t unknown0012; // unknown
+/*0000*/ uint32_t exp; // EQ Legends: level progress
+ // on a 0-100000 scale
+ // (hundredths of a percent);
+ // live was x/330. NOTE: on
+ // Legends this rides opcode
+ // 6801 (in-game-calibrated
+ // against the exp bar; the
+ // ids were cross-wired)
+/*0004*/ uint32_t unknown0004; // 0 observed
+/*0008*/ uint32_t aaUnspent; // unassigned AA points
+ // (in-game-verified)
+/*0012*/ uint32_t unknown0012; // 0 observed
/*0016*/
};
@@ -2264,10 +2399,18 @@
*/
struct altExpUpdateStruct
{
-/*0000*/ uint32_t altexp; // alt exp x/330
-/*0004*/ uint32_t aapoints; // current number of AA points
-/*0008*/ uint8_t percent; // percentage in integer form
-/*0009*/ uint8_t unknown0009[3]; // ***Place Holder
+/*0000*/ uint32_t altexp; // EQ Legends: AA per-point
+ // progress on a 0-100000
+ // scale (live was x/330).
+ // NOTE: on Legends this
+ // rides opcode 42d1
+ // (in-game-calibrated
+ // against the AA bar; the
+ // ids were cross-wired)
+/*0004*/ uint32_t aapoints; // unassigned AA points
+ // (in-game-verified)
+/*0008*/ uint32_t unknown0008; // varying tail (suffix u32
+ // family)
/*0012*/
};
@@ -2427,32 +2570,22 @@
*/
struct playerSelfPosStruct
{
-/*0000*/ uint16_t unknown0000; // ***Placeholder (update time counter?)
-/*0002*/ uint16_t spawnId; // Player's spawn id
-/*0004*/ uint16_t unknown0004; // ***Placeholder
-/*0006*/
- float y; // y coord (2nd loc value)
-/*0010*/
- unsigned pitch:12; // pitch (up/down heading)
- unsigned heading:12; // heading
- unsigned padding01:8;
-/*0014*/
- float deltaY; // change in y
-/*0018*/
- signed animation:10; // current animation
- unsigned padding03:22;
-/*0022*/
- float z; // z coord (3rd loc value)
-/*0026*/
- float x; // x coord (1st loc value)
-/*0030*/
- float deltaX; // change in x
-/*0034*/
- signed deltaHeading:10; // change in heading
- unsigned padding07:22;
-/*0038*/
- float deltaZ; // change in z
-/*0042*/
+/*0000*/ float x; // x coord (Legends float at offset 0)
+/*0004*/ float y; // y coord
+/*0008*/ float z; // z coord
+/*0012*/ uint8_t posFlag; // 0x02 moving marker
+/*0013*/ uint8_t unknown0013;
+/*0014*/ uint8_t unknown0014;
+/*0015*/ uint8_t seq; // increments each update
+/*0016*/ uint16_t headingRaw; // heading region
+ uint16_t spawnId;
+ float deltaX;
+ float deltaY;
+ float deltaZ;
+ unsigned heading:12;
+ signed deltaHeading:10;
+ unsigned pitch:12;
+ signed animation:10;
};
/*
@@ -2658,11 +2791,15 @@
{
/*000*/ uint32_t guildId; // guild id
/*004*/ uint8_t unknown004[4]; // 4 bytes added 11/16/16
-/*008*/ char name[64]; // member name
-/*072*/ uint16_t zoneId; // zone id
-/*074*/ uint16_t zoneInstance; // zone instance
-/*076*/ uint32_t lastOn; // time the player was last on.
-/*080*/ uint8_t unknown080[8]; // 4 bytes added 11/28/12, +4 added 11/16/16
+/*008*/ uint32_t rank; // EQ Legends: member rank
+ // (inserted here; watched
+ // count down 7..2 across
+ // promote/demote clicks)
+/*012*/ char name[64]; // member name
+/*076*/ uint16_t zoneId; // zone id
+/*078*/ uint16_t zoneInstance; // zone instance
+/*080*/ uint32_t lastOn; // time the player was last on.
+/*084*/ uint8_t unknown084[4]; // EQL tail (uninit server mem)
/*088*/
};
Modified: showeq/branches/showeqlegends/src/group.cpp
===================================================================
--- showeq/branches/showeqlegends/src/group.cpp 2026-07-11 20:22:48 UTC (rev 1660)
+++ showeq/branches/showeqlegends/src/group.cpp 2026-07-11 20:32:53 UTC (rev 1661)
@@ -67,19 +67,15 @@
void GroupMgr::player(const charProfileStruct* player)
{
- // We receive a groupUpdate packet after playerProfile so we'll clear the
- // member list then repopulate it in case we lost someone during zoning
- m_memberCount = 0;
- m_membersInZoneCount = 0;
+ // EQ Legends does NOT resend the group roster on zone-in (verified
+ // 2026-07-09: zoned mid-group, no group packets followed the
+ // profile), so we must NOT clear here - the group persists across
+ // zones and is only changed by the per-member add/remove opcodes
+ // (0x1512 / 0x09aa). Clearing here (stock behavior, which relied on
+ // a post-profile groupUpdate to repopulate) would wipe the group on
+ // every zone with nothing to refill it.
+ (void)player;
- for(int i = 0; i < MAX_GROUP_MEMBERS; i++)
- {
- m_members[i]->m_name = "";
- m_members[i]->m_spawn = 0;
- }
-
- emit cleared();
-
// 9/3/2008 - Not used. Group data is no longer sent in charProfile. We still
// need to reset the data as done above.
#if 0
@@ -245,6 +241,71 @@
}
}
+void GroupMgr::groupRosterEQL(const uint8_t* data, size_t len)
+{
+ // EQ Legends full group roster (OP_GroupUpdate = 0x397d), resent at
+ // zone-in - the group analog of the buff list (0x77ae): it loads the
+ // members you already have when ShowEQ zones in or starts up mid-
+ // group, which the incremental 0x1512/0x09aa updates cannot. The
+ // stock groupUpdate parser cannot read this format (verified), so it
+ // needs a dedicated decoder. Header: u32 groupId | u32 count |
+ // leader name @8; then count 44-byte member records from offset 18
+ // (name NUL-terminated, possibly a leading NUL from the prior
+ // record's trailing index). count == 1 is the solo/not-grouped
+ // state. Authoritative: clear and repopulate.
+ if (len < 12)
+ return;
+
+ uint32_t count = *(const uint32_t*)(data + 4);
+
+ m_memberCount = 0;
+ m_membersInZoneCount = 0;
+ for (int i = 0; i < MAX_GROUP_MEMBERS; i++)
+ {
+ m_members[i]->m_name = "";
+ m_members[i]->m_spawn = 0;
+ }
+ emit cleared();
+
+ if (count < 2) // solo = not in a group
+ return;
+
+ for (uint32_t r = 0; r < count && r < MAX_GROUP_MEMBERS; r++)
+ {
+ size_t off = 18 + r * 44;
+ if (off >= len)
+ break;
+ while (off < len && data[off] == 0)
+ off++;
+ size_t s = off;
+ while (off < len && data[off])
+ off++;
+ if (off == s)
+ continue;
+ QString name = QString::fromLatin1((const char*)(data + s), int(off - s));
+
+ bool dup = false;
+ for (int j = 0; j < MAX_GROUP_MEMBERS; j++)
+ if (m_members[j]->m_name == name) { dup = true; break; }
+ if (dup)
+ continue;
+
+ for (int j = 0; j < MAX_GROUP_MEMBERS; j++)
+ {
+ if (m_members[j]->m_name.isEmpty())
+ {
+ m_members[j]->m_name = name;
+ m_memberCount++;
+ m_members[j]->m_spawn = m_spawnShell->findSpawnByName(name);
+ if (m_members[j]->m_spawn)
+ m_membersInZoneCount++;
+ emit added(name, m_members[j]->m_spawn);
+ break;
+ }
+ }
+ }
+}
+
void GroupMgr::addItem(const Item* item)
{
// only care about Spawn class
Modified: showeq/branches/showeqlegends/src/group.h
===================================================================
--- showeq/branches/showeqlegends/src/group.h 2026-07-11 20:22:48 UTC (rev 1660)
+++ showeq/branches/showeqlegends/src/group.h 2026-07-11 20:32:53 UTC (rev 1661)
@@ -64,6 +64,7 @@
void groupUpdate(const uint8_t* data, size_t size);
void addGroupMember(const uint8_t* data);
void removeGroupMember(const uint8_t* data);
+ void groupRosterEQL(const uint8_t* data, size_t len);
void addItem(const Item* item);
void delItem(const Item* item);
void killSpawn(const Item* item);
Modified: showeq/branches/showeqlegends/src/guildshell.cpp
===================================================================
--- showeq/branches/showeqlegends/src/guildshell.cpp 2026-07-11 20:22:48 UTC (rev 1660)
+++ showeq/branches/showeqlegends/src/guildshell.cpp 2026-07-11 20:32:53 UTC (rev 1661)
@@ -70,8 +70,12 @@
}
m_banker = m_banker % 2;
- // 4 byte class
- m_class = uint8_t(netStream.readUInt32NC());
+ // 4 byte class - EQ Legends sends the multiclass bitmask here
+ // (bit N = class N); display the lowest set bit as the class
+ uint32_t classMask = netStream.readUInt32NC();
+ m_class = 0;
+ for (int cb = 0; cb < 32; cb++)
+ if (classMask & (1 << cb)) { m_class = cb; break; }
// 4 byte rank (0 = member, 1 = officer, 2 = leader)
m_guildRank = netStream.readUInt32NC();
@@ -97,12 +101,12 @@
// public note
m_publicNote = netStream.readLPText();
- // 2 byte zoneInstance and zoneId for current zone
+ // EQ Legends tail (live skipped these 6 bytes unread): u16 zoneId,
+ // u16 unknown, u16 online flag. The wire is little-endian, so read
+ // NC (readUInt16 byte-swaps and turned zone 21 into 0x1500=5376).
m_zoneInstance = 0;
- m_zoneId = 0;
-
- // Unknown
- netStream.skipBytes(6);
+ m_zoneId = netStream.readUInt16NC();
+ netStream.skipBytes(4);
}
GuildMember::~GuildMember()
@@ -111,9 +115,15 @@
void GuildMember::update(const GuildMemberUpdate* gmu)
{
- m_zoneId = gmu->zoneId;
- m_zoneInstance = gmu->zoneInstance;
- m_lastOn = gmu->lastOn;
+ // EQ Legends: the 0x51ed member-update reliably carries only
+ // guildId, name, and rank - the zone/instance/lastOn tail is
+ // uninitialized server memory (0x7fff.. pointer bytes observed).
+ // A member ...
[truncated message content] |