Documentation
¶
Overview ¶
Package gameloop 是一个「机制而非策略」的定步长游戏循环原语,用于在 beauty 上 搭帧同步(lockstep)/状态同步的服务端骨架。
它只负责四件苦活:
- 定步长 tick:按固定频率驱动逻辑帧(lockstep 的命脉是「所有端帧率一致」);
- 输入聚合:并发收集各玩家输入,每帧原子地取走「上一帧以来的全部输入」;
- 扇出下发:把 OnTick 产出的东西经 stream.Broadcaster 推给所有订阅连接;
- 生命周期:结构上满足 beauty.Service(Start/String)+ ReadyNotifier,可直接 beauty.WithService(room) 挂进框架,随 app 优雅停机。
它刻意「不懂」任何同步策略——帧同步 vs 状态同步、确定性、序列化、快照/增量、 AOI,全在你的 Handler.OnTick 里决定。这条边界正是它保持轻量、不膨胀成「同步 引擎」的原因(本包仅依赖 pkg/stream)。
连接层不在本包职责内:调用方用 pkg/ws 把连接的「收到消息→Push」「订阅→写回」 接上即可(见 examples/gameloop 的 lockstep demo)。
Index ¶
- type Handler
- type HandlerFunc
- type Option
- type PlayerInput
- type Room
- func (r *Room[In, Out]) Frame() uint64
- func (r *Room[In, Out]) Push(player string, in In)
- func (r *Room[In, Out]) PushInput(in PlayerInput[In])
- func (r *Room[In, Out]) Ready() <-chan struct{}
- func (r *Room[In, Out]) Start(ctx context.Context) error
- func (r *Room[In, Out]) String() string
- func (r *Room[In, Out]) Subscribe(ctx context.Context) (<-chan Out, func())
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Handler ¶
type Handler[In any, Out any] interface { OnTick(frame uint64, inputs []PlayerInput[In]) []Out }
Handler 定义每帧要做什么。OnTick 在 tick goroutine 内被串行调用: frame 是自增帧号(从 1 起),inputs 是「上一帧以来收集到的全部玩家输入」 (可能为空)。返回的每个 Out 都会被 Publish 给所有订阅者。
- 帧同步:原样把 inputs 打包成一帧广播(客户端拿到全体输入后确定性重放);
- 状态同步:在这里跑服务器权威模拟,产出快照/增量(可配合 pkg/spatial 做 AOI)。
type HandlerFunc ¶
type HandlerFunc[In any, Out any] func(frame uint64, inputs []PlayerInput[In]) []Out
HandlerFunc 把普通函数适配成 Handler。
func (HandlerFunc[In, Out]) OnTick ¶
func (f HandlerFunc[In, Out]) OnTick(frame uint64, inputs []PlayerInput[In]) []Out
OnTick 实现 Handler。
type Option ¶
type Option func(*config)
Option 配置 Room。
func WithBufferSize ¶
WithBufferSize 设置每个订阅连接的下发队列容量(默认 64)。队列写满时按 stream.Broadcaster 的默认策略丢最旧——慢客户端不拖垮 tick 循环与其他连接。
type PlayerInput ¶
type PlayerInput[In any] struct { Player string `json:"player"` Input In `json:"input"` ClientFrame uint64 `json:"client_frame,omitempty"` // 客户端逻辑帧(延迟补偿) ReceivedAt time.Time `json:"-"` // 服务器收到时刻 }
PlayerInput 是某个玩家在某一帧提交的一条输入。
type Room ¶
Room 是一个定步长游戏循环(一个「房间」)。零值不可用,用 New 构造。并发安全。
func New ¶
func New[In any, Out any](rate time.Duration, handler Handler[In, Out], opts ...Option) *Room[In, Out]
New 创建一个房间。rate 是逻辑帧间隔(如 50ms ≈ 20Hz);rate<=0 时取 50ms。 handler 定义每帧行为。
func (*Room[In, Out]) Push ¶
Push 提交一条玩家输入(线程安全)。它会在下一个 tick 被 OnTick 收到。 连接的读循环里,每收到一条客户端消息就 Push 一次。
func (*Room[In, Out]) PushInput ¶ added in v0.8.0
func (r *Room[In, Out]) PushInput(in PlayerInput[In])
PushInput 提交带 clientFrame/ReceivedAt 的完整输入(延迟补偿场景)。
func (*Room[In, Out]) Ready ¶
func (r *Room[In, Out]) Ready() <-chan struct{}
Ready 在 tick 循环启动后关闭——满足 beauty.ReadyNotifier。