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

92 lines
2.9 KiB
Go

package dnfbridge
import (
"fmt"
"longheng.io/server/internal/modules/dnf/dnfenum"
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
)
const currentSceneTransitionMsgID = uint16(dnfenum.CmdPacketSetItemtradeState)
type currentSceneTransitionRow struct {
ObjectOrResourceKey uint16
Value1 uint16
Value2 uint16
Value3 byte
Value4 byte
}
func buildCurrentSceneTransitionBody(townID byte, areaID byte, rows []currentSceneTransitionRow) ([]byte, error) {
if len(rows) > int(^uint16(0)) {
return nil, fmt.Errorf("current scene transition row count %d exceeds u16", len(rows))
}
var writer packetWriter
writer.writeByte(townID)
writer.writeByte(areaID)
writer.writeUint16(uint16(len(rows)))
for _, row := range rows {
writer.writeUint16(row.ObjectOrResourceKey)
writer.writeUint16(row.Value1)
writer.writeUint16(row.Value2)
writer.writeByte(row.Value3)
writer.writeByte(row.Value4)
}
return writer.bytes(), nil
}
func currentSceneTransitionLocation(character dnfrepo.CharacterRecord, hasCharacter bool) (byte, byte, error) {
if !hasCharacter {
return 0, 0, fmt.Errorf("selected character record not found")
}
if character.Stats == nil {
return 0, 0, fmt.Errorf("selected character stats not loaded")
}
townID, hasTownID := character.Stats["town_id"]
if !hasTownID {
return 0, 0, fmt.Errorf("selected character town_id not loaded")
}
areaID, hasAreaID := character.Stats["area_id"]
if !hasAreaID {
return 0, 0, fmt.Errorf("selected character area_id not loaded")
}
if townID < 0 || townID > int64(^byte(0)) {
return 0, 0, fmt.Errorf("selected character town_id %d is outside u8", townID)
}
if areaID < 0 || areaID > int64(^byte(0)) {
return 0, 0, fmt.Errorf("selected character area_id %d is outside u8", areaID)
}
return byte(townID), byte(areaID), nil
}
func (s *Service) sendCurrentSceneTransitionOp24(session *gameSession, character dnfrepo.CharacterRecord, hasCharacter bool, source string) error {
townID, areaID, err := currentSceneTransitionLocation(character, hasCharacter)
if err != nil {
s.logGameEvent(session, "game-upper-current-scene-transition-op24-deferred",
"source", source,
"char_id", session.selectedCharacterID,
"msg_id", currentSceneTransitionMsgID,
"reason", err.Error())
return nil
}
rows := []currentSceneTransitionRow(nil)
body, err := buildCurrentSceneTransitionBody(townID, areaID, rows)
if err != nil {
return err
}
s.logGameEvent(session, "game-upper-current-scene-transition-op24-send",
"source", source,
"char_id", session.selectedCharacterID,
"msg_id", currentSceneTransitionMsgID,
"classification", 0,
"town_id", townID,
"area_id", areaID,
"row_count", len(rows),
"body_len", len(body),
"body_source", "current_exe_sub_1D901D0_typed",
"row_source", "no_current_owned_resource_rows")
return s.sendGameUpperRawClass(session, currentSceneTransitionMsgID, body, 0)
}