130 lines
4.0 KiB
Go
130 lines
4.0 KiB
Go
package dnfbridge
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"longheng.io/server/internal/modules/dnf/channelcatalog"
|
|
)
|
|
|
|
type companionPartyTestConn struct {
|
|
net.Conn
|
|
write bytes.Buffer
|
|
}
|
|
|
|
func (c *companionPartyTestConn) Write(data []byte) (int, error) { return c.write.Write(data) }
|
|
func (c *companionPartyTestConn) SetWriteDeadline(time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
func TestCompanionPresenceFriendRowRendersOnline(t *testing.T) {
|
|
service := &Service{}
|
|
virtual := &gameSession{
|
|
selectedCharacterID: 6,
|
|
characterGeneration: 7,
|
|
channel: channelcatalog.Channel{ID: 11},
|
|
companionVirtual: true,
|
|
companionOwnerID: 2,
|
|
}
|
|
service.companionSessions = map[uint16]*gameSession{6: virtual}
|
|
|
|
presence := service.currentUnitedFriendPresence(6)
|
|
if presence.Busy {
|
|
t.Fatal("volunteer friend row must not render busy/登录中")
|
|
}
|
|
if presence.ChannelID != 11 {
|
|
t.Fatalf("friend row channel=%d want 11", presence.ChannelID)
|
|
}
|
|
}
|
|
|
|
func TestBuildCompanionPartyStateUsesNativeFourSlotRoster(t *testing.T) {
|
|
service := &Service{}
|
|
owner := &gameSession{selectedCharacterID: 2}
|
|
run := ¤tDungeonCompanionRun{
|
|
Ready: true,
|
|
Sources: []currentDungeonCompanionSourceRun{
|
|
{Ready: true, SourceCharacterID: 6},
|
|
{Ready: false, SourceCharacterID: 7},
|
|
{Ready: true, SourceCharacterID: 3},
|
|
},
|
|
}
|
|
state := service.buildCompanionPartyState(owner, run)
|
|
if state.PartyID <= 0 || state.PartyID >= 0xFFFF {
|
|
t.Fatalf("party id=%d is not a printable native party", state.PartyID)
|
|
}
|
|
if state.UserID != 2 || !state.IsLeader || state.MaxMembers != 4 {
|
|
t.Fatalf("party identity=%+v", state)
|
|
}
|
|
if len(state.Members) != 3 {
|
|
t.Fatalf("members=%v want owner + 2 ready volunteers", state.Members)
|
|
}
|
|
want := []struct {
|
|
id uint16
|
|
slot byte
|
|
}{{2, 0}, {6, 1}, {3, 2}}
|
|
for index, expected := range want {
|
|
member := state.Members[index]
|
|
if member.UserID != expected.id || member.Slot != expected.slot {
|
|
t.Fatalf("member %d=%+v want id=%d slot=%d", index, member, expected.id, expected.slot)
|
|
}
|
|
if member.HPPercent != 100 || member.MPPercent != 100 {
|
|
t.Fatalf("member %d hp/mp=%d/%d want 100/100", index, member.HPPercent, member.MPPercent)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClearCompanionNativePartySendsOp9ThenOp153(t *testing.T) {
|
|
connection := &companionPartyTestConn{}
|
|
session := &gameSession{
|
|
conn: connection,
|
|
connID: "companion-party-clear",
|
|
selectedCharacterID: 2,
|
|
characterGeneration: 3,
|
|
channel: channelcatalog.Channel{ID: 11},
|
|
}
|
|
run := ¤tDungeonCompanionRun{
|
|
Ready: true,
|
|
NativePartyID: 0xE002,
|
|
NativePartySent: true,
|
|
}
|
|
service := &Service{}
|
|
service.clearCurrentDungeonCompanionNativeParty(session, run)
|
|
if run.NativePartySent {
|
|
t.Fatal("native party flag was not cleared")
|
|
}
|
|
stream := connection.write.Bytes()
|
|
headerSize := service.gameUpperHeaderSize()
|
|
msgIDs := make([]uint16, 0, 2)
|
|
for len(stream) > 0 {
|
|
if len(stream) < headerSize {
|
|
t.Fatalf("truncated packet header: %d", len(stream))
|
|
}
|
|
total := int(binary.LittleEndian.Uint32(stream[3:7]))
|
|
if total < headerSize || total > len(stream) {
|
|
t.Fatalf("packet total=%d stream=%d", total, len(stream))
|
|
}
|
|
msgIDs = append(msgIDs, binary.LittleEndian.Uint16(stream[1:3]))
|
|
stream = stream[total:]
|
|
}
|
|
if len(msgIDs) != 2 || msgIDs[0] != 9 || msgIDs[1] != 0x0099 {
|
|
t.Fatalf("clear packets=%v want [9 153]", msgIDs)
|
|
}
|
|
}
|
|
|
|
func TestCompanionSessionIdentityIsCurrentOnlyWhileRegistered(t *testing.T) {
|
|
service := &Service{}
|
|
virtual := &gameSession{selectedCharacterID: 6, characterGeneration: 9, companionVirtual: true}
|
|
service.companionSessions = map[uint16]*gameSession{6: virtual}
|
|
identity, ok := service.companionSessionSnapshot(6)
|
|
if !ok || !identity.virtual || !service.boundGameSessionCharacterCurrent(identity) {
|
|
t.Fatalf("virtual identity=%+v ok=%v current=%v", identity, ok, service.boundGameSessionCharacterCurrent(identity))
|
|
}
|
|
delete(service.companionSessions, 6)
|
|
if service.boundGameSessionCharacterCurrent(identity) {
|
|
t.Fatal("removed volunteer session must fail the liveness check")
|
|
}
|
|
}
|