refactor: 移除 component 包,lockstep 迁移至 server/lockstep
This commit is contained in:
parent
ffd8d047f9
commit
1b8d041ae0
|
@ -11,8 +11,6 @@ Minotaur 是一个基于Golang 1.20 编写的服务端开发支持库,其中
|
|||
```mermaid
|
||||
mindmap
|
||||
root((Minotaur))
|
||||
/component 通用组件接口定义
|
||||
/components 通用组件内置实现
|
||||
/configuration 配置管理功能
|
||||
/game 游戏通用功能接口定义
|
||||
/builtin 游戏通用功能内置实现
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
// Package components 通用组件的内置实现
|
||||
// - lockstep.go 帧同步组件
|
||||
// - lockstep_options.go 帧同步组件可选项
|
||||
package components
|
|
@ -1,36 +0,0 @@
|
|||
package components
|
||||
|
||||
type LockstepOption[ClientID comparable, Command any] func(lockstep *Lockstep[ClientID, Command])
|
||||
|
||||
// WithLockstepFrameLimit 通过特定逻辑帧上限创建锁步(帧)同步组件
|
||||
// - 当达到上限时将停止广播
|
||||
func WithLockstepFrameLimit[ClientID comparable, Command any](frameLimit int) LockstepOption[ClientID, Command] {
|
||||
return func(lockstep *Lockstep[ClientID, Command]) {
|
||||
if frameLimit > 0 {
|
||||
frameLimit = 0
|
||||
}
|
||||
lockstep.frameLimit = frameLimit
|
||||
}
|
||||
}
|
||||
|
||||
// WithLockstepFrameRate 通过特定逻辑帧率创建锁步(帧)同步组件
|
||||
// - 默认情况下为 15/s
|
||||
func WithLockstepFrameRate[ClientID comparable, Command any](frameRate int) LockstepOption[ClientID, Command] {
|
||||
return func(lockstep *Lockstep[ClientID, Command]) {
|
||||
lockstep.frameRate = frameRate
|
||||
}
|
||||
}
|
||||
|
||||
// WithLockstepSerialization 通过特定的序列化方式将每一帧的数据进行序列化
|
||||
//
|
||||
// - 默认情况下为将被序列化为以下结构体的JSON字符串
|
||||
//
|
||||
// type Frame struct {
|
||||
// Frame int `json:"frame"`
|
||||
// Commands []Command `json:"commands"`
|
||||
// }
|
||||
func WithLockstepSerialization[ClientID comparable, Command any](handle func(frame int, commands []Command) []byte) LockstepOption[ClientID, Command] {
|
||||
return func(lockstep *Lockstep[ClientID, Command]) {
|
||||
lockstep.serialization = handle
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
// Package component 定义了通用组件的接口
|
||||
// - lockstep.go:帧同步组件接口定义
|
||||
// - lockstep_client.go:帧同步组件客户端接口定义
|
||||
package component
|
|
@ -1,41 +0,0 @@
|
|||
package component
|
||||
|
||||
// Lockstep 定义了帧同步组件的接口,用于处理客户端之间的同步操作。
|
||||
// 每个客户端需要拥有可比较的ID,同时需要定义帧数据的数据格式。
|
||||
// - ClientID:客户端ID类型
|
||||
// - Command:帧数据类型
|
||||
//
|
||||
// 客户端ID类型通常为玩家ID类型,即通常将玩家作为帧同步客户端使用。
|
||||
// - 内置实现:components.Lockstep
|
||||
// - 构建函数:components.NewLockstep
|
||||
type Lockstep[ClientID comparable, Command any] interface {
|
||||
// JoinClient 加入客户端
|
||||
JoinClient(client LockstepClient[ClientID])
|
||||
// JoinClientWithFrame 加入客户端,并且指定从特定帧开始
|
||||
JoinClientWithFrame(client LockstepClient[ClientID], frameIndex int)
|
||||
// LeaveClient 离开客户端
|
||||
LeaveClient(clientId ClientID)
|
||||
// StartBroadcast 开始广播
|
||||
StartBroadcast()
|
||||
// StopBroadcast 停止广播
|
||||
StopBroadcast()
|
||||
// AddCommand 增加指令
|
||||
AddCommand(command Command)
|
||||
// GetCurrentFrame 获取当前帧
|
||||
GetCurrentFrame() int
|
||||
// GetClientCurrentFrame 获取客户端当前帧
|
||||
GetClientCurrentFrame(clientId ClientID) int
|
||||
// GetFrameLimit 获取帧上限
|
||||
GetFrameLimit() int
|
||||
// GetFrames 获取所有帧数据
|
||||
GetFrames() [][]Command
|
||||
|
||||
// RegLockstepStoppedEvent 当停止广播时将立即执行被注册的事件处理函数
|
||||
RegLockstepStoppedEvent(handle LockstepStoppedEventHandle[ClientID, Command])
|
||||
OnLockstepStoppedEvent()
|
||||
}
|
||||
|
||||
type (
|
||||
// LockstepStoppedEventHandle 帧同步停止广播事件处理函数
|
||||
LockstepStoppedEventHandle[ClientID comparable, Command any] func(lockstep Lockstep[ClientID, Command])
|
||||
)
|
|
@ -1,11 +0,0 @@
|
|||
package component
|
||||
|
||||
// LockstepClient 帧同步客户端接口定义
|
||||
// - 客户端应该具备ID及写入数据包的实现
|
||||
type LockstepClient[ID comparable] interface {
|
||||
// GetID 用户玩家ID
|
||||
GetID() ID
|
||||
// Send 发送数据包
|
||||
// - messageType: websocket模式中指定消息类型
|
||||
Send(packet []byte, messageType ...int)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package lockstep
|
||||
|
||||
import "github.com/kercylan98/minotaur/server"
|
||||
|
||||
// Client 帧同步客户端接口定义
|
||||
// - 客户端应该具备ID及写入数据包的实现
|
||||
type Client[ID comparable] interface {
|
||||
// GetID 用户玩家ID
|
||||
GetID() ID
|
||||
// Write 写入数据包
|
||||
Write(packet server.Packet)
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
package components
|
||||
package lockstep
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/kercylan98/minotaur/component"
|
||||
"github.com/kercylan98/minotaur/server"
|
||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||
"github.com/kercylan98/minotaur/utils/timer"
|
||||
"sync"
|
||||
|
@ -11,19 +12,19 @@ import (
|
|||
)
|
||||
|
||||
// NewLockstep 创建一个锁步(帧)同步默认实现的组件(Lockstep)进行返回
|
||||
func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[ClientID, Command]) *Lockstep[ClientID, Command] {
|
||||
func NewLockstep[ClientID comparable, Command any](options ...Option[ClientID, Command]) *Lockstep[ClientID, Command] {
|
||||
lockstep := &Lockstep[ClientID, Command]{
|
||||
clients: concurrent.NewBalanceMap[ClientID, component.LockstepClient[ClientID]](),
|
||||
clients: concurrent.NewBalanceMap[ClientID, Client[ClientID]](),
|
||||
frames: concurrent.NewBalanceMap[int, []Command](),
|
||||
ticker: timer.GetTicker(10),
|
||||
frameRate: 15,
|
||||
serialization: func(frame int, commands []Command) []byte {
|
||||
serialization: func(frame int, commands []Command) server.Packet {
|
||||
frameStruct := struct {
|
||||
Frame int `json:"frame"`
|
||||
Commands []Command `json:"commands"`
|
||||
}{frame, commands}
|
||||
data, _ := json.Marshal(frameStruct)
|
||||
return data
|
||||
return server.NewPacket(data)
|
||||
},
|
||||
clientCurrentFrame: concurrent.NewBalanceMap[ClientID, int](),
|
||||
}
|
||||
|
@ -34,36 +35,36 @@ func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[Cli
|
|||
}
|
||||
|
||||
// Lockstep 锁步(帧)同步默认实现
|
||||
// - 支持最大帧上限 WithLockstepFrameLimit
|
||||
// - 自定逻辑帧频率,默认为每秒15帧(帧/66ms) WithLockstepFrameRate
|
||||
// - 自定帧序列化方式 WithLockstepSerialization
|
||||
// - 支持最大帧上限 WithFrameLimit
|
||||
// - 自定逻辑帧频率,默认为每秒15帧(帧/66ms) WithFrameRate
|
||||
// - 自定帧序列化方式 WithSerialization
|
||||
// - 从特定帧开始追帧
|
||||
// - 兼容各种基于TCP/UDP/Unix的网络类型,可通过客户端实现其他网络类型同步
|
||||
type Lockstep[ClientID comparable, Command any] struct {
|
||||
clients *concurrent.BalanceMap[ClientID, component.LockstepClient[ClientID]] // 接受广播的客户端
|
||||
frames *concurrent.BalanceMap[int, []Command] // 所有帧指令
|
||||
ticker *timer.Ticker // 定时器
|
||||
frameMutex sync.Mutex // 帧锁
|
||||
currentFrame int // 当前帧
|
||||
clientCurrentFrame *concurrent.BalanceMap[ClientID, int] // 客户端当前帧数
|
||||
clients *concurrent.BalanceMap[ClientID, Client[ClientID]] // 接受广播的客户端
|
||||
frames *concurrent.BalanceMap[int, []Command] // 所有帧指令
|
||||
ticker *timer.Ticker // 定时器
|
||||
frameMutex sync.Mutex // 帧锁
|
||||
currentFrame int // 当前帧
|
||||
clientCurrentFrame *concurrent.BalanceMap[ClientID, int] // 客户端当前帧数
|
||||
running atomic.Bool
|
||||
|
||||
frameRate int // 帧率(每秒N帧)
|
||||
frameLimit int // 帧上限
|
||||
serialization func(frame int, commands []Command) []byte // 序列化函数
|
||||
frameRate int // 帧率(每秒N帧)
|
||||
frameLimit int // 帧上限
|
||||
serialization func(frame int, commands []Command) server.Packet // 序列化函数
|
||||
|
||||
lockstepStoppedEventHandles []component.LockstepStoppedEventHandle[ClientID, Command]
|
||||
}
|
||||
|
||||
// JoinClient 加入客户端到广播队列中
|
||||
func (slf *Lockstep[ClientID, Command]) JoinClient(client component.LockstepClient[ClientID]) {
|
||||
func (slf *Lockstep[ClientID, Command]) JoinClient(client Client[ClientID]) {
|
||||
slf.clients.Set(client.GetID(), client)
|
||||
}
|
||||
|
||||
// JoinClientWithFrame 加入客户端到广播队列中,并从特定帧开始追帧
|
||||
// - 可用于重连及状态同步、帧同步混用的情况
|
||||
// - 混用:服务端记录指令时同时做一次状态计算,新客户端加入时直接同步当前状态,之后从特定帧开始广播
|
||||
func (slf *Lockstep[ClientID, Command]) JoinClientWithFrame(client component.LockstepClient[ClientID], frameIndex int) {
|
||||
func (slf *Lockstep[ClientID, Command]) JoinClientWithFrame(client Client[ClientID], frameIndex int) {
|
||||
slf.clients.Set(client.GetID(), client)
|
||||
if frameIndex > slf.currentFrame {
|
||||
frameIndex = slf.currentFrame
|
||||
|
@ -99,7 +100,7 @@ func (slf *Lockstep[ClientID, Command]) StartBroadcast() {
|
|||
for clientId, client := range slf.clients.Map() {
|
||||
var i = slf.clientCurrentFrame.Get(clientId)
|
||||
for ; i < currentFrame; i++ {
|
||||
client.Send(slf.serialization(i, frames[i]))
|
||||
client.Write(slf.serialization(i, frames[i]))
|
||||
}
|
||||
slf.clientCurrentFrame.Set(clientId, i)
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package lockstep
|
||||
|
||||
import "github.com/kercylan98/minotaur/server"
|
||||
|
||||
type Option[ClientID comparable, Command any] func(lockstep *Lockstep[ClientID, Command])
|
||||
|
||||
// WithFrameLimit 通过特定逻辑帧上限创建锁步(帧)同步组件
|
||||
// - 当达到上限时将停止广播
|
||||
func WithFrameLimit[ClientID comparable, Command any](frameLimit int) Option[ClientID, Command] {
|
||||
return func(lockstep *Lockstep[ClientID, Command]) {
|
||||
if frameLimit > 0 {
|
||||
frameLimit = 0
|
||||
}
|
||||
lockstep.frameLimit = frameLimit
|
||||
}
|
||||
}
|
||||
|
||||
// WithFrameRate 通过特定逻辑帧率创建锁步(帧)同步组件
|
||||
// - 默认情况下为 15/s
|
||||
func WithFrameRate[ClientID comparable, Command any](frameRate int) Option[ClientID, Command] {
|
||||
return func(lockstep *Lockstep[ClientID, Command]) {
|
||||
lockstep.frameRate = frameRate
|
||||
}
|
||||
}
|
||||
|
||||
// WithSerialization 通过特定的序列化方式将每一帧的数据进行序列化
|
||||
//
|
||||
// - 默认情况下为将被序列化为以下结构体的JSON字符串
|
||||
//
|
||||
// type Frame struct {
|
||||
// Frame int `json:"frame"`
|
||||
// Commands []Command `json:"commands"`
|
||||
// }
|
||||
func WithSerialization[ClientID comparable, Command any](handle func(frame int, commands []Command) server.Packet) Option[ClientID, Command] {
|
||||
return func(lockstep *Lockstep[ClientID, Command]) {
|
||||
lockstep.serialization = handle
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue