服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
230 lines
8.3 KiB
Go
230 lines
8.3 KiB
Go
package raid
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
)
|
|
|
|
func TestCurrentRaidResultFlowMessageIDs(t *testing.T) {
|
|
if RaidMovieMsgID != 0x0257 || RaidRewardListMsgID != 0x0258 || RaidResultMsgID != 0x0259 {
|
|
t.Fatalf("S2C ids = %#04x/%#04x/%#04x", RaidMovieMsgID, RaidRewardListMsgID, RaidResultMsgID)
|
|
}
|
|
if RaidResultStageRequestMsgID != 0x0293 || RaidRewardCardRequestMsgID != 0x0294 ||
|
|
RaidDoBehaviorRequestMsgID != 0x0295 || RaidMessageRequestMsgID != 0x0297 {
|
|
t.Fatalf("C2S ids = %#04x/%#04x/%#04x/%#04x", RaidResultStageRequestMsgID, RaidRewardCardRequestMsgID, RaidDoBehaviorRequestMsgID, RaidMessageRequestMsgID)
|
|
}
|
|
}
|
|
|
|
func TestRaidDoBehaviorRequestIsExactU32U16WithoutAck(t *testing.T) {
|
|
request, err := DecodeRaidDoBehaviorRequest([]byte{0x44, 0x33, 0x22, 0x11, 0x66, 0x55})
|
|
if err != nil {
|
|
t.Fatalf("decode behavior: %v", err)
|
|
}
|
|
if request.Value0 != 0x11223344 || request.Value1 != 0x5566 {
|
|
t.Fatalf("behavior = %+v", request)
|
|
}
|
|
for _, body := range [][]byte{nil, make([]byte, 5), make([]byte, 7)} {
|
|
if _, err := DecodeRaidDoBehaviorRequest(body); err != errRaidDoBehaviorBodyInvalid {
|
|
t.Fatalf("behavior body len=%d error=%v", len(body), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRaidMessageRequestIsExactU8DSTRU8WithoutAck(t *testing.T) {
|
|
body := []byte{2, 3, 0, 0, 0, 'a', 'b', 'c', 7}
|
|
request, err := DecodeRaidMessageRequest(body)
|
|
if err != nil {
|
|
t.Fatalf("decode message: %v", err)
|
|
}
|
|
if request.Mode != 2 || request.Tail != 7 || !bytes.Equal(request.MessageBytes, []byte("abc")) {
|
|
t.Fatalf("message = %+v", request)
|
|
}
|
|
request.MessageBytes[0] = 'z'
|
|
if body[5] != 'a' {
|
|
t.Fatalf("decoded message aliases caller body")
|
|
}
|
|
|
|
for _, body := range [][]byte{nil, {0, 0, 0, 0, 0}, {0, 2, 0, 0, 0, 'a', 0}} {
|
|
if _, err := DecodeRaidMessageRequest(body); err != errRaidMessageBodyInvalid && err != errRaidMessageDSTRTruncated {
|
|
t.Fatalf("message body % X error=%v", body, err)
|
|
}
|
|
}
|
|
if _, err := DecodeRaidMessageRequest([]byte{0, 1, 0, 0, 0, 'a', 0, 0}); err != errRaidMessageBodyInvalid {
|
|
t.Fatalf("message trailing byte error=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestBuildRaidMovieSkipBodyIsExactlyEmpty(t *testing.T) {
|
|
if got := BuildRaidMovieSkipBody(); got == nil || len(got) != 0 {
|
|
t.Fatalf("movie-skip body = % X, want non-nil empty body", got)
|
|
}
|
|
}
|
|
|
|
func TestBuildRaidSettlementResultBodyExactTenBytes(t *testing.T) {
|
|
got, err := BuildRaidSettlementResultBody(RaidSettlementResult{
|
|
ResultType: 2,
|
|
PhaseIndex: 1,
|
|
ClearSeconds: 0x11223344,
|
|
DeathCount: 0x5566,
|
|
Score: 0x77,
|
|
Flags: 0x88,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("BuildRaidSettlementResultBody error = %v", err)
|
|
}
|
|
want := []byte{2, 1, 0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x77, 0x88}
|
|
if !bytes.Equal(got, want) {
|
|
t.Fatalf("result body = % X, want % X", got, want)
|
|
}
|
|
if len(got) != 10 {
|
|
t.Fatalf("result body len = %d, want 10", len(got))
|
|
}
|
|
|
|
minimal, err := BuildRaidSettlementResultBody(RaidSettlementResult{ClearSeconds: 300})
|
|
if err != nil {
|
|
t.Fatalf("minimal result error = %v", err)
|
|
}
|
|
if want := []byte{0, 0, 0x2c, 0x01, 0, 0, 0, 0, 0, 0}; !bytes.Equal(minimal, want) {
|
|
t.Fatalf("minimal result body = % X, want % X", minimal, want)
|
|
}
|
|
}
|
|
|
|
func TestBuildRaidSettlementResultBodyRejectsNativeRangeOverflow(t *testing.T) {
|
|
if _, err := BuildRaidSettlementResultBody(RaidSettlementResult{ResultType: 4}); err != errRaidResultTypeInvalid {
|
|
t.Fatalf("result type error = %v, want %v", err, errRaidResultTypeInvalid)
|
|
}
|
|
if _, err := BuildRaidSettlementResultBody(RaidSettlementResult{PhaseIndex: 2}); err != errRaidResultPhaseInvalid {
|
|
t.Fatalf("phase error = %v, want %v", err, errRaidResultPhaseInvalid)
|
|
}
|
|
}
|
|
|
|
func TestBuildRaidSettlementRewardsBodyExactNestedBytes(t *testing.T) {
|
|
got, err := BuildRaidSettlementRewardsBody(RaidSettlementRewards{
|
|
ResultType: 1,
|
|
Members: []RaidSettlementMemberRewards{
|
|
{
|
|
MemberKey: 0x1234,
|
|
CardIndex: 5,
|
|
Rewards: []RaidSettlementReward{
|
|
{Kind: 0, ItemIDOrValue: 0x11223344, CountOrAux: 0x5566},
|
|
{},
|
|
},
|
|
},
|
|
{MemberKey: 0xabcd, CardIndex: 9},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("BuildRaidSettlementRewardsBody error = %v", err)
|
|
}
|
|
want := []byte{
|
|
1, 2,
|
|
0x34, 0x12, 5, 2,
|
|
0, 0x44, 0x33, 0x22, 0x11, 0x66, 0x55,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
0xcd, 0xab, 9, 0,
|
|
}
|
|
if !bytes.Equal(got, want) {
|
|
t.Fatalf("reward body = % X, want % X", got, want)
|
|
}
|
|
}
|
|
|
|
func TestBuildRaidSettlementRewardsBodyValidatesCountsAndNormalItems(t *testing.T) {
|
|
if _, err := BuildRaidSettlementRewardsBody(RaidSettlementRewards{
|
|
Members: make([]RaidSettlementMemberRewards, maxRaidByteCount+1),
|
|
}); err != errRaidByteCountOverflow {
|
|
t.Fatalf("member overflow error = %v, want %v", err, errRaidByteCountOverflow)
|
|
}
|
|
if _, err := BuildRaidSettlementRewardsBody(RaidSettlementRewards{
|
|
Members: []RaidSettlementMemberRewards{{
|
|
Rewards: make([]RaidSettlementReward, maxRaidByteCount+1),
|
|
}},
|
|
}); err != errRaidByteCountOverflow {
|
|
t.Fatalf("reward overflow error = %v, want %v", err, errRaidByteCountOverflow)
|
|
}
|
|
|
|
invalidItems := []RaidSettlementReward{
|
|
{Kind: 0, ItemIDOrValue: 1},
|
|
{Kind: 0, CountOrAux: 1},
|
|
}
|
|
for _, reward := range invalidItems {
|
|
if _, err := BuildRaidSettlementRewardsBody(RaidSettlementRewards{
|
|
Members: []RaidSettlementMemberRewards{{Rewards: []RaidSettlementReward{reward}}},
|
|
}); err != errRaidNormalRewardItemInvalid {
|
|
t.Fatalf("partial normal item %+v error = %v, want %v", reward, err, errRaidNormalRewardItemInvalid)
|
|
}
|
|
}
|
|
|
|
if _, err := BuildRaidSettlementRewardsBody(RaidSettlementRewards{
|
|
Members: []RaidSettlementMemberRewards{{Rewards: []RaidSettlementReward{{Kind: 1}}}},
|
|
}); err != nil {
|
|
t.Fatalf("unproved non-zero reward kind should remain encodable: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRaidResultStageRequestExactBodyAndDecode(t *testing.T) {
|
|
for _, stage := range []byte{0, 1} {
|
|
got, err := BuildRaidResultStageRequestBody(RaidResultStageRequest{Stage: stage})
|
|
if err != nil {
|
|
t.Fatalf("build stage %d error = %v", stage, err)
|
|
}
|
|
if want := []byte{stage}; !bytes.Equal(got, want) {
|
|
t.Fatalf("stage %d body = % X, want % X", stage, got, want)
|
|
}
|
|
decoded, err := DecodeRaidResultStageRequest(got)
|
|
if err != nil || decoded.Stage != stage {
|
|
t.Fatalf("decode stage %d = %+v, %v", stage, decoded, err)
|
|
}
|
|
}
|
|
if _, err := BuildRaidResultStageRequestBody(RaidResultStageRequest{Stage: 2}); err != errRaidResultStageInvalid {
|
|
t.Fatalf("invalid build stage error = %v", err)
|
|
}
|
|
for _, body := range [][]byte{nil, {0, 1}} {
|
|
if _, err := DecodeRaidResultStageRequest(body); err != errRaidResultStageBodyInvalid {
|
|
t.Fatalf("stage body % X error = %v, want %v", body, err, errRaidResultStageBodyInvalid)
|
|
}
|
|
}
|
|
if _, err := DecodeRaidResultStageRequest([]byte{2}); err != errRaidResultStageInvalid {
|
|
t.Fatalf("invalid decoded stage error = %v", err)
|
|
}
|
|
if got := BuildRaidResultStageAckBody(); got == nil || len(got) != 0 {
|
|
t.Fatalf("stage ACK = % X, want non-nil empty body", got)
|
|
}
|
|
}
|
|
|
|
func TestRaidRewardCardRequestExactBodyDecodeAndAck(t *testing.T) {
|
|
for _, request := range []RaidRewardCardRequest{
|
|
{Mode: 0, CardIndex: 0x11},
|
|
{Mode: 1, CardIndex: 0x80},
|
|
{Mode: 2, CardIndex: 0xff},
|
|
} {
|
|
got, err := BuildRaidRewardCardRequestBody(request)
|
|
if err != nil {
|
|
t.Fatalf("build %+v error = %v", request, err)
|
|
}
|
|
if want := []byte{request.Mode, request.CardIndex}; !bytes.Equal(got, want) {
|
|
t.Fatalf("request %+v body = % X, want % X", request, got, want)
|
|
}
|
|
decoded, err := DecodeRaidRewardCardRequest(got)
|
|
if err != nil || decoded != request {
|
|
t.Fatalf("decode % X = %+v, %v; want %+v", got, decoded, err, request)
|
|
}
|
|
}
|
|
if _, err := BuildRaidRewardCardRequestBody(RaidRewardCardRequest{Mode: 3}); err != errRaidRewardCardModeInvalid {
|
|
t.Fatalf("invalid build mode error = %v", err)
|
|
}
|
|
for _, body := range [][]byte{nil, {0}, {0, 1, 2}} {
|
|
if _, err := DecodeRaidRewardCardRequest(body); err != errRaidRewardCardBodyInvalid {
|
|
t.Fatalf("card body % X error = %v, want %v", body, err, errRaidRewardCardBodyInvalid)
|
|
}
|
|
}
|
|
if _, err := DecodeRaidRewardCardRequest([]byte{3, 0}); err != errRaidRewardCardModeInvalid {
|
|
t.Fatalf("invalid decoded mode error = %v", err)
|
|
}
|
|
if got, want := BuildRaidRewardCardAckBody(true, 0xff), []byte{1}; !bytes.Equal(got, want) {
|
|
t.Fatalf("success ACK = % X, want % X", got, want)
|
|
}
|
|
if got, want := BuildRaidRewardCardAckBody(false, 0x7e), []byte{0, 0x7e}; !bytes.Equal(got, want) {
|
|
t.Fatalf("failure ACK = % X, want % X", got, want)
|
|
}
|
|
}
|