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,120 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"longheng.io/server/internal/modules/dnf/dnfenum"
|
||||
"longheng.io/server/internal/modules/dnf/premium"
|
||||
dnfproto "longheng.io/server/internal/modules/dnf/protocol"
|
||||
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
|
||||
)
|
||||
|
||||
func TestBuildCurrentCeraTicketSuccessExactBody(t *testing.T) {
|
||||
webURL := "http://127.0.0.1:18111" + PaymentWebOrdinaryPath
|
||||
body, err := buildCurrentCeraTicketSuccess(webURL, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if body[0] != 1 || binary.LittleEndian.Uint32(body[1:5]) != uint32(len(webURL)) {
|
||||
t.Fatalf("cera ticket prefix=% X", body[:5])
|
||||
}
|
||||
end := 5 + len(webURL)
|
||||
if string(body[5:end]) != webURL || binary.LittleEndian.Uint32(body[end:]) != 0 {
|
||||
t.Fatalf("cera ticket body=% X", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCurrentCeraTicketRejectsUnsupportedURLLength(t *testing.T) {
|
||||
for _, webURL := range []string{"", strings.Repeat("x", currentCeraTicketMaxURL+1)} {
|
||||
if _, err := buildCurrentCeraTicketSuccess(webURL, 0); err == nil {
|
||||
t.Fatalf("URL length %d was accepted", len(webURL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentPremiumWebOrdinaryPageOwnsBlackDiamondNavigation(t *testing.T) {
|
||||
service := &Service{}
|
||||
request := httptest.NewRequest(http.MethodGet, PaymentWebOrdinaryPath, nil)
|
||||
response := httptest.NewRecorder()
|
||||
service.PaymentWebHandler().ServeHTTP(response, request)
|
||||
if response.Code != http.StatusOK ||
|
||||
!strings.Contains(response.Body.String(), "黑钻会员") ||
|
||||
!strings.Contains(response.Body.String(), PaymentWebBlackDiamondPath) {
|
||||
t.Fatalf("ordinary premium page status=%d body=%q", response.Code, response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentCeraTicketRequiresClassOne(t *testing.T) {
|
||||
service := &Service{}
|
||||
session := &gameSession{}
|
||||
handled, err := currentGameplayModules.DispatchUpper(
|
||||
service,
|
||||
session,
|
||||
uint16(dnfenum.CmdPacketGenCeraticket),
|
||||
0,
|
||||
nil,
|
||||
)
|
||||
if err != nil || !handled {
|
||||
t.Fatalf("class0 op65 handled=%t err=%v", handled, err)
|
||||
}
|
||||
if session.conn != nil {
|
||||
t.Fatal("class0 op65 must not create a payment session")
|
||||
}
|
||||
if dnfproto.DefaultChannelClassification != 1 {
|
||||
t.Fatalf("unexpected default channel classification=%d", dnfproto.DefaultChannelClassification)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentBlackDiamondActivationPersistsEntitlementAndPreservesFatigueUse(t *testing.T) {
|
||||
service, _, session, _, _ := prepareCompletedSettlementRuntime(t)
|
||||
now := time.Unix(1_700_000_000, 0).UTC()
|
||||
service.gameplayTimers = &processGameplayTimeQueue{now: func() time.Time { return now }}
|
||||
repositories, ok := service.repositoryGroup()
|
||||
if !ok {
|
||||
t.Fatal("repository group unavailable")
|
||||
}
|
||||
if err := repositories.Account.Save(context.Background(), dnfrepo.AccountRecord{AccountID: "account-1"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
character, found, err := repositories.Character.Load(context.Background(), "99")
|
||||
if err != nil || !found {
|
||||
t.Fatalf("load character found=%t err=%v", found, err)
|
||||
}
|
||||
character.Stats["fatigue"] = 120
|
||||
character.Stats["fatigue_limit"] = 156
|
||||
if err := repositories.Character.Save(context.Background(), character); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ticket := ¤tPremiumWebTicket{
|
||||
accountID: "account-1", characterID: 99,
|
||||
characterGeneration: session.characterGeneration, session: session,
|
||||
}
|
||||
result, err := service.activateCurrentBlackDiamondEntitlement(context.Background(), ticket)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantExpiry := now.Add(currentBlackDiamondTerm).Unix()
|
||||
if !result.Active || result.ExpiresAt.Unix() != wantExpiry ||
|
||||
result.Character.Stats["fatigue"] != 152 || result.Character.Stats["fatigue_limit"] != 188 {
|
||||
t.Fatalf("activation result=%+v stats=%+v", result, result.Character.Stats)
|
||||
}
|
||||
account, found, err := repositories.Account.Load(context.Background(), "account-1")
|
||||
if err != nil || !found {
|
||||
t.Fatalf("load account found=%t err=%v", found, err)
|
||||
}
|
||||
if got := account.Metadata[premium.MetadataKey(premium.TypeBlackDiamondEntitlement)]; got != strconv.FormatInt(wantExpiry, 10) {
|
||||
t.Fatalf("black diamond expiry metadata=%q, want %d", got, wantExpiry)
|
||||
}
|
||||
for _, ordinaryType := range []int64{1, 17, 29, premium.TypeBlackDiamondWire} {
|
||||
if _, exists := account.Metadata[premium.MetadataKey(ordinaryType)]; exists {
|
||||
t.Fatalf("activation wrote non-entitlement premium type %d", ordinaryType)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user