130 lines
3.4 KiB
Go
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))
|
|
}
|
|
}
|