DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
This commit is contained in:
commit
645b64fe21
4388 files changed
+1405457
No files matched your search
@@ -0,0 +1,178 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"longheng.io/server/internal/modules/dnf/dnfenum"
|
||||
dnfproto "longheng.io/server/internal/modules/dnf/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
currentInitialTownProgress = uint32(36)
|
||||
currentInitialTownEntryDelay = time.Duration(0)
|
||||
currentChannelReconnectProbeSize = 31
|
||||
observed90CNShortEndpointBodySize = 586
|
||||
currentChannelReconnectDisplayProbeSize = 590
|
||||
reference90CNChannelReconnectDisplayProbeBodySize = 598
|
||||
observed90CNCrackEndpointBodySize = 610
|
||||
observed90CNShortEndpointMaxBodySize = 630
|
||||
current90CNEndpointShortVariantSize = 4
|
||||
current90CNEndpointAdapterRecordSize = 32
|
||||
current90CNEndpointMaxAdapterRecords = 32
|
||||
)
|
||||
|
||||
type currentInitialTownRouteStage byte
|
||||
|
||||
type currentInitialTownRoutePolicy struct {
|
||||
includeReportClientSpec bool
|
||||
includeSecondFullMode1 bool
|
||||
skipPrerequisiteMode1 bool
|
||||
commitTransitionEarly bool
|
||||
earlyActorDisplayBody []byte
|
||||
// actorMode0Body is the exact selected-actor row already written by the
|
||||
// route. Cold login intentionally delays its first complete mode1 until the
|
||||
// durable skill snapshot is ready; retain this row so that later successful
|
||||
// mode1 can form the owner-produced party admission projection. State-only
|
||||
// expert-job op205 follows the ordinary op19.
|
||||
actorMode0Body []byte
|
||||
// The first character-list town route must seed the native clear-quest
|
||||
// bitmap after the local position/area notifications. Normal movement and
|
||||
// channel reconnect retain their existing policy/order.
|
||||
deferClearQuestListUntilLocationNotifications bool
|
||||
}
|
||||
|
||||
const (
|
||||
currentInitialTownRouteIdle currentInitialTownRouteStage = iota
|
||||
currentInitialTownRouteArmed
|
||||
currentInitialTownRouteActionTableSent
|
||||
currentInitialTownRouteObjectSent
|
||||
currentInitialTownRouteActorBound
|
||||
// These cursors are used by the completed-dungeon return route. The skill
|
||||
// acquisition reader remains suppressed there; the durable expert-job state
|
||||
// is projected only after the retained skill manager and final mode1 are safe.
|
||||
currentInitialTownRouteSkillStateSent
|
||||
currentInitialTownRouteEquipmentMode1Sent
|
||||
currentInitialTownRouteSkinAppearanceRefreshSent
|
||||
currentInitialTownRouteBuffRegistrationSent
|
||||
currentInitialTownRouteProfessionStateSent
|
||||
currentInitialTownRoutePlayerStatePrepared
|
||||
// Completed dungeon returns create a fresh town actor but do not run the
|
||||
// ordinary post-op24 generation. Keep the dedicated slot-26 creature
|
||||
// rebind in the route cursor so a retry cannot publish it twice.
|
||||
currentInitialTownRouteCreatureStateSent
|
||||
currentInitialTownRouteTransitionSent
|
||||
currentInitialTownRoutePlayerStateSent
|
||||
)
|
||||
|
||||
func isCurrentChannelReconnectLifecycleBody(msgID uint16, classification byte, bodyLen int) bool {
|
||||
if classification != dnfproto.DefaultChannelClassification {
|
||||
return false
|
||||
}
|
||||
switch dnfenum.UpperMsg(msgID) {
|
||||
case dnfenum.UpperMsgGameEndpoint:
|
||||
return isCurrentChannelReconnectDisplayProbeBodyLen(bodyLen)
|
||||
case dnfenum.UpperMsgCharacterRoster:
|
||||
return bodyLen == currentChannelReconnectProbeSize
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isCurrentChannelReconnectDisplayProbeBodyLen(bodyLen int) bool {
|
||||
if bodyLen >= observed90CNShortEndpointBodySize &&
|
||||
bodyLen <= observed90CNShortEndpointMaxBodySize &&
|
||||
(bodyLen-observed90CNShortEndpointBodySize)%current90CNEndpointShortVariantSize == 0 {
|
||||
return true
|
||||
}
|
||||
return bodyLen >= reference90CNChannelReconnectDisplayProbeBodySize+current90CNEndpointAdapterRecordSize &&
|
||||
bodyLen <= reference90CNChannelReconnectDisplayProbeBodySize+current90CNEndpointAdapterRecordSize*current90CNEndpointMaxAdapterRecords &&
|
||||
(bodyLen-reference90CNChannelReconnectDisplayProbeBodySize)%current90CNEndpointAdapterRecordSize == 0
|
||||
}
|
||||
|
||||
func markCurrentChannelReconnect(session *gameSession, bodyLen int) bool {
|
||||
if session == nil || bodyLen != currentChannelReconnectProbeSize ||
|
||||
session.rosterRequested || currentTownSceneReady(session) || currentDungeonSceneActive(session) {
|
||||
return false
|
||||
}
|
||||
armCurrentChannelReconnect(session)
|
||||
return true
|
||||
}
|
||||
|
||||
func markCurrentChannelReconnectOnSelection(session *gameSession) bool {
|
||||
if session == nil || session.channelReconnect || session.rosterRequested ||
|
||||
session.selectedCharacterID != 0 || currentTownSceneReady(session) || currentDungeonSceneActive(session) {
|
||||
return false
|
||||
}
|
||||
armCurrentChannelReconnect(session)
|
||||
return true
|
||||
}
|
||||
|
||||
func armCurrentChannelReconnect(session *gameSession) {
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
session.channelReconnect = true
|
||||
session.sceneBootstrapTailDeferred = false
|
||||
session.sceneBootstrapTailSent = false
|
||||
session.townActorOwnerChannel = currentConnectionTownActorOwnerContext(session)
|
||||
}
|
||||
|
||||
func currentTownSceneReady(session *gameSession) bool {
|
||||
if session == nil {
|
||||
return false
|
||||
}
|
||||
session.townMu.Lock()
|
||||
ready := session.townSceneReadyCharacterID != 0 ||
|
||||
session.initialTownRouteStage >= currentInitialTownRouteTransitionSent
|
||||
session.townMu.Unlock()
|
||||
return ready
|
||||
}
|
||||
|
||||
func currentDungeonSceneActive(session *gameSession) bool {
|
||||
if session == nil {
|
||||
return false
|
||||
}
|
||||
session.dungeon.mu.Lock()
|
||||
active := session.dungeon.runtime != nil
|
||||
session.dungeon.mu.Unlock()
|
||||
return active
|
||||
}
|
||||
|
||||
func (s *Service) resetCurrentInitialTownRoute(session *gameSession) {
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
session.townMu.Lock()
|
||||
resetCurrentTownPostTransitionPlayerState(session)
|
||||
session.initialTownRouteCharacterID = 0
|
||||
session.initialTownRouteStage = currentInitialTownRouteIdle
|
||||
session.initialTownGuardianGemRefreshSent = false
|
||||
session.initialTownGuardianShieldDeckSent = false
|
||||
session.initialTownGuardianShieldAppearanceSent = false
|
||||
session.chatEmoticonCatalogSent = false
|
||||
session.initialTownActorSceneSnapshotSent = false
|
||||
session.initialTownActorDisplaySent = false
|
||||
session.initialTownActorPlacementSent = false
|
||||
session.initialTownDungeonPermissionSent = false
|
||||
session.initialTownLocationNotificationsSent = false
|
||||
session.initialTownQuestSnapshotsSent = false
|
||||
session.initialTownQuestHUDRefreshSent = false
|
||||
session.initialTownGuildStateProjectionSent = false
|
||||
session.initialTownSkillInfoPrepared = false
|
||||
session.initialTownSkillInfoSent = false
|
||||
session.initialTownSkillInfo = currentSceneSkillInfoProjection{}
|
||||
session.initialTownMode3RefreshPending = false
|
||||
session.completedTownReturnBuffRegistrationPending = false
|
||||
session.initialTownLegacySceneReadyAccepted = false
|
||||
session.initialTownAdventureOverheadRefreshSent = false
|
||||
session.initialTownCombatPowerAffixesSent = false
|
||||
session.initialTownMoodProjectionSent = false
|
||||
session.crystalContractMu.Lock()
|
||||
session.crystalContractTownUIReadyStateSent = false
|
||||
session.crystalContractMu.Unlock()
|
||||
session.auraSkinMu.Lock()
|
||||
session.auraSkinTownUIReadyStateSent = false
|
||||
session.auraSkinMu.Unlock()
|
||||
session.townActorOwnerChannel = currentConnectionTownActorOwnerContext(session)
|
||||
session.townMu.Unlock()
|
||||
}
|
||||
Reference in new issue
Block a user