mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-27 01:53:19 +01:00
fix(channelserver): correct session handler retail mismatches (#167)
Lobby search now returns only quest-bound players (QuestReserved) instead of all reserved slots, matching retail behavior. The new field is pre-collected under server lock before stage iteration to respect Server.Mutex → Stage.RWMutex lock ordering. Replaced three TODOs with RE documentation from Ghidra decompilation of mhfo-hd.dll ZZ: - Log key off-by-one: putRecord_log/putTerminal_log pass size 0 for the key field in ZZ, so the stored key is unused beyond issuance - User search padding: ZZ per-entry parser confirms 40-byte block via memcpy(dst, src+8, 0x28); G2 DLL analysis inconclusive (stripped) - Player count: field at entry offset 0x08 maps to struct param_1[0xe]
This commit is contained in:
@@ -107,6 +107,19 @@ func (r *LocalChannelRegistry) SearchStages(stagePrefix string, max int) []Stage
|
||||
if len(results) >= max {
|
||||
break
|
||||
}
|
||||
|
||||
// Pre-collect which charIDs are in quest stages under server lock,
|
||||
// so we can count quest-reserved players without lock ordering issues
|
||||
// (Server.Mutex must be acquired before Stage.RWMutex).
|
||||
c.Lock()
|
||||
inQuest := make(map[uint32]bool)
|
||||
for _, sess := range c.sessions {
|
||||
if sess.stage != nil && len(sess.stage.id) > 4 && sess.stage.id[3:5] == "Qs" {
|
||||
inQuest[sess.charID] = true
|
||||
}
|
||||
}
|
||||
c.Unlock()
|
||||
|
||||
cIP := net.ParseIP(c.IP).To4()
|
||||
cPort := c.Port
|
||||
c.stages.Range(func(_ string, stage *Stage) bool {
|
||||
@@ -127,16 +140,24 @@ func (r *LocalChannelRegistry) SearchStages(stagePrefix string, max int) []Stage
|
||||
bin3Copy := make([]byte, len(bin3))
|
||||
copy(bin3Copy, bin3)
|
||||
|
||||
questReserved := 0
|
||||
for charID := range stage.reservedClientSlots {
|
||||
if inQuest[charID] {
|
||||
questReserved++
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, StageSnapshot{
|
||||
ServerIP: cIP,
|
||||
ServerPort: cPort,
|
||||
StageID: stage.id,
|
||||
ClientCount: len(stage.clients) + len(stage.reservedClientSlots),
|
||||
Reserved: len(stage.reservedClientSlots),
|
||||
MaxPlayers: stage.maxPlayers,
|
||||
RawBinData0: bin0Copy,
|
||||
RawBinData1: bin1Copy,
|
||||
RawBinData3: bin3Copy,
|
||||
ServerIP: cIP,
|
||||
ServerPort: cPort,
|
||||
StageID: stage.id,
|
||||
ClientCount: len(stage.clients) + len(stage.reservedClientSlots),
|
||||
Reserved: len(stage.reservedClientSlots),
|
||||
QuestReserved: questReserved,
|
||||
MaxPlayers: stage.maxPlayers,
|
||||
RawBinData0: bin0Copy,
|
||||
RawBinData1: bin1Copy,
|
||||
RawBinData3: bin3Copy,
|
||||
})
|
||||
stage.RUnlock()
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user