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

128 lines
3.0 KiB
Go

package bus
import (
"context"
"encoding/json"
"errors"
"strings"
"longheng.io/server/internal/platform/metrics"
)
func isContextCanceled(err error) bool { return errors.Is(err, context.Canceled) }
func isContextDeadline(err error) bool { return errors.Is(err, context.DeadlineExceeded) }
const (
MetricPublishTotal = "bus_publish_total"
MetricPublishBytes = "bus_publish_bytes_total"
MetricSubscribeTotal = "bus_subscribe_total"
MetricHandlerTotal = "bus_handler_total"
MetricSubscriptions = "bus_subscriptions"
ResultOK = "ok"
ResultError = "error"
ResultClosed = "closed"
ResultPanic = "panic"
ResultTimeout = "timeout"
ResultCancel = "canceled"
)
// Metrics 是各个 bus adapter 在热路径上调用的轻量指标层。
// nil receiver 是 no-op,因此即使指标 wiring 关闭,调用方也可以保留
// `m.recordX(...)`,无需到处写 nil 检查。
type Metrics struct {
reg *metrics.Registry
kind string
}
// NewMetrics 返回绑定 registry 和 adapter kind 标签("memory" / "redis" / "nats")的指标发射器。
// 任一参数为空时返回 nil,让禁用指标的 wiring 保持真正的 no-op。
func NewMetrics(reg *metrics.Registry, kind string) *Metrics {
kind = strings.TrimSpace(kind)
if reg == nil || kind == "" {
return nil
}
return &Metrics{reg: reg, kind: kind}
}
func (m *Metrics) recordPublish(result string) {
if m == nil {
return
}
m.reg.Inc(MetricPublishTotal, m.labels(result))
}
func (m *Metrics) recordPublishBytes(size int64) {
if m == nil || size <= 0 {
return
}
m.reg.Add(MetricPublishBytes, m.kindOnlyLabels(), size)
}
func (m *Metrics) recordSubscribe(result string) {
if m == nil {
return
}
m.reg.Inc(MetricSubscribeTotal, m.labels(result))
}
func (m *Metrics) recordHandler(result string) {
if m == nil {
return
}
m.reg.Inc(MetricHandlerTotal, m.labels(result))
}
func (m *Metrics) trackSubscription(delta int64) {
if m == nil {
return
}
m.reg.AddGauge(MetricSubscriptions, m.kindOnlyLabels(), delta)
}
func (m *Metrics) labels(result string) map[string]string {
return map[string]string{
"kind": m.kind,
"result": result,
}
}
func (m *Metrics) kindOnlyLabels() map[string]string {
return map[string]string{"kind": m.kind}
}
// classifyHandlerError 返回单次 handler 投递结果对应的 bus_handler_total result 标签。
// context.Canceled 归类为 "canceled" 而不是 "error",避免调用方取消拉高错误率。
func classifyHandlerError(err error) string {
if err == nil {
return ResultOK
}
switch {
case isContextCanceled(err):
return ResultCancel
case isContextDeadline(err):
return ResultTimeout
default:
return ResultError
}
}
func payloadSize(payload any) int64 {
switch value := payload.(type) {
case nil:
return 0
case []byte:
return int64(len(value))
case json.RawMessage:
return int64(len(value))
case string:
return int64(len(value))
default:
raw, err := json.Marshal(value)
if err != nil {
return 0
}
return int64(len(raw))
}
}