Files
ngzz-mc/internal/mpsync/hub_test.go

175 lines
4.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package mpsync
import (
"context"
"net"
"path/filepath"
"testing"
"time"
"mc/internal/block"
"mc/internal/logx"
"mc/internal/netproto"
"mc/internal/netserver"
"mc/internal/world"
"mc/internal/worldgen"
)
// syncHandler 测试用服务器处理器:登录 → hub.AddPlayer。
type syncHandler struct {
hub *Hub
recv chan *netserver.Conn
}
func (h *syncHandler) OnConnect(c *netserver.Conn) {}
func (h *syncHandler) OnHandshake(c *netserver.Conn, hs netproto.Handshake) uint8 {
if h.recv != nil {
h.recv <- c
}
h.hub.AddPlayer(c, 8, 8)
return 0
}
func (h *syncHandler) OnMove(c *netserver.Conn, m netproto.PlayerMove) {}
func (h *syncHandler) OnDisconnect(c *netserver.Conn) { h.hub.RemovePlayer(c) }
// TestLoginReceivesChunk 登录后客户端收到区块数据帧(多人同步.md §4)。
func TestLoginReceivesChunk(t *testing.T) {
reg, err := block.Load(filepath.Join("..", "..", "assets", "config", "blocks.json"))
if err != nil {
t.Fatalf("加载注册表失败: %v", err)
}
gen, err := worldgen.New(reg, 7, 0.005, 0.01, 0.1)
if err != nil {
t.Fatalf("创建生成器失败: %v", err)
}
w, err := world.New(reg, gen, 4)
if err != nil {
t.Fatalf("创建世界失败: %v", err)
}
defer w.Close()
deadline := time.Now().Add(15 * time.Second)
for time.Now().Before(deadline) {
w.Update(8, 64, 8, 8*time.Millisecond)
if w.Block(8, 0, 8) != block.Air {
break
}
time.Sleep(2 * time.Millisecond)
}
hub := NewHub(w, 4)
log, _ := logx.New("", logx.LevelDebug)
h := &syncHandler{hub: hub, recv: make(chan *netserver.Conn, 1)}
srv := netserver.New("127.0.0.1:0", log, h)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := srv.Listen(ctx); err != nil {
t.Fatalf("监听失败: %v", err)
}
nc, err := net.DialTimeout("tcp", srv.Addr(), 3*time.Second)
if err != nil {
t.Fatalf("拨号失败: %v", err)
}
defer nc.Close()
hs := netproto.Handshake{ProtocolVersion: 1, ClientID: "同步测试"}
_, _ = nc.Write(netproto.EncodeFrame(netproto.Frame{MsgID: netproto.MsgHandshake, Payload: hs.Encode()}))
gotChunk := false
buf := make([]byte, 64*1024)
_ = nc.SetReadDeadline(time.Now().Add(5 * time.Second))
acc := []byte{}
for !gotChunk {
n, err := nc.Read(buf)
if err != nil {
break
}
acc = append(acc, buf[:n]...)
for len(acc) >= 4 {
f, rest, err := netproto.DecodeFrame(acc)
if err != nil {
break
}
acc = rest
if f.MsgID == netproto.MsgChunkData {
gotChunk = true
}
}
}
if !gotChunk {
t.Fatal("登录后未收到区块数据")
}
}
// TestBlockChangeBroadcast 服务器权威写方块 → 客户端收到广播(多人同步.md §5)。
func TestBlockChangeBroadcast(t *testing.T) {
reg, err := block.Load(filepath.Join("..", "..", "assets", "config", "blocks.json"))
if err != nil {
t.Fatalf("加载注册表失败: %v", err)
}
gen, err := worldgen.New(reg, 8, 0.005, 0.01, 0.1)
if err != nil {
t.Fatalf("创建生成器失败: %v", err)
}
w, err := world.New(reg, gen, 2)
if err != nil {
t.Fatalf("创建世界失败: %v", err)
}
defer w.Close()
hub := NewHub(w, 4)
log, _ := logx.New("", logx.LevelDebug)
h := &syncHandler{hub: hub, recv: make(chan *netserver.Conn, 1)}
srv := netserver.New("127.0.0.1:0", log, h)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := srv.Listen(ctx); err != nil {
t.Fatalf("监听失败: %v", err)
}
nc, err := net.DialTimeout("tcp", srv.Addr(), 3*time.Second)
if err != nil {
t.Fatalf("拨号失败: %v", err)
}
defer nc.Close()
_, _ = nc.Write(netproto.EncodeFrame(netproto.Frame{MsgID: netproto.MsgHandshake, Payload: netproto.Handshake{ProtocolVersion: 1}.Encode()}))
// 等待服务器完成登录注册(避免广播早于 AddPlayer 执行,多人同步.md §2 时序)
select {
case <-h.recv:
case <-time.After(3 * time.Second):
t.Fatal("未完成登录注册")
}
torch, _ := reg.ID("torch")
go hub.SetBlockWorld(8, 70, 8, block.NewState(torch, 0))
buf := make([]byte, 64*1024)
_ = nc.SetReadDeadline(time.Now().Add(5 * time.Second))
acc := []byte{}
deadline := time.Now().Add(5 * time.Second)
for time.Now().Before(deadline) {
n, err := nc.Read(buf)
if err != nil {
break
}
acc = append(acc, buf[:n]...)
for len(acc) >= 4 {
f, rest, err := netproto.DecodeFrame(acc)
if err != nil {
break
}
acc = rest
if f.MsgID != netproto.MsgBlockChange {
continue
}
m, err := netproto.DecodeBlockChange(f.Payload)
if err != nil {
t.Fatalf("解码失败: %v", err)
}
if m.X == 8 && m.Y == 70 && m.Z == 8 && m.BlockID == torch {
return
}
}
}
t.Fatal("未收到方块变更广播")
}