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,106 @@
|
||||
package npk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/binary"
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func ddsTexture(format string, width, height uint32, blocks []byte) []byte {
|
||||
data := make([]byte, 128)
|
||||
copy(data, "DDS ")
|
||||
binary.LittleEndian.PutUint32(data[4:8], 124)
|
||||
binary.LittleEndian.PutUint32(data[12:16], height)
|
||||
binary.LittleEndian.PutUint32(data[16:20], width)
|
||||
binary.LittleEndian.PutUint32(data[76:80], 32)
|
||||
binary.LittleEndian.PutUint32(data[80:84], 4)
|
||||
copy(data[84:88], format)
|
||||
return append(data, blocks...)
|
||||
}
|
||||
|
||||
func TestSpriteDDSHeaderAndAtlasCrop(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
format int32
|
||||
blockSize, colors int
|
||||
}{{"DXT1", FormatDXT1, 8, 0}, {"DXT3", FormatDXT3, 16, 8}, {"DXT5", FormatDXT5, 16, 8}} {
|
||||
for _, wrapped := range []bool{false, true} {
|
||||
name := tc.name + "/raw"
|
||||
if wrapped {
|
||||
name = tc.name + "/dds"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Two atlas blocks: red at x=0, green at x=4. The frame selects green.
|
||||
blocks := make([]byte, tc.blockSize*2)
|
||||
for i, c := range []uint16{0xf800, 0x07e0} {
|
||||
block := blocks[i*tc.blockSize : (i+1)*tc.blockSize]
|
||||
if tc.format == FormatDXT3 {
|
||||
for j := 0; j < 8; j++ {
|
||||
block[j] = 255
|
||||
}
|
||||
}
|
||||
if tc.format == FormatDXT5 {
|
||||
block[0] = 255
|
||||
}
|
||||
binary.LittleEndian.PutUint16(block[tc.colors:], c)
|
||||
}
|
||||
payload := blocks
|
||||
if wrapped {
|
||||
payload = ddsTexture(tc.name, 8, 4, blocks)
|
||||
}
|
||||
var compressed bytes.Buffer
|
||||
writer := zlib.NewWriter(&compressed)
|
||||
_, _ = writer.Write(payload)
|
||||
_ = writer.Close()
|
||||
prefix := []byte{}
|
||||
putInt32(&prefix, 1, int32(compressed.Len()), 0) // sprite count, data size, palette count
|
||||
putInt32(&prefix, 1, tc.format, 0, int32(compressed.Len()), int32(len(payload)), 8, 4)
|
||||
frame := imageHeader(tc.format, ExtraSprite, 4, 4, 0)
|
||||
putInt32(&frame, 0, 0, 4, 0, 8, 4, 0)
|
||||
data := makeIMG(5, append(prefix, frame...), compressed.Bytes(), int32(len(frame)))
|
||||
parsed, err := ParseIMGAt(bytes.NewReader(data), 0, int64(len(data)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
decoded, err := DecodeImage(bytes.NewReader(data), 0, int64(len(data)), parsed, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decoded.Bounds().Dx() != 4 || decoded.Bounds().Dy() != 4 {
|
||||
t.Fatal(decoded.Bounds())
|
||||
}
|
||||
for y := 0; y < 4; y++ {
|
||||
for x := 0; x < 4; x++ {
|
||||
got := color.NRGBAModel.Convert(decoded.At(x, y)).(color.NRGBA)
|
||||
if got != (color.NRGBA{G: 255, A: 255}) {
|
||||
t.Fatalf("裁剪到相邻图标或文件头:(%d,%d)=%+v", x, y, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpriteDDSInvalidHeaders(t *testing.T) {
|
||||
valid := ddsTexture("DXT1", 4, 4, make([]byte, 8))
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
data []byte
|
||||
sprite SpriteMeta
|
||||
}{
|
||||
{"truncated", []byte("DDS "), SpriteMeta{Format: FormatDXT1, Width: 4, Height: 4}},
|
||||
{"dimensions", valid, SpriteMeta{Format: FormatDXT1, Width: 8, Height: 4}},
|
||||
{"format", valid, SpriteMeta{Format: FormatDXT5, Width: 4, Height: 4}},
|
||||
{"unsupported", ddsTexture("DX10", 4, 4, nil), SpriteMeta{Format: FormatDXT1, Width: 4, Height: 4}},
|
||||
{"pixels", valid[:128], SpriteMeta{Format: FormatDXT1, Width: 4, Height: 4}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if _, err := decodeSpriteTexture(tc.data, tc.sprite); err == nil {
|
||||
t.Fatal("应拒绝无效DDS")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user