jl-ecs/world.go

296 lines
6.0 KiB
Go
Raw Normal View History

2023-08-04 11:02:08 +08:00
package ecs
import (
"context"
2023-08-04 11:02:08 +08:00
"fmt"
"log/slog"
"math"
"runtime/debug"
2023-08-04 11:02:08 +08:00
"time"
"github.com/yohamta/donburi"
"github.com/yohamta/donburi/features/events"
)
type WorldState int
type WorldId = donburi.WorldId
2023-08-04 11:02:08 +08:00
const (
WorldInit WorldState = 0
WorldRunning WorldState = 1
WorldPause WorldState = 2
WorldError WorldState = 3
WorldClosed WorldState = 4
2023-08-04 11:02:08 +08:00
)
type (
World interface {
donburi.World
// 世界运行间隔时间
Tick() int
2024-01-23 09:34:28 +08:00
// 获取世界状态
State() WorldState
// 启动世界
StartUp()
// 暂停世界
Pause()
// 恢复世界
Resume()
// 关闭世界
Close()
// 设置运行倍速
SetSpeed(speed float64) error
// 添加系统
AddSystem(sys ...ISystem)
// 在世界中执行处理逻辑(在世界运行线程中)
Execute(fn HandleFunc) error
}
// 处理函数
HandleFunc func()
// 世界状态变更消息
WorldStateChange struct {
OldState WorldState
NewState WorldState
}
)
2023-08-04 11:02:08 +08:00
// 世界状态变更事件
var WorldStateChangeEvent = NewEventType[WorldStateChange]()
2023-08-04 11:02:08 +08:00
type world struct {
donburi.World
systems []ISystem
state WorldState
tick int
ticker *time.Ticker
// 世界运行倍速
speed float64
// 下一帧系统需要执行的次数
times float64
2023-08-04 11:02:08 +08:00
// 待执行函数
toBeExecuteds chan HandleFunc
cancel context.CancelFunc
done chan struct{} // 服务协程退出信号
2023-08-04 11:02:08 +08:00
}
// 新建一个组件类型
2023-08-04 11:02:08 +08:00
func NewComponentType[T any](opts ...interface{}) *ComponentType[T] {
ct := donburi.NewComponentType[T](opts...)
return &ComponentType[T]{ct}
}
// 新建一个标签
func NewTag() *ComponentType[struct{}] {
return NewComponentType[struct{}]()
}
// 将entity列表转换为entry列表
func Entries(w World, entities []donburi.Entity) []*Entry {
entries := make([]*Entry, len(entities))
for i, entity := range entities {
entries[i] = w.Entry(entity)
}
return entries
}
// 初始化一个新World
// tick 单位为ms且必须大于0,(小于15ms的值在Windows系统中会达不到Windows系统中系统中断好像默认是15.6ms也就是一秒最多64次)
2023-08-04 11:02:08 +08:00
func NewWorld(tick int) World {
if tick <= 0 {
2023-10-20 15:04:45 +08:00
panic(fmt.Errorf("创建World错误: tick必须大于0"))
}
2023-08-04 11:02:08 +08:00
return &world{
World: donburi.NewWorld(),
systems: make([]ISystem, 0),
state: WorldInit,
tick: tick,
ticker: time.NewTicker(time.Duration(tick) * time.Millisecond),
speed: 1,
times: 1,
toBeExecuteds: make(chan HandleFunc, 32),
done: make(chan struct{}),
2023-08-04 11:02:08 +08:00
}
}
2023-08-15 09:17:22 +08:00
func (w *world) Tick() int {
return w.tick
}
2023-08-04 11:02:08 +08:00
2024-01-23 09:34:28 +08:00
func (w *world) State() WorldState {
return w.state
}
2023-08-04 11:02:08 +08:00
// 添加系统
func (w *world) AddSystem(sys ...ISystem) {
w.systems = append(w.systems, sys...)
}
// 执行所有事件处理
func (w *world) ProcessAllEvents() {
events.ProcessAllEvents(w.World)
2023-08-04 11:02:08 +08:00
}
// 暂停世界
func (w *world) Pause() {
if w.state == WorldRunning {
w.updateState(WorldPause)
2023-08-04 11:02:08 +08:00
}
}
// 恢复世界运行
func (w *world) Resume() {
if w.state == WorldPause {
w.updateState(WorldRunning)
}
}
func (w *world) updateState(state WorldState) {
if w.state != state {
old := w.state
slog.Debug("世界状态变更", "oldstate", old, "state", state)
w.state = state
WorldStateChangeEvent.Publish(w, &WorldStateChange{
OldState: old,
NewState: state,
})
WorldStateChangeEvent.ProcessEvents(w)
2023-08-04 11:02:08 +08:00
}
}
const (
speedMin = 0.1
speedMax = 10
2023-08-04 11:02:08 +08:00
)
func WorldSpeedMax() float64 {
return speedMax
}
func WorldSpeedMin() float64 {
return speedMin
}
// 设置世界运行倍速
2023-08-04 11:02:08 +08:00
func (w *world) SetSpeed(speed float64) error {
if speed < speedMin || speed > speedMax {
return fmt.Errorf("世界倍速必须在[%f, %d]之间", speedMin, speedMax)
2023-08-04 11:02:08 +08:00
}
w.speed = speed
return nil
}
// 启动世界,世界逻辑开始执行且世界为运行状态
func (w *world) StartUp() {
if w.state == WorldInit { // 避免重复运行
slog.Debug("启动世界", "id", w.Id())
ctx, cancle := context.WithCancel(context.Background())
go w.run(ctx)
w.cancel = cancle
w.updateState(WorldRunning)
2023-08-04 11:02:08 +08:00
}
}
// 在世界线程执行逻辑
func (w *world) Execute(fn HandleFunc) error {
if w.state == WorldError {
return fmt.Errorf("世界运行异常,无法执行请求")
} else if w.state == WorldClosed {
return fmt.Errorf("世界已经关闭,无法执行请求")
}
w.toBeExecuteds <- fn
return nil
}
2023-08-04 11:02:08 +08:00
// 关闭世界
func (w *world) Close() {
if w.state == WorldInit {
slog.Debug("关闭世界", "id", w.Id())
w.updateState(WorldClosed)
return
}
w.cancel()
<-w.done
2023-08-04 11:02:08 +08:00
}
// 执行待处理方法
func (w *world) executeTodos() {
funcs := w.toBeExecuteds
2023-08-31 16:19:41 +08:00
for {
select {
case fn := <-funcs:
2023-08-31 16:19:41 +08:00
{
fn()
2023-08-31 16:19:41 +08:00
}
default:
return
}
}
}
// 世界循环
func (w *world) run(ctx context.Context) {
defer func() {
if err := recover(); err != nil {
w.exception(err)
slog.Error("世界运行异常", "error", err, "stack", string(debug.Stack()))
debug.PrintStack()
}
}()
defer close(w.done)
for range w.ticker.C {
select {
case <-ctx.Done():
w.close()
2023-08-04 11:02:08 +08:00
return
default:
2023-08-04 11:02:08 +08:00
}
// start := time.Now()
if w.state != WorldRunning { // 世界非运行状态
2023-08-04 11:02:08 +08:00
continue
}
if w.times >= 1 {
times := int(math.Floor(w.times))
for i := 0; i < times; i++ {
for _, sys := range w.systems {
sys.Update(w)
}
// 执行待执行逻辑
w.executeTodos()
// 处理所有事件
processAllEvents(w)
}
w.times = w.times - float64(times) + w.speed
} else {
w.times += w.speed
2023-08-04 11:02:08 +08:00
}
// dt := time.Since(start)
// slog.Info("仿真系统执行耗时:" + dt.Milliseconds() + "ms")
2023-08-04 11:02:08 +08:00
}
}
// 世界运行异常处理
func (w *world) exception(err any) {
// slog.Error("世界运行异常", "error", err, "stack", string(debug.Stack()))
w.updateState(WorldError)
// 关闭定时器
w.ticker.Stop()
// w.handleRequestAndEvent()
}
// 世界正常关闭逻辑
func (w *world) close() {
slog.Debug("关闭世界", "id", w.Id())
// 世界正常关闭
w.updateState(WorldClosed)
// 关闭定时器
w.ticker.Stop()
slog.Debug("世界关闭finish")
}