|
From: <xer...@us...> - 2026-07-18 01:43:57
|
Revision: 1673
http://sourceforge.net/p/seq/svn/1673
Author: xerxes00
Date: 2026-07-18 01:43:54 +0000 (Sat, 18 Jul 2026)
Log Message:
-----------
live-fixes: logout/exp opcode wiring, modern exp+AA display, parser/map/payload fixes
- map + wire OP_Logout (camp-accept handshake) and OP_ExpUpdate; add a
uint16_t payload primitive so 2-byte opcodes can strict size-match
- player: drop the legacy calc_exp bar reconstruction (2001/2005) that
renders as garbage on current EQ; show Exp and AA only from their wire
updates, blank until the first arrives (no zone-in flash)
- eqstr: stop parsing length-prefixed args past the payload end
- mapcore: keep comma-bearing map location labels intact
- packetstream: clear the payload candidate on no-match so the warning fires
- util: add Berserker to the class string
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
showeq/branches/xerxes-trunk-dev/src/eqstr.cpp
showeq/branches/xerxes-trunk-dev/src/interface.cpp
showeq/branches/xerxes-trunk-dev/src/mapcore.cpp
showeq/branches/xerxes-trunk-dev/src/packetinfo.cpp
showeq/branches/xerxes-trunk-dev/src/packetstream.cpp
showeq/branches/xerxes-trunk-dev/src/player.cpp
showeq/branches/xerxes-trunk-dev/src/player.h
showeq/branches/xerxes-trunk-dev/src/util.cpp
Modified: showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-18 01:43:54 UTC (rev 1673)
@@ -37,8 +37,8 @@
<comment>Receiving AA experience. Also when percent to AA changes.</comment>
<payload dir="server" typename="altExpUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_ExpUpdate" updated="07/19/23">
- <comment>ExpUpdateCode</comment>
+ <opcode id="10cc" name="OP_ExpUpdate" updated="07/15/26">
+ <comment>ExpUpdateCode - exp permille@0 (0..100000), type@8 0=set/2=update</comment>
<payload dir="server" typename="expUpdateStruct" sizechecktype="match"/>
</opcode>
<opcode id="3425" name="OP_GuildMOTD" updated="07/15/26">
@@ -295,9 +295,10 @@
<comment>Movement history for speed/movement hack detection</comment>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_Logout" updated="12/12/12">
- <comment></comment>
- <payload dir="server" typename="uint8_t" sizechecktype="match"/>
+ <opcode id="deb4" name="OP_Logout" updated="07/15/26">
+ <comment>Camp accept - s2c 2B {00 01}; c2s 0B completion</comment>
+ <payload dir="server" typename="uint16_t" sizechecktype="match"/>
+ <payload dir="client" typename="none" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_UIUpdate" updated="05/21/14">
<comment>Seems to be sent to handle a variety of UI updates - Variable length</comment>
Modified: showeq/branches/xerxes-trunk-dev/src/eqstr.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/eqstr.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/eqstr.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -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 - 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/xerxes-trunk-dev/src/interface.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -746,7 +746,7 @@
"moneyOnCorpseStruct", SZC_Match,
m_messageShell, SLOT(moneyOnCorpse(const uint8_t*)));
m_packet->connect2("OP_Logout", SP_Zone, DIR_Server,
- "none", SZC_Match,
+ "uint16_t", SZC_Match,
m_messageShell, SLOT(logOut(const uint8_t*, size_t, uint8_t)));
m_packet->connect2("OP_NewZone", SP_Zone, DIR_Server,
"uint8_t", SZC_None,
Modified: showeq/branches/xerxes-trunk-dev/src/mapcore.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/mapcore.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/mapcore.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -1127,9 +1127,9 @@
filelines, count, (const char*)*lit);
#endif
- if (count != 8)
+ if (count < 8)
{
- seqWarn("Error reading L line %d on map '%s'! %d is an incorrect field count!",
+ seqWarn("Error reading P line %d on map '%s'! %d is an incorrect field count!",
filelines, fileName.toLatin1().data(), count);
continue;
}
@@ -1142,8 +1142,14 @@
g = (*fit++).toUShort();
b = (*fit++).toUShort();
fit++; // skip unknown
- name = (*fit); // Location name, conver
-
+ // Location name is the remaining field(s). Labels can contain the field
+ // separator, e.g. "Locked_Door_(Shiny_Metal_Key,Picklock_40)", which the
+ // comma split breaks into extra fields (count > 8), so rejoin from here
+ // to the end instead of taking a single field.
+ name.clear();
+ for (; fit != fields.end(); ++fit)
+ name += (name.isEmpty() ? QString() : QString(",")) + *fit;
+
// convert underscores to spaces.
name.replace("_", " ");
Modified: showeq/branches/xerxes-trunk-dev/src/packetinfo.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/packetinfo.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/packetinfo.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -98,6 +98,7 @@
// these we add manually to handle strings and octet streams
addStruct("char", sizeof(char));
addStruct("uint8_t", sizeof(uint8_t));
+ addStruct("uint16_t", sizeof(uint16_t));
addStruct("none", 0);
addStruct("unknown", 0);
}
Modified: showeq/branches/xerxes-trunk-dev/src/packetstream.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/packetstream.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/packetstream.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -126,9 +126,12 @@
return false;
}
- EQPacketPayload* payload;
+ EQPacketPayload* payload = nullptr;
- // try to find a matching payload for this opcode
+ // try to find a matching payload for this opcode. The candidate must be
+ // cleared when it does not match: leaving the loop variable set silently
+ // binds the receiver to the LAST payload of the opcode when no typename
+ // matches, bypassing the no-matching-payload warning below.
EQPayloadListIterator pit(*opcode);
while (pit.hasNext())
{
@@ -136,10 +139,11 @@
if (!payload)
break;
// if all the parameters match, then use this payload
- if ((payload->dir() & m_dir) &&
- (payload->typeName() == payloadType) &&
+ if ((payload->dir() & m_dir) &&
+ (payload->typeName() == payloadType) &&
(payload->sizeCheckType() == szt))
break;
+ payload = nullptr;
}
// if no payload found, create one and issue a warning
Modified: showeq/branches/xerxes-trunk-dev/src/player.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/player.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/player.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -192,6 +192,8 @@
m_playerLanguages[a] = 255; // indicate an invalid value
m_mana = 0;
+ m_expBarProgress = -1;
+ m_validAltExp = false;
setLevel (1);
setRace (1);
setClassVal (1);
@@ -352,6 +354,9 @@
void Player::player(const charProfileStruct* player)
{
+ // new zone-in: let the first real exp/AA opcode print, then dedup the
+ // 2-3 identical repeats the server sends right after
+ m_lastExpRaw = m_lastAltExpRaw = 0xffffffff;
QString messag;
if (m_name != player->name)
@@ -410,13 +415,26 @@
// Exp
m_currentExp = player->exp;
- m_currentAltExp = player->expAA;
+ // The profile's expAA is on a different scale than the display and reads
+ // as garbage; the real value comes from OP_AAExpUpdate (updateAltExp).
+ // Don't let the profile clobber a wire value we've already received.
+ if (!m_validAltExp)
+ m_currentAltExp = player->expAA;
m_validExp = true;
-
- emit expChangedInt (m_currentExp, m_minExp, m_maxExp);
- emit expAltChangedInt(m_currentAltExp, 0, 15000000);
- emit setAltExp(m_currentAltExp, 15000000, 15000000/330, m_currentAApts);
+ // Show the exp bar only from the wire's exact permille. The legacy
+ // m_currentExp/m_minExp/m_maxExp reconstruction (calc_exp, from 2001/2005)
+ // predates the modern permille wire format and renders as garbage on
+ // current EQ, so suppress it until the first OP_ExpUpdate set arrives
+ // (a few seconds after zone-in) rather than flashing a bad value.
+ if (m_expBarProgress >= 0)
+ emit expChangedInt (int(m_expBarProgress), 0, 100000);
+ // Same for AA: suppress until the first OP_AAExpUpdate set (see updateAltExp).
+ if (m_validAltExp)
+ {
+ emit expAltChangedInt(m_currentAltExp, 0, 15000000);
+ emit setAltExp(m_currentAltExp, 15000000, 15000000/330, m_currentAApts);
+ }
if (showeq_params->savePlayerState)
savePlayerState();
@@ -611,6 +629,10 @@
void Player::updateAltExp(const uint8_t* data)
{
const altExpUpdateStruct* altexp = (const altExpUpdateStruct*)data;
+ // server re-sends the same AA update at zone-in; skip no-change repeats
+ if (altexp->altexp == m_lastAltExpRaw)
+ return;
+ m_lastAltExpRaw = altexp->altexp;
/* purple: I got no idea what is up here. This seems to be written like
* the packet from the server gives the entire exp bump and not
@@ -624,7 +646,11 @@
else
expIncrement = 0;
*/
- uint32_t realExp = altexp->altexp * (15000000 / 330);
+ // This EQ era sends altexp as percent x 1000 (0-100000); convert to the
+ // 0-330 tick scale the emitted totals expect. (Stock's raw altexp *
+ // (15000000/330) assumed 0-330 and produced multi-billion garbage.)
+ uint32_t altTicks = uint32_t(uint64_t(altexp->altexp) * 330 / 100000);
+ uint32_t realExp = altTicks * (15000000 / 330);
uint32_t expIncrement;
if (realExp > m_currentExp)
@@ -638,10 +664,11 @@
m_currentAApts = altexp->aapoints;
m_currentAltExp = realExp;
+ m_validAltExp = true;
emit expAltChangedInt(m_currentAltExp, 0, 15000000);
- emit newAltExp(expIncrement, m_currentAltExp, altexp->altexp,
+ emit newAltExp(expIncrement, m_currentAltExp, altTicks,
15000000, 15000000/330, m_currentAApts);
if (showeq_params->savePlayerState)
@@ -651,6 +678,10 @@
void Player::updateExp(const uint8_t* data)
{
const expUpdateStruct* exp = (const expUpdateStruct*)data;
+ // server re-sends the same exp update at zone-in; skip no-change repeats
+ if (exp->exp == m_lastExpRaw)
+ return;
+ m_lastExpRaw = exp->exp;
// if this is just setting the percentage, then do nothing (use info from
// player packet).
@@ -665,7 +696,10 @@
}
#endif
- uint32_t realExp = (m_tickExp * exp->exp) + m_minExp;
+ // This EQ era sends exp as level progress in percent x 1000 (0-100000);
+ // convert to the 0-330 tick scale the math below expects.
+ uint32_t expTicks = uint32_t(uint64_t(exp->exp) * 330 / 100000);
+ uint32_t realExp = (m_tickExp * expTicks) + m_minExp;
uint32_t expIncrement;
// if realExperience is greater then current expereince, calculate the
@@ -680,11 +714,16 @@
m_validExp = true;
// signal the new experience
- emit newExp(expIncrement, realExp, exp->exp,
+ emit newExp(expIncrement, realExp, expTicks,
m_minExp, m_maxExp, m_tickExp);
-
- emit expChangedInt (realExp, m_minExp, m_maxExp);
+ // Display the wire's exact bar progress (percent*1000, 0-100000). The
+ // realExp reconstruction above feeds the console prints only; the permille
+ // is authoritative and avoids a calc_exp reconstruction that can invert
+ // (m_maxExp below m_minExp) on some level curves.
+ m_expBarProgress = exp->exp;
+ emit expChangedInt (int(exp->exp), 0, 100000);
+
if(m_freshKill)
{
emit expGained( m_lastSpawnKilledName,
@@ -746,7 +785,9 @@
expIncrement,
m_zoneMgr->longZoneName());
- emit expChangedInt( m_currentExp, m_minExp, m_maxExp);
+ // permille only; suppress the legacy calc_exp fallback (see player())
+ if (m_expBarProgress >= 0)
+ emit expChangedInt (int(m_expBarProgress), 0, 100000);
// update the con table
fillConTable();
@@ -1015,10 +1056,29 @@
valid = m_validStam;
break;
case LIST_EXP:
- curValue = m_currentExp;
- maxValue = m_maxExp;
- valid = m_validExp;
+ // Only the wire permille is trustworthy on modern EQ; the legacy
+ // m_currentExp/m_maxExp values render as garbage (see player()), so mark
+ // the row invalid until the first OP_ExpUpdate set populates the permille.
+ if (m_expBarProgress >= 0)
+ {
+ curValue = uint32_t(m_expBarProgress);
+ maxValue = 100000;
+ valid = m_validExp;
+ }
+ else
+ {
+ curValue = 0;
+ maxValue = 100000;
+ valid = false;
+ }
break;
+ case LIST_ALTEXP:
+ // Only trust the wire value from OP_AAExpUpdate; the profile's expAA reads
+ // as garbage, so mark the row invalid until the first update arrives.
+ curValue = m_currentAltExp;
+ maxValue = 15000000;
+ valid = m_validAltExp;
+ break;
case LIST_FOOD:
curValue = m_food;
maxValue = 127;
Modified: showeq/branches/xerxes-trunk-dev/src/player.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/player.h 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/player.h 2026-07-18 01:43:54 UTC (rev 1673)
@@ -248,8 +248,18 @@
// ExperienceWindow needs this
uint32_t m_currentAltExp;
+ int64_t m_expBarProgress; // exact level progress from the wire
+ // (percent*1000, 0-100000); -1 = unset.
+ // Display uses this instead of the calc_exp
+ // reconstruction, whose curve can invert
+ // (m_maxExp below m_minExp) on some levels.
+ bool m_validAltExp; // true once OP_AAExpUpdate has set a real AA
+ // value; until then the profile's expAA reads
+ // as garbage, so the AA bar/row is suppressed.
uint16_t m_currentAApts;
uint32_t m_currentExp;
+ uint32_t m_lastExpRaw = 0xffffffff; // dedup redundant zone-in exp packets
+ uint32_t m_lastAltExpRaw = 0xffffffff; // dedup redundant zone-in AA-exp packets
uint32_t m_minExp;
uint32_t m_maxExp;
uint32_t m_tickExp;
Modified: showeq/branches/xerxes-trunk-dev/src/util.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/util.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/util.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -284,12 +284,14 @@
class_str += "ENC ";
if (classes & 16384)
class_str += "BST ";
+ if (classes & 32768)
+ class_str += "BER ";
if (classes >= 32768) // 2^15 aka (1 << 15)
{
int new_class;
- for (int i = 15; i < 31; ++i)
- {
+ for (int i = 16; i < 31; ++i)
+ {
new_class = 1 << i;
if (classes & new_class)
{
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|