docs & feat: add license & attribution notice (Apache-2.0 / GPL-3.0) and core system enhancements

This commit is contained in:
CAtlas0784 committed 2026-11-01 20:14:36 +07:00
1 parent 507836d367
commit ce6aabfc55
47 files changed
+5674 -724

No files matched your search

+174 -192
View File
@@ -275,217 +275,199 @@ func cwNextRound(catalog *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH) (*pb.
}
func init() {
cwHandle("GridFightUpdatePos", func(s *Session, req *pb.GridFightUpdatePosCsReq, rsp *pb.GridFightUpdatePosScRsp) error {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
if _, active := CURRENT_BATTLE.Load(s.Token); active {
PacketLog("CW_UPDATE_POS rejected reason=battle_active")
return nil
}
catalog, err := common.LoadCurrencyWarsCatalog()
if err != nil {
return err
}
data, revision, err := readCurrencyWarsData(catalog)
if err != nil {
return err
}
if data == nil || data.DHFOFLFCLAH == nil {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_NOT_IN_GAMEPLAY)
return nil
}
if revision != s.currencyWarsRevision {
return nil
}
run := data.DHFOFLFCLAH
if run.FMIGOKIDKJL != nil || len(req.AAKDHMHCMLP) == 0 {
PacketLog("CW_UPDATE_POS rejected reason=not_preparing_or_empty")
return nil
}
player, team, _ := cwComponents(run)
if player == nil || team == nil {
return nil
}
front, err := catalog.IntegerRule("GridFight_Front_AvatarMaxNum")
if err != nil {
return err
}
back, err := catalog.IntegerRule("GridFight_Back_AvatarMaxNum")
if err != nil {
return err
}
activeEnd := uint64(front + back)
overflow, err := catalog.IntegerRule("GridFight_Bench_OverFlow_AvatarNum")
if err != nil {
return err
}
benchEnd := activeEnd + uint64(player.MICEOJBAKGN) + uint64(overflow)
changes := map[uint32]uint32{}
for _, v := range req.AAKDHMHCMLP {
if v == nil || v.UniqueId == 0 {
cwHandle("GridFightUpdatePos", cwUpdatePositions)
// CmdID 8543 (GEBBMDCOLCH) -> ScRsp 8630 (ALNFEBKHCFC)
// Protocol revision: 4.6.53 (upstream_cmdids.go).
// Assumption: Native Client sends GEBBMDCOLCH during preparation / round transition to sync
// the batch formation role positions (CONJCEMAHGN: MDNAGANGNKK = role UID, JFCJNHNBMCB = pos).
// Reuse the validated formation transaction; acknowledge only after commit.
cwHandleNamed("GEBBMDCOLCH", "ALNFEBKHCFC", func(s *Session, req *pb.GEBBMDCOLCH, rsp *pb.ALNFEBKHCFC) error {
mapped := &pb.GridFightUpdatePosCsReq{}
for _, entry := range req.CONJCEMAHGN {
if entry == nil {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
return nil
}
if _, ok := changes[v.UniqueId]; ok {
return nil
}
if uint64(v.Pos) > benchEnd {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_POS_NOT_VALID)
return nil
}
changes[v.UniqueId] = v.Pos
mapped.AAKDHMHCMLP = append(mapped.AAKDHMHCMLP, &pb.MHEGMHKHAAD{UniqueId: entry.MDNAGANGNKK, Pos: entry.JFCJNHNBMCB})
}
// Validate final positions together, so swaps need no intermediate empty slot.
occupied := map[uint32]bool{}
owned := map[uint32]bool{}
active := uint32(0)
activeAvatars := map[uint32]bool{}
for _, role := range team.GridGameRoleList {
owned[role.UniqueId] = true
pos := role.Pos
if v, ok := changes[role.UniqueId]; ok {
pos = v
}
if pos != 0 {
if occupied[pos] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_POS_EXIST_ROLE)
return nil
}
occupied[pos] = true
}
if pos != 0 && uint64(pos) <= activeEnd {
config, err := catalog.CapturedRoleVariant(role.Id, role.RoleStar)
if err != nil {
return err
}
if activeAvatars[config.Config.AvatarID] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_SAME_ROLE_IN_BATTLE)
return nil
}
activeAvatars[config.Config.AvatarID] = true
active++
}
result := &pb.GridFightUpdatePosScRsp{}
err := cwUpdatePositions(s, mapped, result)
rsp.Retcode = result.Retcode
return err
})
}
func cwUpdatePositions(s *Session, req *pb.GridFightUpdatePosCsReq, rsp *pb.GridFightUpdatePosScRsp) error {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
if _, active := CURRENT_BATTLE.Load(s.Token); active {
PacketLog("CW_UPDATE_POS rejected reason=battle_active")
return nil
}
catalog, err := common.LoadCurrencyWarsCatalog()
if err != nil {
return err
}
data, revision, err := readCurrencyWarsData(catalog)
if err != nil {
return err
}
if data == nil || data.DHFOFLFCLAH == nil {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_NOT_IN_GAMEPLAY)
return nil
}
if revision != s.currencyWarsRevision {
return nil
}
run := data.DHFOFLFCLAH
if run.FMIGOKIDKJL != nil || len(req.AAKDHMHCMLP) == 0 {
PacketLog("CW_UPDATE_POS rejected reason=not_preparing_or_empty")
return nil
}
player, team, _ := cwComponents(run)
if player == nil || team == nil {
return nil
}
front, err := catalog.IntegerRule("GridFight_Front_AvatarMaxNum")
if err != nil {
return err
}
back, err := catalog.IntegerRule("GridFight_Back_AvatarMaxNum")
if err != nil {
return err
}
activeEnd := uint64(front + back)
overflow, err := catalog.IntegerRule("GridFight_Bench_OverFlow_AvatarNum")
if err != nil {
return err
}
benchEnd := activeEnd + uint64(player.MICEOJBAKGN) + uint64(overflow)
changes := map[uint32]uint32{}
for _, v := range req.AAKDHMHCMLP {
if v == nil || v.UniqueId == 0 {
return nil
}
for _, forge := range team.KCOMPKMJCMC {
pos := forge.Pos
if _, changed := changes[forge.UniqueId]; changed {
return nil
} // Projection movement needs its separately verified sync arm.
if pos != 0 {
if occupied[pos] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_POS_EXIST_ROLE)
return nil
}
occupied[pos] = true
}
if _, ok := changes[v.UniqueId]; ok {
return nil
}
for _, forge := range team.PMEOPPDAMLM {
owned[forge.UniqueId] = true
pos := forge.Pos
if v, ok := changes[forge.UniqueId]; ok {
pos = v
}
if pos == 0 || uint64(pos) <= activeEnd || uint64(pos) > benchEnd {
return nil
}
if uint64(v.Pos) > benchEnd {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_POS_NOT_VALID)
return nil
}
changes[v.UniqueId] = v.Pos
}
// Validate final positions together, so swaps need no intermediate empty slot.
occupied := map[uint32]bool{}
owned := map[uint32]bool{}
active := uint32(0)
activeAvatars := map[uint32]bool{}
for _, role := range team.GridGameRoleList {
owned[role.UniqueId] = true
pos := role.Pos
if v, ok := changes[role.UniqueId]; ok {
pos = v
}
if pos != 0 {
if occupied[pos] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_POS_EXIST_ROLE)
return nil
}
occupied[pos] = true
}
for id := range changes {
if !owned[id] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_ROLE_NOT_EXIST)
if pos != 0 && uint64(pos) <= activeEnd {
config, err := catalog.CapturedRoleVariant(role.Id, role.RoleStar)
if err != nil {
return err
}
if activeAvatars[config.Config.AvatarID] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_SAME_ROLE_IN_BATTLE)
return nil
}
activeAvatars[config.Config.AvatarID] = true
active++
}
if active > player.AFFCFLFIFPH {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_REGION_FULL)
}
for _, forge := range team.KCOMPKMJCMC {
pos := forge.Pos
if _, changed := changes[forge.UniqueId]; changed {
return nil
} // Projection movement needs its separately verified sync arm.
if pos != 0 {
if occupied[pos] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_POS_EXIST_ROLE)
return nil
}
occupied[pos] = true
}
}
for _, forge := range team.PMEOPPDAMLM {
owned[forge.UniqueId] = true
pos := forge.Pos
if v, ok := changes[forge.UniqueId]; ok {
pos = v
}
if pos == 0 || uint64(pos) <= activeEnd || uint64(pos) > benchEnd {
return nil
}
var updates []*pb.KFNCAIHIHCM
for _, role := range team.GridGameRoleList {
if pos, ok := changes[role.UniqueId]; ok {
role.Pos = pos
updates = append(updates, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_CFOCDALNCBP{CFOCDALNCBP: role}})
}
if occupied[pos] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_POS_EXIST_ROLE)
return nil
}
for _, forge := range team.PMEOPPDAMLM {
if pos, ok := changes[forge.UniqueId]; ok {
forge.Pos = pos
updates = append(updates, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_FEKCPKAPHHO{FEKCPKAPHHO: forge}})
}
occupied[pos] = true
}
for id := range changes {
if !owned[id] {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_ROLE_NOT_EXIST)
return nil
}
runtime, _ := cwBonfireRuntime(catalog, run)
cwSyncTraitComponent(catalog, run, runtime)
if tbCat, err := common.LoadCurrencyWarsTrailblaze(); err == nil && (cwGetTrailblazeLayer(catalog, run) > 0 || (runtime != nil && runtime.Rules["TbOpened"] != 0)) {
updates = append(updates,
&pb.KFNCAIHIHCM{
MNLCHEJKDHO: &pb.KFNCAIHIHCM_HKHJJCMKKJE{
HKHJJCMKKJE: &pb.JLKCBOIFJKC{
TraitId: cwTrailblazeTrait,
ENPLACDLJGA: cwTrailblazeTraitEffectProto(tbCat, runtime),
},
}
if active > player.AFFCFLFIFPH {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_REGION_FULL)
return nil
}
var updates []*pb.KFNCAIHIHCM
for _, role := range team.GridGameRoleList {
if pos, ok := changes[role.UniqueId]; ok {
role.Pos = pos
updates = append(updates, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_CFOCDALNCBP{CFOCDALNCBP: role}})
}
}
for _, forge := range team.PMEOPPDAMLM {
if pos, ok := changes[forge.UniqueId]; ok {
forge.Pos = pos
updates = append(updates, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_FEKCPKAPHHO{FEKCPKAPHHO: forge}})
}
}
runtime, err := cwBonfireRuntime(catalog, run)
if err != nil {
return err
}
cwSyncTraitComponent(catalog, run, runtime)
if tbCat, err := common.LoadCurrencyWarsTrailblaze(); err == nil && (cwGetTrailblazeLayer(catalog, run) > 0 || (runtime != nil && runtime.Rules["TbOpened"] != 0)) {
updates = append(updates,
&pb.KFNCAIHIHCM{
MNLCHEJKDHO: &pb.KFNCAIHIHCM_HKHJJCMKKJE{
HKHJJCMKKJE: &pb.JLKCBOIFJKC{
TraitId: cwTrailblazeTrait,
ENPLACDLJGA: cwTrailblazeTraitEffectProto(tbCat, runtime),
},
},
&pb.KFNCAIHIHCM{
MNLCHEJKDHO: &pb.KFNCAIHIHCM_GOOCHGKBIPN{
GOOCHGKBIPN: cwTrailblazeModifierProto(tbCat, runtime),
},
},
&pb.KFNCAIHIHCM{
MNLCHEJKDHO: &pb.KFNCAIHIHCM_GOOCHGKBIPN{
GOOCHGKBIPN: cwTrailblazeModifierProto(tbCat, runtime),
},
)
}
if err = s.saveCurrencyWarsDataWithRuntime(data, catalog, runtime); err != nil {
return err
}
if err = cwSend(s, cwNotify(updates...)); err != nil {
return err
}
rsp.AAKDHMHCMLP = req.AAKDHMHCMLP
rsp.Retcode = 0
PacketLog(fmt.Sprintf("CW_UPDATE_POS saved count=%d active=%d revision=%d", len(changes), active, s.currencyWarsRevision))
return nil
})
// CmdID 8543 (GEBBMDCOLCH) -> ScRsp 8630 (ALNFEBKHCFC)
// Protocol revision: 4.6.53 (upstream_cmdids.go).
// Assumption: Native Client sends GEBBMDCOLCH during preparation / round transition to sync
// the batch formation role positions (CONJCEMAHGN: MDNAGANGNKK = role UID, JFCJNHNBMCB = pos).
// Acknowledging with retcode 0 unblocks the Client UI input controller.
cwHandleNamed("GEBBMDCOLCH", "ALNFEBKHCFC", func(s *Session, req *pb.GEBBMDCOLCH, rsp *pb.ALNFEBKHCFC) error {
catalog, err := common.LoadCurrencyWarsCatalog()
if err != nil {
rsp.Retcode = 0
return nil
}
data, _, err := readCurrencyWarsData(catalog)
if err != nil || data == nil || data.DHFOFLFCLAH == nil {
rsp.Retcode = 0
return nil
}
run := data.DHFOFLFCLAH
_, team, _ := cwComponents(run)
if team != nil && len(req.CONJCEMAHGN) > 0 {
posMap := make(map[uint32]uint32, len(req.CONJCEMAHGN))
for _, entry := range req.CONJCEMAHGN {
if entry != nil && entry.MDNAGANGNKK != 0 {
posMap[entry.MDNAGANGNKK] = entry.JFCJNHNBMCB
}
}
changed := false
for _, role := range team.GridGameRoleList {
if newPos, ok := posMap[role.UniqueId]; ok && role.Pos != newPos {
role.Pos = newPos
changed = true
}
}
if changed {
runtime, _ := cwBonfireRuntime(catalog, run)
cwSyncTraitComponent(catalog, run, runtime)
_ = s.saveCurrencyWarsDataWithRuntime(data, catalog, runtime)
}
}
rsp.Retcode = 0
PacketLog(fmt.Sprintf("CW_BATCH_UPDATE_POS count=%d revision=%d", len(req.CONJCEMAHGN), s.currencyWarsRevision))
return nil
})
},
)
}
if err = s.saveCurrencyWarsDataWithRuntime(data, catalog, runtime); err != nil {
return err
}
if err = cwSend(s, cwNotify(updates...)); err != nil {
return err
}
rsp.AAKDHMHCMLP = req.AAKDHMHCMLP
rsp.Retcode = 0
PacketLog(fmt.Sprintf("CW_UPDATE_POS saved count=%d active=%d revision=%d", len(changes), active, s.currencyWarsRevision))
return nil
}