合并本地领先源码:旧UI、GM疲劳燃烧、白金徽章孔、宠物修复、支援兵普攻开关
This commit is contained in:
1 parent
f2debaf9bf
commit
70b3c5a6dc
57 files changed
+4256
-30
No files matched your search
@@ -0,0 +1,111 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dnfcompanion "longheng.io/server/internal/modules/dnf/companion"
|
||||
)
|
||||
|
||||
// companionOfflineTestConn is intentionally local to this test so the file can
|
||||
// be compiled without the large shared service_test.go harness.
|
||||
type companionOfflineTestConn struct {
|
||||
net.Conn
|
||||
write bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *companionOfflineTestConn) Write(data []byte) (int, error) { return c.write.Write(data) }
|
||||
func (c *companionOfflineTestConn) SetWriteDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestBuildCurrentDungeonCompanionOfflineBodyMatchesPresentZeroContract(t *testing.T) {
|
||||
body := buildCurrentDungeonCompanionOfflineBody(12345)
|
||||
if len(body) != currentDungeonCompanionProjectionHeaderSize {
|
||||
t.Fatalf("body length=%d want=%d", len(body), currentDungeonCompanionProjectionHeaderSize)
|
||||
}
|
||||
if got := binary.LittleEndian.Uint32(body[0:4]); got != currentDungeonCompanionProjectionMagic {
|
||||
t.Fatalf("magic=%#x want=%#x", got, currentDungeonCompanionProjectionMagic)
|
||||
}
|
||||
if body[4] != currentDungeonCompanionProjectionVersion {
|
||||
t.Fatalf("version=%d want=%d", body[4], currentDungeonCompanionProjectionVersion)
|
||||
}
|
||||
if body[5] != 0 {
|
||||
t.Fatalf("flags=%#x want 0 (present must be clear)", body[5])
|
||||
}
|
||||
if got := binary.LittleEndian.Uint16(body[6:8]); got != 0 {
|
||||
t.Fatalf("source_character=%d want 0", got)
|
||||
}
|
||||
if got := binary.LittleEndian.Uint64(body[8:16]); got != 12345 {
|
||||
t.Fatalf("revision=%d want 12345", got)
|
||||
}
|
||||
for offset := 16; offset < len(body); offset++ {
|
||||
if body[offset] != 0 {
|
||||
t.Fatalf("byte %d=%#x want 0", offset, body[offset])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendCurrentDungeonCompanionTeamOfflineIsIdempotent(t *testing.T) {
|
||||
connection := &companionOfflineTestConn{}
|
||||
session := &gameSession{conn: connection, connID: "companion-team-logout"}
|
||||
run := ¤tDungeonCompanionRun{
|
||||
Ready: true,
|
||||
TeamMode: true,
|
||||
TeamLeaderID: 1,
|
||||
TeamSize: 2,
|
||||
Online: true,
|
||||
Sources: []currentDungeonCompanionSourceRun{
|
||||
{Ready: true, SourceCharacterID: 2, Body: buildCurrentDungeonCompanionOfflineBody(77)},
|
||||
{Ready: true, SourceCharacterID: 3, Body: buildCurrentDungeonCompanionOfflineBody(77)},
|
||||
},
|
||||
}
|
||||
runtime := &runtimeDungeonState{Companion: run}
|
||||
service := &Service{}
|
||||
|
||||
service.sendCurrentDungeonCompanionTeamOffline(session, runtime, "dungeon_return_town")
|
||||
if !run.LogoutSent || run.Online {
|
||||
t.Fatalf("run state after logout = sent:%v online:%v", run.LogoutSent, run.Online)
|
||||
}
|
||||
if run.OfflineAt.IsZero() || run.OfflineReason != "dungeon_return_town" {
|
||||
t.Fatalf("offline evidence = at:%v reason:%q", run.OfflineAt, run.OfflineReason)
|
||||
}
|
||||
first := connection.write.Len()
|
||||
if first == 0 {
|
||||
t.Fatal("logout packet was not sent")
|
||||
}
|
||||
|
||||
// A second boundary must not replay the packet.
|
||||
service.sendCurrentDungeonCompanionTeamOffline(session, runtime, "return_select_character")
|
||||
if connection.write.Len() != first {
|
||||
t.Fatalf("second logout wrote %d extra bytes", connection.write.Len()-first)
|
||||
}
|
||||
|
||||
stream := connection.write.Bytes()
|
||||
headerSize := service.gameUpperHeaderSize()
|
||||
if len(stream) < headerSize {
|
||||
t.Fatalf("logout stream too short: %d", len(stream))
|
||||
}
|
||||
total := int(binary.LittleEndian.Uint32(stream[3:7]))
|
||||
if total != len(stream) {
|
||||
t.Fatalf("logout packet total=%d stream=%d", total, len(stream))
|
||||
}
|
||||
if msgID := binary.LittleEndian.Uint16(stream[1:3]); msgID != currentDungeonCompanionProjectionOpcode {
|
||||
t.Fatalf("msg id=%#x want=%#x", msgID, currentDungeonCompanionProjectionOpcode)
|
||||
}
|
||||
body := stream[headerSize:total]
|
||||
if len(body) != currentDungeonCompanionProjectionHeaderSize || body[5] != 0 {
|
||||
t.Fatalf("logout body length=%d flags=%d", len(body), body[5])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchTeamAutoFillKeepsProjectionPipelinePerSource(t *testing.T) {
|
||||
// The matcher is pure; this guard pins the cap the bridge passes so a future
|
||||
// change cannot silently form a five-player dungeon party.
|
||||
if dnfcompanion.TeamMaxCompanions != 3 {
|
||||
t.Fatalf("TeamMaxCompanions=%d want 3", dnfcompanion.TeamMaxCompanions)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user