2026-10-21 20:40:13 +07:00
|
|
|
package gameserver
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"gac-hkrpg-go/internal/common"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (s *Session) applyCurrencyWarsCommands() error {
|
|
|
|
|
if _, active := CURRENT_BATTLE.Load(s.Token); active {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
commands, err := common.ReadCurrencyWarsCommands()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if len(commands) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
cfg, err := common.LoadRuntimeConfig()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-11-04 15:59:48 +07:00
|
|
|
// A pending grant can be blocked by a stuck page. Give the explicit quit
|
|
|
|
|
// request priority so it can recover that run without applying the grant.
|
|
|
|
|
for _, command := range commands {
|
|
|
|
|
if command.UID == cfg.PlayerUID && command.Field == common.CurrencyWarsCommandEnd {
|
|
|
|
|
return s.applyCurrencyWarsEnd(command)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-21 20:40:13 +07:00
|
|
|
for _, command := range commands {
|
|
|
|
|
if command.UID != cfg.PlayerUID {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2026-11-04 15:59:48 +07:00
|
|
|
if !s.currencyWarsOpen {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2026-10-21 20:40:13 +07:00
|
|
|
catalog, data, err := cwPreparingData(s)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if command.RunID != data.DHFOFLFCLAH.UniqueId {
|
|
|
|
|
if err = common.AcknowledgeCurrencyWarsCommand(command.ID); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
PacketLog("CW_GM discarded command for a finished run")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
player, _, _ := cwComponents(data.DHFOFLFCLAH)
|
|
|
|
|
if player == nil {
|
|
|
|
|
return fmt.Errorf("CW player missing")
|
|
|
|
|
}
|
2026-10-31 20:47:36 +07:00
|
|
|
runtime, _ := cwBonfireRuntime(catalog, data.DHFOFLFCLAH)
|
|
|
|
|
notify, applyErr := cwApplyCommand(catalog, data.DHFOFLFCLAH, command, runtime)
|
2026-10-28 18:54:43 +07:00
|
|
|
if applyErr != nil {
|
|
|
|
|
PacketLog(fmt.Sprintf("CW_GM rejected request=%s: %v", command.ID, applyErr))
|
|
|
|
|
if err = common.AcknowledgeCurrencyWarsCommand(command.ID); err != nil {
|
|
|
|
|
return err
|
2026-10-21 20:40:13 +07:00
|
|
|
}
|
2026-10-28 18:54:43 +07:00
|
|
|
continue
|
2026-10-21 20:40:13 +07:00
|
|
|
}
|
2026-10-28 18:54:43 +07:00
|
|
|
|
2026-10-31 20:47:36 +07:00
|
|
|
if err = s.saveCurrencyWarsDataWithRuntime(data, catalog, runtime, command.ID); err != nil {
|
2026-10-21 20:40:13 +07:00
|
|
|
return err
|
|
|
|
|
}
|
2026-10-28 18:54:43 +07:00
|
|
|
if err = s.Send(notify); err != nil {
|
2026-10-21 20:40:13 +07:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
PacketLog(fmt.Sprintf("CW_GM applied request=%s field=%s value=%d revision=%d", command.ID, command.Field, command.Value, s.currencyWarsRevision))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2026-11-04 15:59:48 +07:00
|
|
|
|
|
|
|
|
// End is allowed even with a pending choice so it can recover a stuck CW UI.
|
|
|
|
|
// Do not end a battle or replace a newer session's saved data. Queue removal,
|
|
|
|
|
// archive and quit settlement share the same persistent CAS transaction.
|
|
|
|
|
func (s *Session) applyCurrencyWarsEnd(command common.CurrencyWarsCommand) error {
|
|
|
|
|
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 || data.DHFOFLFCLAH.UniqueId != command.RunID {
|
|
|
|
|
if err := common.AcknowledgeCurrencyWarsCommand(command.ID); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
PacketLog(fmt.Sprintf("CW_GM discarded end request=%s: run already ended or changed", command.ID))
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if revision != s.currencyWarsRevision {
|
|
|
|
|
// If this session has not opened CW, it holds no CW UI state. Adopt the
|
|
|
|
|
// freshly read revision; the CAS still rejects any concurrent changes.
|
|
|
|
|
if s.currencyWarsOpen {
|
|
|
|
|
return common.ErrCurrencyWarsSaveConflict
|
|
|
|
|
}
|
|
|
|
|
s.currencyWarsRevision = revision
|
|
|
|
|
}
|
|
|
|
|
keep, settle, err := currencyWarsQuitSettlement(catalog, data, data.DHFOFLFCLAH)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
data.DHFOFLFCLAH = nil
|
|
|
|
|
if err := s.saveCurrencyWarsTerminal(data, catalog, settle.BFLLJJBHLDF, command.ID); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
wasOpen := s.currencyWarsOpen
|
|
|
|
|
s.currencyWarsOpen = false
|
|
|
|
|
if wasOpen {
|
|
|
|
|
if err := cwSend(s, keep); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := cwSend(s, settle); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PacketLog(fmt.Sprintf("CW_GM applied end request=%s run=%d revision=%d result=quit", command.ID, command.RunID, s.currencyWarsRevision))
|
|
|
|
|
return nil
|
|
|
|
|
}
|