Files
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

175 lines
6.5 KiB
Go

package hotupdate
import (
"fmt"
"strings"
"time"
)
const RolloutEvidenceKind = "hotupdate_rollout_evidence"
type RolloutEvidence struct {
SchemaVersion int `json:"schema_version"`
Kind string `json:"kind"`
Name string `json:"name"`
GeneratedAt time.Time `json:"generated_at"`
Spec RolloutSpec `json:"spec"`
Report RolloutReport `json:"report"`
Result string `json:"result"`
Valid bool `json:"valid"`
Violations []string `json:"violations,omitempty"`
}
func BuildRolloutEvidence(name string, spec RolloutSpec, control Control, generatedAt time.Time) (RolloutEvidence, error) {
if strings.TrimSpace(name) == "" {
name = "hotupdate-rollout"
}
normalized, err := normalizeRolloutSpec(spec, false)
if err != nil {
return RolloutEvidence{}, err
}
report, err := EvaluateRollout(control, normalized)
if err != nil {
return RolloutEvidence{}, err
}
if generatedAt.IsZero() {
generatedAt = time.Now().UTC()
}
violations := ValidateRolloutReport(report)
return RolloutEvidence{
SchemaVersion: 1,
Kind: RolloutEvidenceKind,
Name: strings.TrimSpace(name),
GeneratedAt: generatedAt.UTC(),
Spec: normalized,
Report: report,
Result: rolloutEvResult(report),
Valid: len(violations) == 0,
Violations: violations,
}, nil
}
func ValidateRolloutEvidence(evidence RolloutEvidence) error {
var violations []string
if evidence.SchemaVersion != 1 {
violations = append(violations, fmt.Sprintf("schema_version must be 1, got %d", evidence.SchemaVersion))
}
if evidence.Kind != RolloutEvidenceKind {
violations = append(violations, fmt.Sprintf("kind must be %q, got %q", RolloutEvidenceKind, evidence.Kind))
}
if strings.TrimSpace(evidence.Name) == "" {
violations = append(violations, "name is required")
}
if evidence.GeneratedAt.IsZero() {
violations = append(violations, "generated_at is required")
}
violations = append(violations, ValidateRolloutReport(evidence.Report)...)
if len(evidence.Violations) > 0 {
violations = append(violations, evidence.Violations...)
}
if len(violations) > 0 {
return fmt.Errorf("hotupdate rollout evidence invalid: %s", strings.Join(violations, "; "))
}
if !evidence.Valid {
return fmt.Errorf("hotupdate rollout evidence invalid: valid=false")
}
return nil
}
func ValidateRolloutReport(report RolloutReport) []string {
var violations []string
if strings.TrimSpace(report.Target) == "" {
violations = append(violations, "target is required")
}
if strings.TrimSpace(report.Version) == "" {
violations = append(violations, "version is required")
}
if report.BatchSize <= 0 {
violations = append(violations, "batch_size must be positive")
}
if report.Total < 0 {
violations = append(violations, "total must not be negative")
}
if report.Published > report.Total {
violations = append(violations, "published must not exceed total")
}
if report.ApplyPublished > report.Published {
violations = append(violations, "apply_published must not exceed published")
}
if report.RollbackPublished > report.Published {
violations = append(violations, "rollback_published must not exceed published")
}
if report.Unpublished+report.Published != report.Total {
violations = append(violations, "unpublished + published must equal total")
}
if report.Pending+report.InProgress+report.Applied+report.Failed+report.Restored > report.Total {
violations = append(violations, "node terminal/in-flight counts exceed total")
}
if report.Complete && (report.Applied != report.Total || report.Failed != 0 || report.NeedsRollback) {
violations = append(violations, "complete report must have every node applied and no rollback need")
}
if report.RollbackComplete && (report.RollbackPublished == 0 || report.Restored != report.RollbackPublished || report.NeedsRollback) {
violations = append(violations, "rollback_complete report must have every rollback intent restored and no rollback need")
}
var sumTotal, sumPublished, sumApplyPublished, sumRollbackPublished, sumUnpublished int
var sumPending, sumInProgress, sumApplied, sumFailed, sumRestored int
for _, batch := range report.Batches {
batchTotal := len(batch.NodeIDs)
sumTotal += batchTotal
sumPublished += batch.Published
sumApplyPublished += batch.ApplyPublished
sumRollbackPublished += batch.RollbackPublished
sumUnpublished += batch.Unpublished
sumPending += batch.Pending
sumInProgress += batch.InProgress
sumApplied += batch.Applied
sumFailed += batch.Failed
sumRestored += batch.Restored
if len(batch.Nodes) != batchTotal {
violations = append(violations, fmt.Sprintf("batch %d nodes length does not match node_ids", batch.Index))
}
if batch.Published > batchTotal {
violations = append(violations, fmt.Sprintf("batch %d published exceeds node count", batch.Index))
}
if batch.Unpublished+batch.Published != batchTotal {
violations = append(violations, fmt.Sprintf("batch %d unpublished + published must equal node count", batch.Index))
}
if batch.Complete && batch.Applied != batchTotal {
violations = append(violations, fmt.Sprintf("batch %d complete requires every node applied", batch.Index))
}
if batch.RollbackComplete && (batch.RollbackPublished == 0 || batch.Restored != batch.RollbackPublished) {
violations = append(violations, fmt.Sprintf("batch %d rollback_complete requires every rollback intent restored", batch.Index))
}
}
if sumTotal != report.Total {
violations = append(violations, "batch node count sum must equal total")
}
if sumPublished != report.Published || sumApplyPublished != report.ApplyPublished || sumRollbackPublished != report.RollbackPublished {
violations = append(violations, "batch published counts must match report totals")
}
if sumUnpublished != report.Unpublished || sumPending != report.Pending || sumInProgress != report.InProgress {
violations = append(violations, "batch in-flight counts must match report totals")
}
if sumApplied != report.Applied || sumFailed != report.Failed || sumRestored != report.Restored {
violations = append(violations, "batch terminal counts must match report totals")
}
return violations
}
func rolloutEvResult(report RolloutReport) string {
switch {
case report.Complete:
return "complete"
case report.RollbackComplete:
return "rollback_complete"
case report.NeedsRollback:
return "rollback_required"
case report.InProgress > 0:
return "in_progress"
case report.Pending > 0 || report.Unpublished > 0:
return "pending"
default:
return "unknown"
}
}