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

381 lines
15 KiB
Go

package servergroup
import "context"
const (
// MergeModuleNameProfile 是角色档案合服模块名。
MergeModuleNameProfile = "profile"
// MergeModuleNameMail 是邮件合服模块名。
MergeModuleNameMail = "mail"
// MergeModuleNameLeaderboard 是排行榜合服模块名。
MergeModuleNameLeaderboard = "leaderboard"
// MergeModuleNameGuild 是公会合服模块名。
MergeModuleNameGuild = "guild"
// MergeModuleNameFriend 是好友合服模块名。
MergeModuleNameFriend = "friend"
// MergeModuleNameAuction 是拍卖行合服模块名。
MergeModuleNameAuction = "auction"
// MergeModuleNameActivity 是活动合服模块名。
MergeModuleNameActivity = "activity"
// MergeModuleNamePayment 是支付合服模块名。
MergeModuleNamePayment = "payment"
// MergeModuleNameServerInfo 是服务器信息合服模块名。
MergeModuleNameServerInfo = "server_merge_info"
)
const (
// MergeFeatureProfile 是角色档案合服功能域。
MergeFeatureProfile = "profile"
// MergeFeatureRoleName 是角色名冲突检查功能域。
MergeFeatureRoleName = "role_name"
// MergeFeaturePlayerMirror 是角色镜像数据功能域。
MergeFeaturePlayerMirror = "player_mirror"
// MergeFeatureAccountClean 是账号清理功能域。
MergeFeatureAccountClean = "account_clean"
// MergeFeatureMail 是邮件合服功能域。
MergeFeatureMail = "mail"
// MergeFeatureGlobalMail 是全服邮件合服功能域。
MergeFeatureGlobalMail = "global_mail"
// MergeFeatureLeaderboard 是排行榜合服功能域。
MergeFeatureLeaderboard = "leaderboard"
// MergeFeatureRank 是通用排名功能域。
MergeFeatureRank = "rank"
// MergeFeatureLocalRank 是本服排名功能域。
MergeFeatureLocalRank = "local_rank"
// MergeFeatureCrossRank 是跨服排名功能域。
MergeFeatureCrossRank = "cross_rank"
// MergeFeatureGuild 是公会合服功能域。
MergeFeatureGuild = "guild"
// MergeFeatureGuildName 是公会名冲突检查功能域。
MergeFeatureGuildName = "guild_name"
// MergeFeatureGuildRank 是公会排行功能域。
MergeFeatureGuildRank = "guild_rank"
// MergeFeatureGuildMirror 是公会镜像数据功能域。
MergeFeatureGuildMirror = "guild_mirror"
// MergeFeatureFriend 是好友合服功能域。
MergeFeatureFriend = "friend"
// MergeFeatureFriendRecharge 是好友充值关系功能域。
MergeFeatureFriendRecharge = "friend_recharge"
// MergeFeatureAuction 是拍卖行合服功能域。
MergeFeatureAuction = "auction"
// MergeFeatureWarzoneAuction 是战区拍卖行功能域。
MergeFeatureWarzoneAuction = "warzone_auction"
// MergeFeatureActivity 是活动合服功能域。
MergeFeatureActivity = "activity"
// MergeFeatureActivityState 是活动状态功能域。
MergeFeatureActivityState = "activity_state"
// MergeFeatureActivityRank 是活动排行功能域。
MergeFeatureActivityRank = "activity_rank"
// MergeFeaturePayment 是支付合服功能域。
MergeFeaturePayment = "payment"
// MergeFeatureOrder 是订单合服功能域。
MergeFeatureOrder = "order"
// MergeFeatureIAP 是内购回执合服功能域。
MergeFeatureIAP = "iap"
// MergeFeatureServerGroup 是服务器分组路由功能域。
MergeFeatureServerGroup = "servergroup"
// MergeFeatureServerInfo 是服务器信息合服功能域。
MergeFeatureServerInfo = "server_merge_info"
)
// MergeModuleHandlers 保存项目侧对一个默认合服模块的三段式处理器。
type MergeModuleHandlers struct {
// GenDB 负责合服前生成预检数据、冲突方案和 dry-run 证据。
GenDB MergeGenDBFunc
// Merge 负责合服窗口内的真实业务数据合并。
Merge MergeApplyFunc
// Rollback 负责合服失败后的业务回滚或补偿。
Rollback MergeRollbackFunc
}
// MergeModuleTemplate 描述从成熟项目合服对象中抽出的默认模块规格,不包含项目私有表结构。
type MergeModuleTemplate struct {
// Name 是默认模块名,和 MergeModuleSpec.Name 保持一致。
Name string `json:"name"`
// Description 是模块职责说明,用于报告和运营后台展示。
Description string `json:"description,omitempty"`
// Features 是该模块覆盖的合服 feature 别名集合。
Features []string `json:"features,omitempty"`
// Required 表示正式项目接入时该模块是否必须提供三段处理器。
Required bool `json:"required"`
// Checks 是项目侧实现至少要输出或验证的检查项。
Checks []string `json:"checks,omitempty"`
// Evidence 是项目侧实现应归档的证据类型说明。
Evidence []string `json:"evidence,omitempty"`
// Rollback 是项目侧实现应准备的回滚动作说明。
Rollback []string `json:"rollback,omitempty"`
}
// MergeModuleTemplates 返回龙恒默认合服模块模板,调用方可以安全修改返回值。
func MergeModuleTemplates() []MergeModuleTemplate {
return cloneMergeTemplates(defModuleTpls)
}
// FindMergeTemplate 按模块名查找默认合服模板。
func FindMergeTemplate(name string) (MergeModuleTemplate, bool) {
name = normalizeID(name)
for _, template := range defModuleTpls {
if template.Name == name {
return cloneMergeTemplate(template), true
}
}
return MergeModuleTemplate{}, false
}
// Spec 用项目侧处理器把模板转换为可注册的合服模块规格。
func (t MergeModuleTemplate) Spec(handlers MergeModuleHandlers) (MergeModuleSpec, error) {
template, err := normModuleTpl(t)
if err != nil {
return MergeModuleSpec{}, err
}
return normMergeSpec(MergeModuleSpec{
Name: template.Name,
Description: template.Description,
Features: template.Features,
Required: template.Required,
GenDB: handlers.GenDB,
Merge: handlers.Merge,
Rollback: handlers.Rollback,
})
}
// MergeModuleSpecs 返回带占位处理器的默认模块规格,占位处理器会在请求命中对应 feature 时阻断计划。
func MergeModuleSpecs() ([]MergeModuleSpec, error) {
templates := MergeModuleTemplates()
specs := make([]MergeModuleSpec, 0, len(templates))
for _, template := range templates {
spec, err := tmplPlaceholderSpec(template)
if err != nil {
return nil, err
}
specs = append(specs, spec)
}
return specs, nil
}
// BuildMergeModuleSpecs 用项目侧实现覆盖默认占位模块,未覆盖模块仍会在请求命中时阻断。
func BuildMergeModuleSpecs(overrides ...MergeModuleSpec) ([]MergeModuleSpec, error) {
defaults, err := MergeModuleSpecs()
if err != nil {
return nil, err
}
byName := make(map[string]MergeModuleSpec, len(defaults)+len(overrides))
names := make([]string, 0, len(defaults)+len(overrides))
for _, spec := range defaults {
byName[spec.Name] = spec
names = append(names, spec.Name)
}
for _, override := range overrides {
spec, err := templateOverrideSpec(override)
if err != nil {
return nil, err
}
if _, exists := byName[spec.Name]; !exists {
names = append(names, spec.Name)
}
byName[spec.Name] = spec
}
names = normalizeIDs(names)
specs := make([]MergeModuleSpec, 0, len(names))
for _, name := range names {
specs = append(specs, byName[name])
}
return specs, nil
}
// NewDefaultMergeRegistry 创建带默认模板保护的注册器,适合新项目接入期防止遗漏模块。
func NewDefaultMergeRegistry(overrides ...MergeModuleSpec) (*MergeOpRegistry, error) {
specs, err := BuildMergeModuleSpecs(overrides...)
if err != nil {
return nil, err
}
return NewMergeOpRegistry(specs...)
}
var defModuleTpls = []MergeModuleTemplate{
{
Name: MergeModuleNameProfile,
Description: "玩家档案、角色名、账号清理和玩家镜像合服模板",
Features: []string{MergeFeatureProfile, MergeFeatureRoleName, MergeFeaturePlayerMirror, MergeFeatureAccountClean},
Required: true,
Checks: []string{"角色名唯一键冲突", "账号绑定与玩家镜像完整性", "低活跃账号清理白名单", "Profile 抽样读写校验"},
Evidence: []string{"角色改名方案", "玩家镜像合并摘要", "账号清理名单", "Profile 抽样校验报告"},
Rollback: []string{"恢复角色名索引", "恢复玩家镜像键", "恢复被清理账号快照"},
},
{
Name: MergeModuleNameMail,
Description: "个人邮件、全服邮件和合服补偿邮件模板",
Features: []string{MergeFeatureMail, MergeFeatureGlobalMail},
Required: true,
Checks: []string{"未领取邮件迁移", "全服邮件过滤策略", "补偿邮件去重", "邮件附件幂等发放"},
Evidence: []string{"邮件迁移批次", "全服邮件保留清单", "附件发放校验摘要"},
Rollback: []string{"删除新服邮件批次", "恢复旧服邮件索引"},
},
{
Name: MergeModuleNameLeaderboard,
Description: "本地榜、跨服榜和排行榜补充信息合服模板",
Features: []string{MergeFeatureLeaderboard, MergeFeatureRank, MergeFeatureLocalRank, MergeFeatureCrossRank},
Required: true,
Checks: []string{"榜单排序稳定性", "同玩家多榜去重", "赛季结算窗口", "榜单补充信息一致性"},
Evidence: []string{"榜单重算摘要", "赛季窗口确认", "榜单抽样校验报告"},
Rollback: []string{"恢复旧榜快照", "撤销新榜重算批次"},
},
{
Name: MergeModuleNameGuild,
Description: "公会、公会名、公会镜像和公会排行榜合服模板",
Features: []string{MergeFeatureGuild, MergeFeatureGuildName, MergeFeatureGuildRank, MergeFeatureGuildMirror},
Required: true,
Checks: []string{"公会名唯一键冲突", "公会成员与会长关系", "公会镜像完整性", "公会活动结算窗口"},
Evidence: []string{"公会改名方案", "公会镜像合并摘要", "公会榜重算报告"},
Rollback: []string{"恢复公会名索引", "恢复公会镜像", "撤销公会榜重算"},
},
{
Name: MergeModuleNameFriend,
Description: "好友关系、黑名单和好友充值活动合服模板",
Features: []string{MergeFeatureFriend, MergeFeatureFriendRecharge},
Required: true,
Checks: []string{"好友双向边一致性", "黑名单优先级", "好友活动参与记录合并", "跨服好友引用"},
Evidence: []string{"好友边合并摘要", "好友活动合并批次"},
Rollback: []string{"恢复好友图谱快照", "撤销好友活动批次"},
},
{
Name: MergeModuleNameAuction,
Description: "拍卖、战区拍卖和流拍记录合服模板",
Features: []string{MergeFeatureAuction, MergeFeatureWarzoneAuction},
Required: true,
Checks: []string{"拍卖结算窗口", "竞价订单幂等", "流拍记录去重", "战区归属确认"},
Evidence: []string{"拍卖冻结确认", "流拍记录合并摘要", "订单幂等校验报告"},
Rollback: []string{"恢复拍卖快照", "撤销合服后竞价写入"},
},
{
Name: MergeModuleNameActivity,
Description: "活动状态、活动榜和运营活动过期数据合服模板",
Features: []string{MergeFeatureActivity, MergeFeatureActivityState, MergeFeatureActivityRank},
Required: true,
Checks: []string{"活动开放窗口", "活动状态去重", "活动榜结算", "奖励领取幂等"},
Evidence: []string{"活动状态合并摘要", "活动榜重算报告", "奖励领取抽样"},
Rollback: []string{"恢复活动状态快照", "撤销活动榜重算", "冻结异常奖励领取"},
},
{
Name: MergeModuleNamePayment,
Description: "支付订单、IAP 订单和补单复核合服模板",
Features: []string{MergeFeaturePayment, MergeFeatureOrder, MergeFeatureIAP},
Required: true,
Checks: []string{"订单唯一键冲突", "已发货订单幂等", "未完成订单冻结", "补单复核窗口"},
Evidence: []string{"订单迁移批次", "发货幂等校验", "补单复核清单"},
Rollback: []string{"恢复订单索引", "冻结合服窗口补单"},
},
{
Name: MergeModuleNameServerInfo,
Description: "区服合服信息、来源区服记录和路由元数据模板",
Features: []string{MergeFeatureServerGroup, MergeFeatureServerInfo},
Required: true,
Checks: []string{"来源区服记录", "目标区服版本", "路由元数据一致性", "合服批次号幂等"},
Evidence: []string{"合服批次记录", "来源区服映射", "路由元数据校验"},
Rollback: []string{"恢复合服信息快照", "恢复旧路由元数据"},
},
}
func tmplPlaceholderSpec(template MergeModuleTemplate) (MergeModuleSpec, error) {
normalized, err := normModuleTpl(template)
if err != nil {
return MergeModuleSpec{}, err
}
return normMergeSpec(MergeModuleSpec{
Name: normalized.Name,
Description: normalized.Description,
Features: normalized.Features,
Required: normalized.Required,
GenDB: tmplPlaceholderFunc(normalized, MergeModulePhaseGenDB),
Merge: tmplPlaceholderFunc(normalized, MergeModulePhaseMerge),
Rollback: tmplPlaceholderFunc(normalized, MergeModulePhaseRollback),
})
}
func templateOverrideSpec(override MergeModuleSpec) (MergeModuleSpec, error) {
name := normalizeID(override.Name)
if template, ok := FindMergeTemplate(name); ok {
if override.Description == "" {
override.Description = template.Description
}
if len(override.Features) == 0 {
override.Features = template.Features
}
override.Required = override.Required || template.Required
}
return normMergeSpec(override)
}
func tmplPlaceholderFunc(template MergeModuleTemplate, phase string) MergeModuleFunc {
template = cloneMergeTemplate(template)
phase = normalizeID(phase)
return func(ctx context.Context, input MergeModuleInput) (MergeModuleResult, error) {
if err := ctxErr(ctx); err != nil {
return MergeModuleResult{}, err
}
if !templateRequested(template, mergeInputReq(input)) {
return MergeModuleResult{Skipped: true}, nil
}
return MergeModuleResult{
Findings: []ConflictFinding{{
Code: "merge_module_template_unimplemented",
Severity: MigrationSeverityBlocker,
Subject: template.Name,
Detail: "默认合服模块 " + template.Name + " 尚未接入 " + phase + " 阶段项目处理器",
}},
Meta: map[string]string{
"template": "default",
"phase": phase,
},
}, nil
}
}
func templateRequested(template MergeModuleTemplate, request MergeRequest) bool {
features := requestedFeatures(request)
if len(features) == 0 {
return false
}
for _, feature := range template.Features {
if containsID(features, feature) {
return true
}
}
return false
}
func normModuleTpl(template MergeModuleTemplate) (MergeModuleTemplate, error) {
template.Name = normalizeID(template.Name)
template.Description = firstNonEmpty(template.Description)
template.Features = normalizeIDs(template.Features)
template.Checks = cloneMergeStrings(template.Checks)
template.Evidence = cloneMergeStrings(template.Evidence)
template.Rollback = cloneMergeStrings(template.Rollback)
if template.Name == "" {
return MergeModuleTemplate{}, ErrMergeModuleInvalid
}
if len(template.Features) == 0 {
return MergeModuleTemplate{}, ErrMergeModuleInvalid
}
return template, nil
}
func cloneMergeTemplates(templates []MergeModuleTemplate) []MergeModuleTemplate {
if len(templates) == 0 {
return nil
}
out := make([]MergeModuleTemplate, len(templates))
for i, template := range templates {
out[i] = cloneMergeTemplate(template)
}
return out
}
func cloneMergeTemplate(template MergeModuleTemplate) MergeModuleTemplate {
template.Features = append([]string(nil), template.Features...)
template.Checks = cloneMergeStrings(template.Checks)
template.Evidence = cloneMergeStrings(template.Evidence)
template.Rollback = cloneMergeStrings(template.Rollback)
return template
}