DNF90 服务端与启动器完整源码(含 GM 工具功能增强)

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

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

No files matched your search

@@ -0,0 +1,205 @@
package runtimeguard
import (
"errors"
"sort"
"strings"
"sync"
)
// 历史背景:本包之前只暴露 Check / CheckDescriber 这一对无状态函数;wiring 层
// 要为每一个 runtime 模块(matchmaker、party、crossserver、未来还会更多)手动
// 调用一次,谁漏调用谁就静默逃过门禁。
//
// 全局清单把"声明 — 校验"分成两步:
// 1. 各 runtime 模块构造完成后立即调用 Register / RegisterDescriber,
// 把模块名 + 后端类型挂进进程内全局清单。
// 2. wiring 收尾前调用 Verify(profile) 一次性核对所有声明,违规项被合并
// 成一个 multi-error 抛出,main 看到 error != nil 直接停机。
//
// 这样的好处:
// - 加新的 runtime 模块不再需要 wiring 代码同步加一行 Check;
// - 多个违规会一次性报出来,而不是修一条复活一条;
// - 单元测试可以用 Reset 清空全局状态,避免污染。
// Registration 记录一次注册。Source 是可选的调用方标识(比如 main 的 file:line),
// 主要用来在 Verify 失败时给 ops 一个"到底是谁声明的"的线索。
type Registration struct {
Module string
Backend Backend
Source string
}
type Catalog struct {
mu sync.RWMutex
entries []Registration
}
func NewCatalog() *Catalog {
return &Catalog{}
}
var defaultCatalog = NewCatalog()
// Register 把 module + backend 加进全局清单。生产代码调用,参数静态可见。
// 重复注册(同 module)允许,最后一个生效;这样在 hot-reload 或重新装配场景
// 下不会泄漏过时声明。
func Register(module string, backend Backend) {
defaultCatalog.Register(module, backend)
}
// RegisterDescriber 是 Register 的便捷形式。describer 为 nil 视为 BackendUnknown。
func RegisterDescriber(module string, describer BackendDescriber) {
defaultCatalog.RegisterDescriber(module, describer)
}
func (inv *Catalog) Register(module string, backend Backend) {
if inv == nil {
return
}
inv.registerInternal(Registration{Module: normalizeModule(module), Backend: backend})
}
func (inv *Catalog) RegisterDescriber(module string, describer BackendDescriber) {
var b Backend
if describer != nil {
b = describer.BackendKind()
}
inv.Register(module, b)
}
// RegisterFromSource 与 Register 相同,但额外记录 caller 信息。
// 推荐 wiring 层使用 "package.Function" 或文件路径作为 source。
func RegisterFromSource(module string, backend Backend, source string) {
defaultCatalog.RegisterFromSource(module, backend, source)
}
func (inv *Catalog) RegisterFromSource(module string, backend Backend, source string) {
if inv == nil {
return
}
inv.registerInternal(Registration{
Module: normalizeModule(module),
Backend: backend,
Source: strings.TrimSpace(source),
})
}
func (inv *Catalog) registerInternal(reg Registration) {
if reg.Module == "" {
return
}
inv.mu.Lock()
defer inv.mu.Unlock()
for i := range inv.entries {
if inv.entries[i].Module == reg.Module {
inv.entries[i] = reg
return
}
}
inv.entries = append(inv.entries, reg)
}
// Snapshot 返回当前清单的副本,按 module 名排序,便于测试和 admin 报告。
func Snapshot() []Registration {
return defaultCatalog.Snapshot()
}
func (inv *Catalog) Snapshot() []Registration {
if inv == nil {
return nil
}
inv.mu.RLock()
out := make([]Registration, len(inv.entries))
copy(out, inv.entries)
inv.mu.RUnlock()
sort.Slice(out, func(i, j int) bool { return out[i].Module < out[j].Module })
return out
}
// Verify 对清单里每条注册都跑一次 Check。所有违规聚合成 errors.Join,
// 调用方可以用 errors.Is(err, ErrMemoryDistProfile) 判断是否有
// 至少一个内存后端被拒。
//
// 调用方应在装配完成、http server / bus subscribe 启动之前调用一次。
func Verify(profile Profile) error {
return defaultCatalog.Verify(profile)
}
func (inv *Catalog) Verify(profile Profile) error {
snapshot := inv.Snapshot()
var errs []error
for _, reg := range snapshot {
if err := Check(reg.Module, reg.Backend, profile); err != nil {
if reg.Source != "" {
err = wrapWithSource(err, reg.Source)
}
errs = append(errs, err)
}
}
if len(errs) == 0 {
return nil
}
return errors.Join(errs...)
}
// Reset 清空清单。仅用于测试 / hot-reload。生产代码不应该调用。
func Reset() {
defaultCatalog.Reset()
}
func (inv *Catalog) Reset() {
if inv == nil {
return
}
inv.mu.Lock()
inv.entries = nil
inv.mu.Unlock()
}
// Has 返回 module 是否已注册。便于测试断言。
func Has(module string) bool {
return defaultCatalog.Has(module)
}
func (inv *Catalog) Has(module string) bool {
if inv == nil {
return false
}
module = normalizeModule(module)
inv.mu.RLock()
defer inv.mu.RUnlock()
for _, r := range inv.entries {
if r.Module == module {
return true
}
}
return false
}
func normalizeModule(name string) string {
return strings.TrimSpace(name)
}
// sourceWrappedError 让 errors.Is 仍能命中底层 sentinel,同时携带 Source。
type sourceWrappedError struct {
inner error
source string
}
func (e *sourceWrappedError) Error() string {
if e.source == "" {
return e.inner.Error()
}
return e.inner.Error() + " (registered at " + e.source + ")"
}
func (e *sourceWrappedError) Unwrap() error { return e.inner }
func wrapWithSource(inner error, source string) error {
source = strings.TrimSpace(source)
if inner == nil || source == "" {
return inner
}
return &sourceWrappedError{inner: inner, source: source}
}