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

+163
View File
@@ -0,0 +1,163 @@
package rpc
import (
"context"
"errors"
"strings"
"longheng.io/server/internal/platform/metrics"
)
const (
// MetricRPCPending 记录当前等待响应的 RPC 调用数量。
MetricRPCPending = "rpc_pending"
// MetricRPCHandlers 记录当前已注册的 RPC handler 数量。
MetricRPCHandlers = "rpc_handlers_count"
// MetricRPCStarted 记录 endpoint 是否处于启动状态。
MetricRPCStarted = "rpc_started"
// MetricRPCCallsTotal 记录 RPC 客户端调用总数。
MetricRPCCallsTotal = "rpc_calls_total"
// MetricRPCHandlerCallsTotal 记录 RPC 服务端 handler 调用总数。
MetricRPCHandlerCallsTotal = "rpc_handler_calls_total"
)
// MetricsObserver 沿用 presence 的模式:bus / presence / rpc 都只暴露一个
// ObserveMetrics 方法,由平台 wiring 注册到 metrics.Registry,供 Prometheus scrape 统一 fanout。
type MetricsObserver interface {
ObserveMetrics(reg *metrics.Registry, labels map[string]string)
}
// ObserveMetrics 发布 rpc_pending(进行中的客户端调用 gauge)、rpc_handlers_count
// (已注册服务端 handler gauge)和 rpc_started(0/1)。
// 它只获取 endpoint RLock,可安全在 /metrics scrape goroutine 中调用。
func (e *Endpoint) ObserveMetrics(reg *metrics.Registry, labels map[string]string) {
if e == nil || reg == nil {
return
}
e.mu.RLock()
pending := int64(len(e.pending))
handlers := int64(len(e.handlers))
started := int64(0)
if e.started {
started = 1
}
e.mu.RUnlock()
tags := normObserverLabels(labels)
reg.SetGauge(MetricRPCPending, tags, pending)
reg.SetGauge(MetricRPCHandlers, tags, handlers)
reg.SetGauge(MetricRPCStarted, tags, started)
}
// SetMetrics 设置 endpoint 调用指标使用的 registry 和基础标签。
func (e *Endpoint) SetMetrics(reg *metrics.Registry, labels map[string]string) {
if e == nil {
return
}
e.mu.Lock()
e.metrics = reg
e.metricLabels = normObserverLabels(labels)
e.mu.Unlock()
}
func (e *Endpoint) recordCallMetric(route string, err error) {
e.recordRPCMetric(MetricRPCCallsTotal, route, err)
}
func (e *Endpoint) recordHandlerMetric(route string, err error) {
e.recordRPCMetric(MetricRPCHandlerCallsTotal, route, err)
}
func (e *Endpoint) recordRPCMetric(name, route string, err error) {
if e == nil {
return
}
e.mu.RLock()
reg := e.metrics
labels := normObserverLabels(e.metricLabels)
e.mu.RUnlock()
if reg == nil {
return
}
route = strings.TrimSpace(route)
if route == "" {
route = "unknown"
}
labels = withRPCMetricLabel(labels, "route", route)
labels = withRPCMetricLabel(labels, "result", rpcMetricResult(err))
reg.Inc(name, labels)
}
func rpcMetricResult(err error) string {
if err == nil {
return "ok"
}
if errors.Is(err, context.Canceled) {
return "canceled"
}
if errors.Is(err, context.DeadlineExceeded) {
return "deadline"
}
if errors.Is(err, ErrInvalidTarget) {
return "invalid_target"
}
if errors.Is(err, ErrInvalidService) {
return "invalid_service"
}
if errors.Is(err, ErrInvalidRoute) {
return "invalid_route"
}
if errors.Is(err, ErrInvalidRequestID) {
return "invalid_request"
}
if errors.Is(err, ErrInvalidNodeID) {
return "invalid_node"
}
if errors.Is(err, ErrNoTargetNodes) {
return "no_target"
}
if errors.Is(err, ErrTooManyPending) {
return "too_many_pending"
}
if errors.Is(err, ErrPayloadTooLarge) {
return "payload_too_large"
}
if errors.Is(err, ErrNotStarted) {
return "not_started"
}
var remoteErr RemoteError
if errors.As(err, &remoteErr) {
return "remote_error"
}
if strings.Contains(err.Error(), "no rpc handler registered") {
return "missing_handler"
}
return "error"
}
func withRPCMetricLabel(labels map[string]string, key, value string) map[string]string {
next := normObserverLabels(labels)
if next == nil {
next = make(map[string]string, 1)
}
next[key] = value
return next
}
func normObserverLabels(labels map[string]string) map[string]string {
if len(labels) == 0 {
return nil
}
out := make(map[string]string, len(labels))
for key, value := range labels {
key = strings.TrimSpace(key)
if key == "" {
continue
}
out[key] = value
}
if len(out) == 0 {
return nil
}
return out
}