Files

130 lines
3.4 KiB
Go

package gameserver
import (
"crypto/aes"
"crypto/cipher"
"net"
"testing"
"time"
"ananta-ps/internal/config"
"ananta-ps/pkg/crypto/chacha8"
"ananta-ps/pkg/protocol"
)
func TestGameServerWorldEntry(t *testing.T) {
cfg := &config.Config{
Client: config.ClientConfig{
ServerID: 801,
Aid: 5944,
},
Network: config.NetworkConfig{
BindHost: "127.0.0.1",
AdvertisedHost: "127.0.0.1",
GamePort: 15203,
},
Player: config.PlayerConfig{
Pid: 100,
InitialUnitId: 100000000000,
InitialSpiritTemplateId: 15020967,
AccountId: "test_acc",
DisplayName: "test_player",
},
World: config.WorldConfig{
RaidId: 23300888,
SceneInstanceId: 20001222,
UniverseId: 76000888,
Spawn: config.Vec3{X: 100, Y: 0, Z: 200},
Facing: 0,
},
}
srv := New(cfg, "../../data/player_info_template.bin")
if err := srv.Start(); err != nil {
t.Fatalf("failed to start game server: %v", err)
}
defer srv.Stop()
// Connect client
conn, err := net.DialTimeout("tcp", "127.0.0.1:15203", 2*time.Second)
if err != nil {
t.Fatalf("failed to connect: %v", err)
}
defer conn.Close()
// 1. Client handshake
clientHandshake := make([]byte, 260)
aesKey := []byte("0123456789abcdef")
copy(clientHandshake[4:20], aesKey)
if err := protocol.WriteFrame(conn, 0x01, clientHandshake, nil); err != nil {
t.Fatalf("failed to write handshake: %v", err)
}
// 2. Server reply
replyFrame, err := protocol.ReadFrame(conn, nil)
if err != nil {
t.Fatalf("failed to read reply: %v", err)
}
aesNonce := replyFrame.Payload[8:20]
encryptedHead := replyFrame.Payload[20:96]
tag := replyFrame.Payload[96:112]
cipherBlock, err := aes.NewCipher(aesKey)
if err != nil {
t.Fatalf("failed to create aes cipher: %v", err)
}
aesGcm, err := cipher.NewGCM(cipherBlock)
if err != nil {
t.Fatalf("failed to create aes gcm: %v", err)
}
fullCipher := append(encryptedHead, tag...)
chaChaHead, err := aesGcm.Open(nil, aesNonce, fullCipher, nil)
if err != nil {
t.Fatalf("failed to decrypt chacha head: %v", err)
}
clientSendCipher, _ := chacha8.New(chaChaHead[0:32], chaChaHead[64:76])
clientReadCipher, _ := chacha8.New(chaChaHead[32:64], chaChaHead[64:76])
// 3. Send LoginGame Invoke
invokePayload := protocol.BuildInvoke(protocol.MethodLoginGame, 1, nil)
if err := protocol.WriteFrame(conn, 0x09, invokePayload, clientSendCipher); err != nil {
t.Fatalf("failed to write LoginGame: %v", err)
}
// 4. Read Return frame
retFrame, err := protocol.ReadFrame(conn, clientReadCipher)
if err != nil {
t.Fatalf("failed to read return: %v", err)
}
pkt := protocol.ParseRpcPacket(retFrame.Payload)
if pkt.Kind != protocol.PacketKindReturn || pkt.InvokeID != 1 {
t.Fatalf("expected return for invoke 1, got %v", pkt)
}
// 5. Read S2C notifications (ServerTime, SyncPlayerInfo, SyncEnterScene)
foundEnterScene := false
for i := 0; i < 3; i++ {
notifyFrame, err := protocol.ReadFrame(conn, clientReadCipher)
if err != nil {
t.Fatalf("failed to read notify frame: %v", err)
}
nPkt := protocol.ParseRpcPacket(notifyFrame.Payload)
if nPkt.MethodID == protocol.MethodSyncEnterScene {
foundEnterScene = true
}
}
if !foundEnterScene {
t.Fatalf("expected SyncEnterScene in notifications")
}
// Verify session tracking
sessions := srv.GetActiveSessions()
if len(sessions) != 1 {
t.Fatalf("expected 1 active session, got %d", len(sessions))
}
}