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

100 lines
3.0 KiB
Go
Raw 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 多人同步:登录发送区块数据、方块变更广播(多人同步.md §2、§4§5
//
// 服务器权威:方块破坏/放置由服务器执行后广播(多人同步.md §5
package mpsync
import (
"sync"
"mc/internal/block"
"mc/internal/netproto"
"mc/internal/netserver"
"mc/internal/save"
"mc/internal/world"
)
// Hub 同步中心:玩家会话与广播。
type Hub struct {
mu sync.Mutex
clients map[*netserver.Conn]struct{} // 已登录连接
world *world.World
viewDist int
}
// NewHub 创建同步中心。
func NewHub(w *world.World, viewDist int) *Hub {
return &Hub{clients: make(map[*netserver.Conn]struct{}), world: w, viewDist: viewDist}
}
// AddPlayer 玩家登录:加入广播集并发送周围区块(多人同步.md §4 区块流)。
func (h *Hub) AddPlayer(c *netserver.Conn, spawnX, spawnZ float64) {
h.mu.Lock()
h.clients[c] = struct{}{}
h.mu.Unlock()
c.Send(netproto.Frame{MsgID: netproto.MsgLoginResponse, Payload: []byte{0}})
// 发送视距内已加载区块(按距离排序,多人同步.md §4
chunks := h.world.ActiveChunks()
for _, ch := range chunks {
if absi32(ch.CX-int32(spawnX)/16) > int32(h.viewDist) || absi32(ch.CZ-int32(spawnZ)/16) > int32(h.viewDist) {
continue
}
data := save.EncodeChunk(ch)
c.Send(netproto.Frame{MsgID: netproto.MsgChunkData, Payload: data})
}
}
// RemovePlayer 玩家断开:移出广播集。
func (h *Hub) RemovePlayer(c *netserver.Conn) {
h.mu.Lock()
delete(h.clients, c)
h.mu.Unlock()
}
// BroadcastBlockChange 广播方块变更(多人同步.md §5只发视距内MVP 全量广播)。
func (h *Hub) BroadcastBlockChange(x, y, z int32, id uint16, meta uint8) {
msg := netproto.BlockChange{X: x, Y: y, Z: z, BlockID: id, Meta: meta}
payload := msg.Encode()
h.mu.Lock()
clients := make([]*netserver.Conn, 0, len(h.clients))
for c := range h.clients {
clients = append(clients, c)
}
h.mu.Unlock()
for _, c := range clients {
c.Send(netproto.Frame{MsgID: netproto.MsgBlockChange, Payload: payload})
}
}
// SetBlockWorld 服务器权威写方块:修改世界并广播(多人同步.md §2 权威链路)。
func (h *Hub) SetBlockWorld(x, y, z int32, s block.State) {
h.world.SetBlock(x, y, z, s)
h.BroadcastBlockChange(x, y, z, s.ID(), s.Meta())
}
// BroadcastEntityMove 广播玩家移动给其他客户端(多人同步.md §320Hz 增量)。
// from 为发送者(不回传给自己)。
func (h *Hub) BroadcastEntityMove(from *netserver.Conn, entityID uint32, m netproto.PlayerMove) {
msg := netproto.EntityMove{EntityID: entityID, X: m.X, Y: m.Y, Z: m.Z, Yaw: m.Yaw}
payload := msg.Encode()
h.mu.Lock()
clients := make([]*netserver.Conn, 0, len(h.clients))
for c := range h.clients {
if c != from {
clients = append(clients, c)
}
}
h.mu.Unlock()
for _, c := range clients {
c.Send(netproto.Frame{MsgID: netproto.MsgEntityMove, Payload: payload})
}
}
// absi32 绝对值。
func absi32(v int32) int32 {
if v < 0 {
return -v
}
return v
}