130 lines
3.5 KiB
Go
130 lines
3.5 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"regionio/internal/world"
|
|
)
|
|
|
|
func TestPlayerRegistrySupportsFourPlayersAndEnforcesLimit(t *testing.T) {
|
|
cfg := DefaultConfig()
|
|
cfg.MaxPlayers = 4
|
|
srv, err := NewWithCache(cfg, world.NewCache(-1, world.GenerateFlat))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var sends atomic.Int32
|
|
var sessions []*PlayerSession
|
|
for _, name := range []string{"Alice", "Bob", "Carol", "Dave"} {
|
|
profile := Profile{Name: name, UUID: OfflineUUID(name)}
|
|
session, err := srv.RegisterPlayer(profile, func(int32, []byte) error {
|
|
sends.Add(1)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("register %s: %v", name, err)
|
|
}
|
|
sessions = append(sessions, session)
|
|
}
|
|
if got := srv.PlayerCount(); got != 4 {
|
|
t.Fatalf("player count = %d, want 4", got)
|
|
}
|
|
if _, err := srv.RegisterPlayer(Profile{Name: "Eve", UUID: OfflineUUID("Eve")}, nil); !errors.Is(err, ErrServerFull) {
|
|
t.Fatalf("fifth player error = %v, want ErrServerFull", err)
|
|
}
|
|
|
|
srv.Broadcast(1, []byte("packet"))
|
|
if got := sends.Load(); got != 4 {
|
|
t.Fatalf("broadcast sends = %d, want 4", got)
|
|
}
|
|
for _, session := range sessions {
|
|
srv.UnregisterPlayer(session)
|
|
}
|
|
if got := srv.PlayerCount(); got != 0 {
|
|
t.Fatalf("player count after disconnect = %d, want 0", got)
|
|
}
|
|
}
|
|
|
|
func TestConcurrentFourPlayerTransformsAndChunkBroadcast(t *testing.T) {
|
|
cfg := DefaultConfig()
|
|
cfg.WorldDir = ""
|
|
cfg.MaxPlayers = 4
|
|
srv, err := NewWithCache(cfg, world.NewCache(-1, world.GenerateFlat))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var sends [4]atomic.Int32
|
|
sessions := make([]*PlayerSession, 4)
|
|
for i, name := range []string{"Alice", "Bob", "Carol", "Dave"} {
|
|
i := i
|
|
sessions[i], err = srv.RegisterPlayer(Profile{Name: name, UUID: OfflineUUID(name)}, func(int32, []byte) error {
|
|
sends[i].Add(1)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
srv.SetPlayerViewDistance(sessions[i], 2)
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
for i, session := range sessions {
|
|
i, session := i, session
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for step := 0; step < 500; step++ {
|
|
srv.SetPlayerTransform(session, float64(i*16+step), 80, float64(-step), float32(step%360), 10, step%2 == 0)
|
|
_ = srv.PlayerSnapshots()
|
|
srv.BroadcastChunk(int32(step>>4), int32(-step>>4), 1, nil)
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
if got := len(srv.PlayerSnapshots()); got != 4 {
|
|
t.Fatalf("snapshot count = %d, want 4", got)
|
|
}
|
|
|
|
for i, session := range sessions {
|
|
x := float64(i * 160)
|
|
srv.SetPlayerTransform(session, x, 80, 0, 0, 0, true)
|
|
before := sends[i].Load()
|
|
srv.BroadcastChunk(0, 0, 2, nil)
|
|
got := sends[i].Load() - before
|
|
if i == 0 && got != 1 {
|
|
t.Fatalf("near player received %d packets, want 1", got)
|
|
}
|
|
if i > 0 && got != 0 {
|
|
t.Fatalf("far player %d received %d packets, want 0", i, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPlayerRegistryRejectsDuplicateName(t *testing.T) {
|
|
cfg := DefaultConfig()
|
|
srv, err := NewWithCache(cfg, world.NewCache(-1, world.GenerateFlat))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first := Profile{Name: "Alice", UUID: OfflineUUID("Alice")}
|
|
if _, err := srv.RegisterPlayer(first, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
duplicate := Profile{Name: "ALICE", UUID: OfflineUUID("ALICE")}
|
|
if _, err := srv.RegisterPlayer(duplicate, nil); !errors.Is(err, ErrDuplicatePlayer) {
|
|
t.Fatalf("duplicate error = %v, want ErrDuplicatePlayer", err)
|
|
}
|
|
}
|
|
|
|
func TestServerRejectsNegativeCacheLimit(t *testing.T) {
|
|
cfg := DefaultConfig()
|
|
cfg.MaxCachedChunks = -1
|
|
if _, err := NewWithCache(cfg, world.NewCache(-1, world.GenerateFlat)); err == nil {
|
|
t.Fatal("negative cache limit was accepted")
|
|
}
|
|
}
|