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)) } }