Files
90CNServer/server/internal/services/dnfbridge/gameplay_module.go
T
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

198 lines
5.1 KiB
Go

package dnfbridge
import (
"errors"
"fmt"
dnfproto "longheng.io/server/internal/modules/dnf/protocol"
)
var (
errGameplayModuleNameRequired = errors.New("gameplay module name is required")
errGameplayModuleDuplicate = errors.New("gameplay module route is duplicated")
)
type gameplayRequest struct {
Opcode uint16
Classification byte
Body []byte
}
type gameplayHandler func(*Service, *gameSession, gameplayRequest) error
type gameplayLegacyNormalizer func([]byte) []byte
type gameplayModuleDefinition struct {
Name string
LegacyHandlers map[uint16]gameplayHandler
UpperHandlers map[uint16]gameplayHandler
LegacyNormalizers map[uint16]gameplayLegacyNormalizer
}
type registeredGameplayHandler struct {
module string
handler gameplayHandler
}
type registeredGameplayNormalizer struct {
module string
normalize gameplayLegacyNormalizer
}
type gameplayModuleRegistry struct {
names []string
legacyHandlers map[uint16]registeredGameplayHandler
upperHandlers map[uint16]registeredGameplayHandler
legacyNormalizers map[uint16]registeredGameplayNormalizer
}
func newGameplayModuleRegistry(modules ...gameplayModuleDefinition) (*gameplayModuleRegistry, error) {
registry := &gameplayModuleRegistry{
legacyHandlers: make(map[uint16]registeredGameplayHandler),
upperHandlers: make(map[uint16]registeredGameplayHandler),
legacyNormalizers: make(map[uint16]registeredGameplayNormalizer),
}
seenNames := make(map[string]struct{}, len(modules))
for _, module := range modules {
if module.Name == "" {
return nil, errGameplayModuleNameRequired
}
if _, exists := seenNames[module.Name]; exists {
return nil, fmt.Errorf("%w: module=%s", errGameplayModuleDuplicate, module.Name)
}
seenNames[module.Name] = struct{}{}
registry.names = append(registry.names, module.Name)
for opcode, handler := range module.LegacyHandlers {
if handler == nil {
continue
}
if previous, exists := registry.legacyHandlers[opcode]; exists {
return nil, fmt.Errorf(
"%w: transport=legacy opcode=%d modules=%s,%s",
errGameplayModuleDuplicate,
opcode,
previous.module,
module.Name,
)
}
registry.legacyHandlers[opcode] = registeredGameplayHandler{module: module.Name, handler: handler}
}
for opcode, handler := range module.UpperHandlers {
if handler == nil {
continue
}
if previous, exists := registry.upperHandlers[opcode]; exists {
return nil, fmt.Errorf(
"%w: transport=upper opcode=%d modules=%s,%s",
errGameplayModuleDuplicate,
opcode,
previous.module,
module.Name,
)
}
registry.upperHandlers[opcode] = registeredGameplayHandler{module: module.Name, handler: handler}
}
for opcode, normalize := range module.LegacyNormalizers {
if normalize == nil {
continue
}
if previous, exists := registry.legacyNormalizers[opcode]; exists {
return nil, fmt.Errorf(
"%w: transport=legacy-normalizer opcode=%d modules=%s,%s",
errGameplayModuleDuplicate,
opcode,
previous.module,
module.Name,
)
}
registry.legacyNormalizers[opcode] = registeredGameplayNormalizer{module: module.Name, normalize: normalize}
}
}
return registry, nil
}
func mustGameplayModuleRegistry(modules ...gameplayModuleDefinition) *gameplayModuleRegistry {
registry, err := newGameplayModuleRegistry(modules...)
if err != nil {
panic(err)
}
return registry
}
func (r *gameplayModuleRegistry) DispatchLegacy(
service *Service,
session *gameSession,
opcode uint16,
body []byte,
) (bool, error) {
if r == nil {
return false, nil
}
route, ok := r.legacyHandlers[opcode]
if !ok {
return false, nil
}
return true, route.handler(service, session, gameplayRequest{Opcode: opcode, Body: body})
}
func (r *gameplayModuleRegistry) DispatchUpper(
service *Service,
session *gameSession,
opcode uint16,
classification byte,
body []byte,
) (bool, error) {
if r == nil {
return false, nil
}
route, ok := r.upperHandlers[opcode]
if !ok {
return false, nil
}
return true, route.handler(service, session, gameplayRequest{
Opcode: opcode,
Classification: classification,
Body: body,
})
}
func (r *gameplayModuleRegistry) NormalizeLegacy(opcode uint16, body []byte) ([]byte, bool) {
if r == nil {
return body, false
}
route, ok := r.legacyNormalizers[opcode]
if !ok {
return body, false
}
return route.normalize(body), true
}
func requireDefaultGameplayClassification(
service *Service,
session *gameSession,
request gameplayRequest,
event string,
reason string,
) bool {
if request.Classification == dnfproto.DefaultChannelClassification {
return true
}
service.logGameEvent(session, event,
"classification", request.Classification,
"body_len", len(request.Body),
"reason", reason)
return false
}
func defaultClassGameplayHandler(
event string,
reason string,
handler func(*Service, *gameSession, []byte) error,
) gameplayHandler {
return func(service *Service, session *gameSession, request gameplayRequest) error {
if !requireDefaultGameplayClassification(service, session, request, event, reason) {
return nil
}
return handler(service, session, request.Body)
}
}