fix(scene): unlock all teleports across maps, merge resource configs, fix scenary not load error, and add quick farm battle handlers

This commit is contained in:
CAtlas0784 committed 2026-10-16 20:08:13 +07:00
1 parent 9aab62414f
commit 163d4433e6
47 files changed
+2025067 -6082

No files matched your search

+293 -289
View File
@@ -62,8 +62,15 @@ func skipWireField(wireType uint32, buf *[]byte) {
}
func mapChallengeStageID(id uint32) uint32 {
cd := getChallengeData()
if _, ok := cd.Challenges[id]; ok {
return id
}
if _, ok := cd.Tierce[id]; ok {
return id
}
switch {
case id >= 5400 && id <= 5600: // MoC 1035/1036 -> 5301..5312
case id >= 5400 && id <= 5700: // MoC 1035/1036 -> 5301..5312
rem := id % 100
if rem < 1 {
rem = 1
@@ -91,7 +98,6 @@ func mapChallengeStageID(id uint32) uint32 {
}
return 30200 + rem
case id > 1:
cd := getChallengeData()
if _, ok := cd.Challenges[id]; !ok {
return id - 1
}
@@ -100,10 +106,59 @@ func mapChallengeStageID(id uint32) uint32 {
return id
}
func readLenDelimAndAdvance(buf *[]byte) ([]byte, bool) {
b := *buf
l, n := readUvarint(b)
if n == 0 || int(l) > len(b[n:]) {
*buf = nil
return nil, false
}
sub := b[n : n+int(l)]
*buf = b[n+int(l):]
return sub, true
}
func readLenDelim(buf []byte) ([]byte, bool) {
l, n := readUvarint(buf)
if n == 0 || int(l) > len(buf[n:]) {
return nil, false
}
return buf[n : n+int(l)], true
}
func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
var req decodedChallengeStart
buf := payload
var pbReq goproto.StartChallengeCsReq
if err := proto.Unmarshal(payload, &pbReq); err == nil && pbReq.ChallengeId != 0 {
req.challengeID = pbReq.ChallengeId
req.firstAvatars = pbReq.FirstLineup
if len(req.firstAvatars) == 0 && len(pbReq.AvatarLineupFirst) > 0 {
for _, av := range pbReq.AvatarLineupFirst {
if av.Id != 0 {
req.firstAvatars = append(req.firstAvatars, av.Id)
}
}
}
req.secondAvatars = pbReq.SecondLineup
if len(req.secondAvatars) == 0 && len(pbReq.AvatarLineupSecond) > 0 {
for _, av := range pbReq.AvatarLineupSecond {
if av.Id != 0 {
req.secondAvatars = append(req.secondAvatars, av.Id)
}
}
}
if pbReq.StageInfo != nil {
if pbReq.StageInfo.BossInfo != nil && pbReq.StageInfo.BossInfo.BuffOne != 0 {
req.buffID = pbReq.StageInfo.BossInfo.BuffOne
} else if pbReq.StageInfo.StoryInfo != nil && pbReq.StageInfo.StoryInfo.BuffOne != 0 {
req.buffID = pbReq.StageInfo.StoryInfo.BuffOne
}
}
return req
}
buf := payload
for len(buf) > 0 {
tag, n := readUvarint(buf)
if n == 0 {
@@ -133,13 +188,10 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
buf = buf[n2:]
req.firstAvatars = append(req.firstAvatars, uint32(v))
case fieldNumber == 6 && wireType == 2:
sub, ok := readLenDelim(buf)
sub, ok := readLenDelimAndAdvance(&buf)
if !ok {
buf = nil
break
}
buf = buf[len(buf)-len(sub):] // consumed below
buf = buf[len(sub):]
for len(sub) > 0 {
v, n3 := readUvarint(sub)
if n3 == 0 {
@@ -157,12 +209,10 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
buf = buf[n2:]
req.secondAvatars = append(req.secondAvatars, uint32(v))
case fieldNumber == 8 && wireType == 2:
sub, ok := readLenDelim(buf)
sub, ok := readLenDelimAndAdvance(&buf)
if !ok {
buf = nil
break
}
buf = buf[len(sub):]
for len(sub) > 0 {
v, n3 := readUvarint(sub)
if n3 == 0 {
@@ -171,14 +221,11 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
sub = sub[n3:]
req.secondAvatars = append(req.secondAvatars, uint32(v))
}
case fieldNumber == 4 && wireType == 2:
// AvatarLineup: id = tag 1
sub, ok := readLenDelim(buf)
case (fieldNumber == 4 || fieldNumber == 15) && wireType == 2:
sub, ok := readLenDelimAndAdvance(&buf)
if !ok {
buf = nil
break
}
buf = buf[len(sub):]
for len(sub) > 0 {
stag, n3 := readUvarint(sub)
if n3 == 0 {
@@ -192,19 +239,20 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
break
}
sub = sub[n3+n4:]
req.firstAvatars = append(req.firstAvatars, uint32(v))
if fieldNumber == 4 {
req.firstAvatars = append(req.firstAvatars, uint32(v))
} else {
req.secondAvatars = append(req.secondAvatars, uint32(v))
}
} else {
skipWireField(swt, &sub)
}
}
case fieldNumber == 10 && wireType == 2:
// ChallengeBuffInfo { story_info = 9, boss_info = 10 }
sub, ok := readLenDelim(buf)
sub, ok := readLenDelimAndAdvance(&buf)
if !ok {
buf = nil
break
}
buf = buf[len(sub):]
for len(sub) > 0 {
stag, n3 := readUvarint(sub)
if n3 == 0 {
@@ -212,9 +260,9 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
}
sfn := uint32(stag >> 3)
swt := uint32(stag & 0x7)
sub2 := sub[n3:]
sub = sub[n3:]
if swt == 2 && (sfn == 9 || sfn == 10) {
inner, ok2 := readLenDelim(sub2)
inner, ok2 := readLenDelimAndAdvance(&sub)
if !ok2 {
break
}
@@ -225,12 +273,13 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
}
f2 := uint32(t2 >> 3)
w2 := uint32(t2 & 0x7)
inner = inner[n4:]
if w2 == 0 && (f2 == 10 || f2 == 15) {
v, n5 := readUvarint(inner[n4:])
v, n5 := readUvarint(inner)
if n5 == 0 {
break
}
inner = inner[n4+n5:]
inner = inner[n5:]
if req.buffID == 0 {
req.buffID = uint32(v)
}
@@ -238,14 +287,6 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
skipWireField(w2, &inner)
}
}
sub = sub2
// skip the whole len-delimited field
_, n6 := readUvarint(sub2)
if n6 == 0 {
sub = nil
} else {
sub = sub2[n6:]
}
} else {
skipWireField(swt, &sub)
}
@@ -258,13 +299,6 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
return req
}
func readLenDelim(buf []byte) ([]byte, bool) {
l, n := readUvarint(buf)
if n == 0 || int(l) > len(buf[n:]) {
return nil, false
}
return buf[n : n+int(l)], true
}
type tierceStageEntry struct {
buff uint32
@@ -273,105 +307,122 @@ type tierceStageEntry struct {
func decodeStartChallengeTierceReq(payload []byte) decodedChallengeStart {
var req decodedChallengeStart
buf := payload
var stagesAvatars []tierceStageEntry
for len(buf) > 0 {
tag, n := readUvarint(buf)
if n == 0 {
break
var pbReq goproto.StartChallengeTierceCsReq
if err := proto.Unmarshal(payload, &pbReq); err == nil && pbReq.ChallengeId != 0 {
req.challengeID = pbReq.ChallengeId
req.stageIndex = pbReq.FINOCEGDBDI
req.isSingleStage = pbReq.MPKMIKBFEHG
for _, lt := range pbReq.LineupTierceList {
var l []uint32
for _, av := range lt.TierceAvatarIdList {
if av.Id != 0 {
l = append(l, av.Id)
}
}
stagesAvatars = append(stagesAvatars, tierceStageEntry{
buff: lt.BuffId,
lineup: l,
})
}
buf = buf[n:]
fieldNumber := uint32(tag >> 3)
wireType := uint32(tag & 0x7)
} else {
buf := payload
for len(buf) > 0 {
tag, n := readUvarint(buf)
if n == 0 {
break
}
buf = buf[n:]
fieldNumber := uint32(tag >> 3)
wireType := uint32(tag & 0x7)
switch {
case (fieldNumber == 10 || fieldNumber == 1) && wireType == 0:
v, n2 := readUvarint(buf)
if n2 == 0 {
buf = nil
break
}
buf = buf[n2:]
req.stageIndex = uint32(v)
case (fieldNumber == 15 || fieldNumber == 3) && wireType == 0:
v, n2 := readUvarint(buf)
if n2 == 0 {
buf = nil
break
}
buf = buf[n2:]
req.challengeID = uint32(v)
case (fieldNumber == 5 || fieldNumber == 13) && wireType == 0:
v, n2 := readUvarint(buf)
if n2 == 0 {
buf = nil
break
}
buf = buf[n2:]
req.isSingleStage = v != 0
case (fieldNumber == 12 || fieldNumber == 14) && wireType == 2:
sub, ok := readLenDelim(buf)
if !ok {
buf = nil
break
}
buf = buf[len(sub):]
var stageBuff uint32
var stageLineup []uint32
for len(sub) > 0 {
subTag, n2 := readUvarint(sub)
switch {
case (fieldNumber == 10 || fieldNumber == 1 || fieldNumber == 5) && wireType == 0:
v, n2 := readUvarint(buf)
if n2 == 0 {
buf = nil
break
}
subFn := uint32(subTag >> 3)
subWt := uint32(subTag & 0x7)
switch {
case (subFn == 6 || subFn == 5) && subWt == 0:
v, n3 := readUvarint(sub[n2:])
if n3 == 0 {
sub = nil
buf = buf[n2:]
req.stageIndex = uint32(v)
case (fieldNumber == 15 || fieldNumber == 3) && wireType == 0:
v, n2 := readUvarint(buf)
if n2 == 0 {
buf = nil
break
}
buf = buf[n2:]
req.challengeID = uint32(v)
case (fieldNumber == 2 || fieldNumber == 13) && wireType == 0:
v, n2 := readUvarint(buf)
if n2 == 0 {
buf = nil
break
}
buf = buf[n2:]
req.isSingleStage = v != 0
case (fieldNumber == 9 || fieldNumber == 12 || fieldNumber == 14) && wireType == 2:
sub, ok := readLenDelimAndAdvance(&buf)
if !ok {
break
}
var stageBuff uint32
var stageLineup []uint32
for len(sub) > 0 {
subTag, n2 := readUvarint(sub)
if n2 == 0 {
break
}
sub = sub[n2+n3:]
stageBuff = uint32(v)
case (subFn == 4 || subFn == 9) && subWt == 2:
abuf, ok2 := readLenDelim(sub[n2:])
if !ok2 {
sub = nil
break
}
sub = sub[n2+len(abuf):]
var aid uint32
for len(abuf) > 0 {
atag, n4 := readUvarint(abuf)
if n4 == 0 {
subFn := uint32(subTag >> 3)
subWt := uint32(subTag & 0x7)
sub = sub[n2:]
switch {
case (subFn == 10 || subFn == 6 || subFn == 5) && subWt == 0:
v, n3 := readUvarint(sub)
if n3 == 0 {
sub = nil
break
}
afn := uint32(atag >> 3)
awt := uint32(atag & 0x7)
if (afn == 7 || afn == 9) && awt == 0 {
v, n5 := readUvarint(abuf[n4:])
if n5 == 0 {
sub = sub[n3:]
stageBuff = uint32(v)
case (subFn == 9 || subFn == 4) && subWt == 2:
abuf, ok2 := readLenDelimAndAdvance(&sub)
if !ok2 {
break
}
var aid uint32
for len(abuf) > 0 {
atag, n4 := readUvarint(abuf)
if n4 == 0 {
break
}
abuf = abuf[n4+n5:]
aid = uint32(v)
} else {
skipWireField(awt, &abuf)
afn := uint32(atag >> 3)
awt := uint32(atag & 0x7)
abuf = abuf[n4:]
if (afn == 1 || afn == 7 || afn == 9) && awt == 0 {
v, n5 := readUvarint(abuf)
if n5 == 0 {
break
}
abuf = abuf[n5:]
aid = uint32(v)
} else {
skipWireField(awt, &abuf)
}
}
if aid != 0 {
stageLineup = append(stageLineup, aid)
}
default:
skipWireField(subWt, &sub)
}
if aid != 0 {
stageLineup = append(stageLineup, aid)
}
default:
skipWireField(subWt, &sub)
}
stagesAvatars = append(stagesAvatars, tierceStageEntry{buff: stageBuff, lineup: stageLineup})
default:
skipWireField(wireType, &buf)
}
stagesAvatars = append(stagesAvatars, tierceStageEntry{buff: stageBuff, lineup: stageLineup})
default:
skipWireField(wireType, &buf)
}
}
@@ -636,7 +687,7 @@ func handleStartChallenge(s *Session, payload []byte) error {
baseID := mapChallengeStageID(req.challengeID)
cd := getChallengeData()
entrance, group, monster, event, buff := uint32(3014101), uint32(5), uint32(4033010), uint32(30124011), uint32(0)
entrance, group, monster, event, buff := uint32(3000101), uint32(5), uint32(4033010), uint32(30124011), uint32(0)
if c, ok := cd.Challenges[baseID]; ok {
entrance, group, monster, event = c.Entrance, c.Group1, c.Monster1, c.Event1
if req.buffID != 0 {
@@ -644,13 +695,12 @@ func handleStartChallenge(s *Session, payload []byte) error {
} else {
buff = c.Buff
}
} else if t, ok := cd.Tierce[baseID]; ok {
entrance, group, monster, event = t.Entrance, t.Group, t.Monster, t.Event
buff = req.buffID
}
// the 4.6.51 client has no MapEntrance 3014101 (Rust lesson: Lua
// GotoMapParam nil -> stuck loading) even though res.json has it
if entrance == 3014101 {
entrance = 3000101
} else if res, err := common.GameRes(); err == nil && !mapHasEntry(res, entrance) {
if res, err := common.GameRes(); err == nil && !mapHasEntry(res, entrance) {
Log("[CHALLENGE] Arena entrance missing in res.json, fallback to 3000101")
entrance = 3000101
}
@@ -667,64 +717,37 @@ func handleStartChallenge(s *Session, payload []byte) error {
return err
}
custom := map[uint32]uint32{}
for i, aid := range chosenAvatars {
custom[uint32(i)] = aid
var lineups []*goproto.LineupInfo
lineups = append(lineups, lineupInfoFromAvatars(chosenAvatars, sceneInfo.PlaneId, goproto.ExtraLineupType(1), 0))
secondAvatars := req.secondAvatars
if len(secondAvatars) == 0 {
secondAvatars = chosenAvatars
}
lineupInfo := common.ToLineupInfo(custom)
secondAvatars = dedupeTruncate4(secondAvatars)
lineups = append(lineups, lineupInfoFromAvatars(secondAvatars, sceneInfo.PlaneId, goproto.ExtraLineupType(1), 1))
curChallenge := &goproto.CurChallenge{
ChallengeId: req.challengeID,
Status: 1, // CHALLENGE_DOING
Status: goproto.ChallengeStatus_CHALLENGE_DOING,
RoundCount: 0,
ScoreId: scoreIDFor(req.challengeID),
ScoreTwo: 0,
ExtraLineupType: goproto.ExtraLineupType(1),
}
chalBuf, err := proto.Marshal(curChallenge)
if err != nil {
return err
rsp := &goproto.StartChallengeScRsp{
Retcode: 0,
CurChallenge: curChallenge,
LineupList: lineups,
Scene: sceneInfo,
}
lineupBuf, err := proto.Marshal(lineupInfo)
if err != nil {
return err
}
sceneBuf, err := proto.Marshal(sceneInfo)
rspBuf, err := proto.Marshal(rsp)
if err != nil {
return err
}
// 4.6.51: StartChallengeScRsp.stage_info (tag 13) - boss node info echoes
// the selected buff so the client can set up the challenge UI
stageInfo := &goproto.ChallengeStageInfo{}
if req.buffID != 0 {
stageInfo.BossInfo = &goproto.ChallengeBossInfo{
FirstNode: &goproto.ChallengeBossSingleNodeInfo{BuffId: req.buffID},
SecondNode: &goproto.ChallengeBossSingleNodeInfo{BuffId: req.buffID},
}
}
stageBuf, err := proto.Marshal(stageInfo)
if err != nil {
return err
}
var body []byte
body = append(body, 0x18, 0x00) // tag 3: retcode = 0
body = append(body, 0x2A) // tag 5: cur_challenge
body = appendUvarint(body, uint64(len(chalBuf)))
body = append(body, chalBuf...)
body = append(body, 0x32) // tag 6: lineup_list
body = appendUvarint(body, uint64(len(lineupBuf)))
body = append(body, lineupBuf...)
body = append(body, 0x52) // tag 10: scene
body = appendUvarint(body, uint64(len(sceneBuf)))
body = append(body, sceneBuf...)
body = append(body, 0x6A) // tag 13: stage_info
body = appendUvarint(body, uint64(len(stageBuf)))
body = append(body, stageBuf...)
return s.SendRaw(&NetPacket{CmdType: 1787, Body: body})
return s.SendRaw(&NetPacket{CmdType: 1787, Body: rspBuf})
}
func scoreIDFor(challengeID uint32) uint32 {
@@ -734,7 +757,7 @@ func scoreIDFor(challengeID uint32) uint32 {
return 0
}
// handleStartChallengeTierce ports handle_start_challenge_tierce -> cmd 8973.
// handleStartChallengeTierce ports handle_start_challenge_tierce -> cmd 8998 (4.6.51).
func handleStartChallengeTierce(s *Session, payload []byte) error {
req := decodeStartChallengeTierceReq(payload)
baseID := mapChallengeStageID(req.challengeID)
@@ -751,9 +774,17 @@ func handleStartChallengeTierce(s *Session, payload []byte) error {
case 0:
chosenAvatars = req.firstAvatars
case 1:
chosenAvatars = req.secondAvatars
if len(req.secondAvatars) > 0 {
chosenAvatars = req.secondAvatars
} else {
chosenAvatars = req.firstAvatars
}
case 2:
chosenAvatars = req.thirdAvatars
if len(req.thirdAvatars) > 0 {
chosenAvatars = req.thirdAvatars
} else {
chosenAvatars = req.firstAvatars
}
}
if len(chosenAvatars) == 0 {
@@ -768,32 +799,12 @@ func handleStartChallengeTierce(s *Session, payload []byte) error {
}
cd := getChallengeData()
entrance, group, monster, event, buff := uint32(3014101), uint32(5), uint32(4033010), uint32(30124011), uint32(0)
switch {
case req.stageIndex == 2:
entrance, group, monster, event, buff := uint32(3000101), uint32(5), uint32(4033010), uint32(30124011), uint32(0)
if req.stageIndex == 0 {
if t, ok := cd.Tierce[baseID]; ok {
entrance, group, monster, event, buff = t.Entrance, t.Group, t.Monster, t.Event, req.buffID
} else if c, ok2 := cd.Challenges[baseID]; ok2 {
entrance, group, monster, event = c.Entrance2, c.Group2, c.Monster2, c.Event2
if req.buffID != 0 {
buff = req.buffID
} else {
buff = c.Buff
}
}
case req.stageIndex == 1:
if c, ok := cd.Challenges[baseID]; ok {
entrance, group, monster, event = c.Entrance2, c.Group2, c.Monster2, c.Event2
if req.buffID != 0 {
buff = req.buffID
} else {
buff = c.Buff
}
} else {
entrance, group, monster, event = 3014101, 6, 8013010, 30124012
}
default:
if c, ok := cd.Challenges[baseID]; ok {
entrance, group, monster, event = c.Entrance, c.Group1, c.Monster1, c.Event1
if req.buffID != 0 {
buff = req.buffID
@@ -801,13 +812,32 @@ func handleStartChallengeTierce(s *Session, payload []byte) error {
buff = c.Buff
}
}
} else if req.stageIndex == 1 {
// Stage 2 (Node 2)
if c, ok := cd.Challenges[baseID-1]; ok && c.Entrance2 != 0 {
entrance, group, monster, event = c.Entrance2, c.Group2, c.Monster2, c.Event2
if req.buffID != 0 {
buff = req.buffID
} else {
buff = c.Buff
}
} else if c, ok2 := cd.Challenges[baseID]; ok2 && c.Entrance2 != 0 {
entrance, group, monster, event = c.Entrance2, c.Group2, c.Monster2, c.Event2
if req.buffID != 0 {
buff = req.buffID
} else {
buff = c.Buff
}
} else if t, ok3 := cd.Tierce[baseID]; ok3 {
entrance, group, monster, event, buff = t.Entrance, t.Group, t.Monster, t.Event, req.buffID
}
} else {
if t, ok := cd.Tierce[baseID]; ok {
entrance, group, monster, event, buff = t.Entrance, t.Group, t.Monster, t.Event, req.buffID
}
}
// the 4.6.51 client cannot load arena 3014101 (no MapEntrance) - fall
// back to the standard arena, mirroring handleStartChallenge
if entrance == 3014101 {
entrance = 3000101
} else if res, err := common.GameRes(); err == nil && !mapHasEntry(res, entrance) {
if res, err := common.GameRes(); err == nil && !mapHasEntry(res, entrance) {
Log("[CHALLENGE] Tierce arena entrance missing in res.json, fallback to 3000101")
entrance = 3000101
}
@@ -875,43 +905,27 @@ func handleStartChallengeTierce(s *Session, payload []byte) error {
}
}
// ChallengeTierceChallengeInfo: stage_index=4, challenge_id=6, is_single_stage=14, lineup_list=15
var tierceInfo []byte
tierceInfo = append(tierceInfo, 0x20) // tag 4: stage_index
tierceInfo = appendUvarint(tierceInfo, uint64(req.stageIndex))
tierceInfo = append(tierceInfo, 0x30) // tag 6: challenge_id
tierceInfo = appendUvarint(tierceInfo, uint64(req.challengeID))
if req.isSingleStage {
tierceInfo = append(tierceInfo, 0x70, 0x01) // tag 14: true
}
var tierceLineupList []*goproto.LineupInfo
for stIdx, stageAvatars := range allLineups {
lineup := lineupInfoFromAvatars(stageAvatars, sceneInfo.PlaneId, goproto.ExtraLineupType(1), uint32(stIdx))
lineupBuf, err := proto.Marshal(lineup)
if err != nil {
return err
}
tierceInfo = append(tierceInfo, 0x7A) // tag 15: lineup_list
tierceInfo = appendUvarint(tierceInfo, uint64(len(lineupBuf)))
tierceInfo = append(tierceInfo, lineupBuf...)
tierceLineupList = append(tierceLineupList, lineupInfoFromAvatars(stageAvatars, sceneInfo.PlaneId, goproto.ExtraLineupType(1), uint32(stIdx)))
}
sceneBuf, err := proto.Marshal(sceneInfo)
// 4.6.51 StartChallengeTierceScRsp (CmdID 8998)
rsp46 := &goproto.StartChallengeTierceScRsp{
Retcode: 0,
Scene: sceneInfo,
CurTierceChallenge: &goproto.CurTierceChallenge{
LineupList: tierceLineupList,
ChallengeId: req.challengeID,
FINOCEGDBDI: req.stageIndex,
MPKMIKBFEHG: req.isSingleStage,
},
}
rspBuf, err := proto.Marshal(rsp46)
if err != nil {
return err
}
// StartChallengeTierceScRsp cmd 8973: retcode=12, challenge_tierce_info=4, scene=9
var body []byte
body = append(body, 0x60, 0x00) // tag 12: retcode = 0
body = append(body, 0x22) // tag 4
body = appendUvarint(body, uint64(len(tierceInfo)))
body = append(body, tierceInfo...)
body = append(body, 0x4A) // tag 9: scene
body = appendUvarint(body, uint64(len(sceneBuf)))
body = append(body, sceneBuf...)
return s.SendRaw(&NetPacket{CmdType: 8973, Body: body})
return s.SendRaw(&NetPacket{CmdType: 8998, Body: rspBuf})
}
func handleSetChallengeTierceLineup(s *Session, payload []byte) error {
@@ -921,34 +935,60 @@ func handleSetChallengeTierceLineup(s *Session, payload []byte) error {
tierceLineups[challengeID] = stages
tierceLineupsMu.Unlock()
}
if err := s.SendRaw(&NetPacket{CmdType: 8999, Body: []byte{0x08, 0x00}}); err != nil {
// 4.6.51 SetChallengeTierceLineupScRsp (cmd 8985, retcode tag 1 = 0)
if err := s.SendRaw(&NetPacket{CmdType: 8985, Body: []byte{0x08, 0x00}}); err != nil {
return err
}
_ = s.SendRaw(&NetPacket{CmdType: 8999, Body: []byte{0x08, 0x00}})
_ = s.SendRaw(&NetPacket{CmdType: 8995, Body: []byte{0x60, 0x00}})
return nil
}
func handleLeaveChallenge(s *Session, _ []byte) error {
var returnEntry uint32 = 102020107
if player := s.JSON(); player != nil {
player.BattleConfig.CustomBattleLineup = nil
// Always return safely to Belobog Admin District 102020107
player.Scene.EntryID = 102020107
player.Scene.PlaneID = 10202
player.Scene.FloorID = 10202001
player.Position = common.Position{X: -92, Y: 4278, Z: 68083, RotY: 155566}
player.SavePersistent()
}
if err := s.SendRaw(&NetPacket{CmdType: 1716, Body: []byte{0x20, 0x00}}); err != nil {
// 4.6.51 LeaveChallengeScRsp (cmd 1771, tag 10 retcode 0)
if err := s.SendRaw(&NetPacket{CmdType: 1771, Body: []byte{0x50, 0x00}}); err != nil {
return err
}
_ = s.SendRaw(&NetPacket{CmdType: 1716, Body: []byte{0x20, 0x00}})
_ = s.SendRaw(&NetPacket{CmdType: 1781, Body: []byte{0x40, 0x00}})
// Restore the player back to the open world
_, _ = loadScene(s, returnEntry, true, nil, nil)
_ = refreshLineup(s)
return nil
}
func handleLeaveChallengeTierce(s *Session, _ []byte) error {
var returnEntry uint32 = 102020107
if player := s.JSON(); player != nil {
player.BattleConfig.CustomBattleLineup = nil
// Always return safely to Belobog Admin District 102020107
player.Scene.EntryID = 102020107
player.Scene.PlaneID = 10202
player.Scene.FloorID = 10202001
player.Position = common.Position{X: -92, Y: 4278, Z: 68083, RotY: 155566}
player.SavePersistent()
}
if err := s.SendRaw(&NetPacket{CmdType: 8997, Body: []byte{0x38, 0x00}}); err != nil {
// 4.6.51 LeaveChallengeTierceScRsp (cmd 8983, tag 13 retcode 0)
if err := s.SendRaw(&NetPacket{CmdType: 8983, Body: []byte{0x68, 0x00}}); err != nil {
return err
}
_ = s.SendRaw(&NetPacket{CmdType: 8997, Body: []byte{0x38, 0x00}})
_ = s.SendRaw(&NetPacket{CmdType: 8982, Body: []byte{0x68, 0x00}})
// Restore the player back to the open world
_, _ = loadScene(s, returnEntry, true, nil, nil)
_ = refreshLineup(s)
return nil
}
@@ -1017,62 +1057,26 @@ func buildGetChallengeTierceDataScRspReal() []byte {
}
sort.Slice(tierceStages, func(i, j int) bool { return tierceStages[i].id < tierceStages[j].id })
var groups []*goproto.ChallengeTierceGroup
for _, e := range tierceStages {
groups = append(groups, &goproto.ChallengeTierceGroup{
ChallengeId: e.id,
IsWin: true,
FinishedTargetList: e.targets,
})
}
rspMsg := &goproto.GetChallengeTierceDataScRsp{
ChallengeTierceGroups: groups,
Retcode: 0,
}
body, err := proto.Marshal(rspMsg)
if err == nil {
return body
}
var rsp []byte
rsp = append(rsp, 0x40, 0x00) // tag 8: retcode = 0
rsp = append(rsp, 0x60, 0x00) // legacy tag 12: retcode = 0
for _, e := range tierceStages {
var tierceData []byte
// tag 3: challenge_id
tierceData = append(tierceData, 0x18)
tierceData = appendUvarint(tierceData, uint64(e.id))
// tag 6: is_passed = true
tierceData = append(tierceData, 0x30, 0x01)
// tag 7: finished_target_list (packed)
var targetsBuf []byte
for _, t := range e.targets {
targetsBuf = appendUvarint(targetsBuf, uint64(t))
}
tierceData = append(tierceData, 0x3A)
tierceData = appendUvarint(tierceData, uint64(len(targetsBuf)))
tierceData = append(tierceData, targetsBuf...)
// tag 4: result_list (3 stages)
for stageIdx := uint32(0); stageIdx < 3; stageIdx++ {
var result []byte
result = append(result, 0x18) // tag 3: score_id
result = appendUvarint(result, uint64(e.score))
result = append(result, 0x48) // tag 9: stage_index
result = appendUvarint(result, uint64(stageIdx))
result = append(result, 0x50, 0x01) // tag 10: end_status = WIN
tierceData = append(tierceData, 0x22)
tierceData = appendUvarint(tierceData, uint64(len(result)))
tierceData = append(tierceData, result...)
}
// tag 8: stage_info_list (3 stages)
for stageIdx := uint32(0); stageIdx < 3; stageIdx++ {
var sinfo []byte
sinfo = append(sinfo, 0x70) // tag 14: stage_index
sinfo = appendUvarint(sinfo, uint64(stageIdx))
tierceData = append(tierceData, 0x42)
tierceData = appendUvarint(tierceData, uint64(len(sinfo)))
tierceData = append(tierceData, sinfo...)
}
// tag 4: challenge_info_list (4.5.52)
rsp = append(rsp, 0x22)
rsp = appendUvarint(rsp, uint64(len(tierceData)))
rsp = append(rsp, tierceData...)
// tag 10: challenge_info_list (legacy)
rsp = append(rsp, 0x52)
rsp = appendUvarint(rsp, uint64(len(tierceData)))
rsp = append(rsp, tierceData...)
}
return rsp
}