diff --git a/modular/dimension.go b/modular/dimension.go new file mode 100644 index 0000000..0c98e8c --- /dev/null +++ b/modular/dimension.go @@ -0,0 +1,40 @@ +package modular + +// Dimension 维度接口 +// - 维度与服务的区别在于,维度是对非全局性的服务进行抽象,例如:依赖特定游戏房间的局内玩家管理服务 +type Dimension[Owner any] interface { + // OnInit 服务初始化阶段,该阶段不应该依赖其他任何服务 + OnInit(owner Owner) error + + // OnPreload 预加载阶段,在进入该阶段时,所有服务已经初始化完成,可在该阶段注入其他服务的依赖 + OnPreload() error + + // OnMount 挂载阶段,该阶段所有服务本身及依赖的服务都已经初始化完成,可在该阶段进行服务功能的定义 + OnMount() error +} + +// RunDimensions 运行维度 +func RunDimensions[Owner any](owner Owner, dimensions ...Dimension[Owner]) error { + // OnInit + for _, dimension := range dimensions { + if err := dimension.OnInit(owner); err != nil { + return err + } + } + + // OnPreload + for _, dimension := range dimensions { + if err := dimension.OnPreload(); err != nil { + return err + } + } + + // OnMount + for _, dimension := range dimensions { + if err := dimension.OnMount(); err != nil { + return err + } + } + + return nil +} diff --git a/modular/example/internal/dimension/core/events.go b/modular/example/internal/dimension/core/events.go new file mode 100644 index 0000000..f6f5f6f --- /dev/null +++ b/modular/example/internal/dimension/core/events.go @@ -0,0 +1,4 @@ +package core + +type Events struct { +} diff --git a/modular/example/internal/dimension/core/room.go b/modular/example/internal/dimension/core/room.go new file mode 100644 index 0000000..dbf7899 --- /dev/null +++ b/modular/example/internal/dimension/core/room.go @@ -0,0 +1,9 @@ +package core + +import "github.com/kercylan98/minotaur/modular/example/internal/dimension/dimensions/exposes" + +type Room struct { + RoomId int64 + *Events + exposes.VisitorsExpose +} diff --git a/modular/example/internal/dimension/dimension.go b/modular/example/internal/dimension/dimension.go new file mode 100644 index 0000000..5f75d6f --- /dev/null +++ b/modular/example/internal/dimension/dimension.go @@ -0,0 +1,20 @@ +package dimension + +import ( + "github.com/kercylan98/minotaur/modular" + "github.com/kercylan98/minotaur/modular/example/internal/dimension/core" + "github.com/kercylan98/minotaur/modular/example/internal/dimension/dimensions/dimensions/visitors" +) + +func New(roomId int64) error { + visitorsDimension := new(visitors.Dimension) + + return modular.RunDimensions(&core.Room{ + RoomId: roomId, + Events: &core.Events{}, + + VisitorsExpose: visitorsDimension, + }, + visitorsDimension, + ) +} diff --git a/modular/example/internal/dimension/dimensions/dimensions/visitors/visitors.go b/modular/example/internal/dimension/dimensions/dimensions/visitors/visitors.go new file mode 100644 index 0000000..0e1a91f --- /dev/null +++ b/modular/example/internal/dimension/dimensions/dimensions/visitors/visitors.go @@ -0,0 +1,64 @@ +package visitors + +import ( + "fmt" + "github.com/kercylan98/minotaur/modular/example/internal/dimension/core" + "github.com/kercylan98/minotaur/modular/example/internal/dimension/dimensions/exposes" + "github.com/kercylan98/minotaur/modular/example/internal/dimension/dimensions/models" + "github.com/kercylan98/minotaur/utils/collection" +) + +type Dimension struct { + *core.Room // 房间 Id + visitors map[string]*models.VisitorsMember // 所有访客 + visitorIds []string // 所有访客 OpenId +} + +func (d *Dimension) OnInit(owner *core.Room) error { + exposes.Visitors = d + d.Room = owner + d.visitors = make(map[string]*models.VisitorsMember) + fmt.Println("visitors dimension initialized") + return nil +} + +func (d *Dimension) OnPreload() error { + fmt.Println("visitors dimension preloaded") + return nil +} + +func (d *Dimension) OnMount() error { + fmt.Println("visitors dimension mounted") + return nil +} + +func (d *Dimension) Count() int { + return len(d.visitors) +} + +func (d *Dimension) OpenIds() []string { + return d.visitorIds +} + +func (d *Dimension) Has(openId string) bool { + return collection.KeyInMap(d.visitors, openId) +} + +func (d *Dimension) Del(openId string) { + member := d.Get(openId) + if member == nil { + return + } + delete(d.visitors, openId) + collection.DropSliceByIndices(&d.visitorIds, member.OpenIdIdx) +} + +func (d *Dimension) Get(openId string) *models.VisitorsMember { + return d.visitors[openId] +} + +func (d *Dimension) Add(member *models.VisitorsMember) { + member.OpenIdIdx = len(d.visitorIds) + d.visitorIds = append(d.visitorIds, member.OpenId) + d.visitors[member.OpenId] = member +} diff --git a/modular/example/internal/dimension/dimensions/exposes/visitors.go b/modular/example/internal/dimension/dimensions/exposes/visitors.go new file mode 100644 index 0000000..f21281c --- /dev/null +++ b/modular/example/internal/dimension/dimensions/exposes/visitors.go @@ -0,0 +1,27 @@ +package exposes + +import ( + "github.com/kercylan98/minotaur/modular/example/internal/dimension/dimensions/models" +) + +var Visitors VisitorsExpose + +type VisitorsExpose interface { + // Count 访客数量 + Count() int + + // OpenIds 访客 OpenId 列表 + OpenIds() []string + + // Has 是否存在指定 OpenId 的访客 + Has(openId string) bool + + // Del 删除指定 OpenId 的访客 + Del(openId string) + + // Get 获取指定 OpenId 的访客 + Get(openId string) *models.VisitorsMember + + // Add 添加访客 + Add(member *models.VisitorsMember) +} diff --git a/modular/example/internal/dimension/dimensions/models/visitors.go b/modular/example/internal/dimension/dimensions/models/visitors.go new file mode 100644 index 0000000..20592bc --- /dev/null +++ b/modular/example/internal/dimension/dimensions/models/visitors.go @@ -0,0 +1,7 @@ +package models + +// VisitorsMember 访客成员 +type VisitorsMember struct { + OpenId string // 访客成员 OpenId + OpenIdIdx int // 访客成员 OpenId 索引 +} diff --git a/modular/example/main.go b/modular/example/main.go index 3ff1e99..683a19b 100644 --- a/modular/example/main.go +++ b/modular/example/main.go @@ -2,6 +2,7 @@ package main import ( "github.com/kercylan98/minotaur/modular" + "github.com/kercylan98/minotaur/modular/example/internal/dimension" "github.com/kercylan98/minotaur/modular/example/internal/service/services/attack" "github.com/kercylan98/minotaur/modular/example/internal/service/services/login" "github.com/kercylan98/minotaur/modular/example/internal/service/services/server" @@ -13,5 +14,11 @@ func main() { new(server.Service), new(login.Service), ) + + err := dimension.New(1) // generate a room + if err != nil { + panic(err) + } + modular.Run() } diff --git a/modular/modular.go b/modular/modular.go index e700368..66614d4 100644 --- a/modular/modular.go +++ b/modular/modular.go @@ -3,7 +3,6 @@ package modular import ( "fmt" "github.com/kercylan98/minotaur/utils/log" - "reflect" "sync" ) @@ -27,14 +26,12 @@ func (m *modular) RegisterServices(s ...Service) { func Run() { m := application var names = make(map[string]bool) - var tvm = make(map[reflect.Type]reflect.Value) for i := 0; i < len(m.registerServices); i++ { s := newService(m.registerServices[i]) if names[s.name] { panic(fmt.Errorf("service %s is already registered", s.name)) } names[s.name] = true - tvm[s.vof.Type()] = s.vof m.services = append(m.services, s) } diff --git a/utils/collection/listings/priority_slice.go b/utils/collection/listings/priority_slice.go index eb77058..6877b48 100644 --- a/utils/collection/listings/priority_slice.go +++ b/utils/collection/listings/priority_slice.go @@ -5,7 +5,7 @@ import ( "sort" ) -// NewPrioritySlice 创建一个优先级切片 +// NewPrioritySlice 创建一个优先级切片,优先级越低越靠前 func NewPrioritySlice[V any](lengthAndCap ...int) *PrioritySlice[V] { p := &PrioritySlice[V]{} if len(lengthAndCap) > 0 { @@ -19,7 +19,7 @@ func NewPrioritySlice[V any](lengthAndCap ...int) *PrioritySlice[V] { return p } -// PrioritySlice 是一个优先级切片 +// PrioritySlice 是一个优先级切片,优先级越低越靠前 type PrioritySlice[V any] struct { items []*priorityItem[V] } diff --git a/utils/geometry/position.go b/utils/geometry/position.go index c50caca..124fd93 100644 --- a/utils/geometry/position.go +++ b/utils/geometry/position.go @@ -109,6 +109,19 @@ func (slf Point[V]) Max(point Point[V]) Point[V] { return NewPoint(x, y) } +// Move 返回向特定角度移动特定距离后的新的位置,其中 angle 期待的角度范围是 -180~180 +func (slf Point[V]) Move(angle, direction V) Point[V] { + df := float64(direction) + // 将角度转换为弧度 + radian := float64(angle) * (math.Pi / 180.0) + + // 计算新的坐标 + newX := float64(slf.GetX()) + df*math.Cos(radian) + newY := float64(slf.GetY()) + df*math.Sin(radian) + + return NewPoint(V(newX), V(newY)) +} + // Min 返回两个位置中每个维度的最小值组成的新的位置 func (slf Point[V]) Min(point Point[V]) Point[V] { x, y := slf.GetXY() diff --git a/utils/geometry/simple_circle.go b/utils/geometry/simple_circle.go index 9cc93a7..e3fff18 100644 --- a/utils/geometry/simple_circle.go +++ b/utils/geometry/simple_circle.go @@ -29,6 +29,11 @@ func (sc SimpleCircle[V]) String() string { return fmt.Sprintf("SimpleCircle{centroid: %v, %v, radius: %v}", sc.centroid.GetX(), sc.centroid.GetY(), sc.radius) } +// IsZero 反映该圆形是否为零值的无效圆形 +func (sc SimpleCircle[V]) IsZero() bool { + return sc.radius == V(0) +} + // Centroid 获取圆形质心位置 func (sc SimpleCircle[V]) Centroid() Point[V] { return sc.centroid @@ -74,15 +79,18 @@ func (sc SimpleCircle[V]) Area() V { return sc.radius * sc.radius } -// Projection 获取圆形投影到另一个圆形的特定比例下的位置和半径 +// Projection 获取圆形投影到另一个圆形的特定比例下的位置和半径(同心合并) func (sc SimpleCircle[V]) Projection(circle SimpleCircle[V], ratio float64) SimpleCircle[V] { // 计算圆心朝目标按比例移动后的位置 - distance := float64(sc.Centroid().Distance(circle.centroid)) + distance := sc.Centroid().Distance(circle.Centroid()) moveDistance := distance * ratio - newX := float64(sc.CentroidX()) + moveDistance*(float64(circle.CentroidX())-float64(sc.CentroidX()))/distance - newY := float64(sc.CentroidY()) + moveDistance*(float64(circle.CentroidY())-float64(sc.CentroidY()))/distance + newX := float64(circle.CentroidX()) + moveDistance*(float64(sc.CentroidX())-float64(circle.CentroidX()))/distance + newY := float64(circle.CentroidY()) + moveDistance*(float64(sc.CentroidY())-float64(circle.CentroidY()))/distance - return NewSimpleCircle(V(float64(sc.radius)*ratio), NewPoint(V(newX), V(newY))) + // 计算新的半径 + newRadius := float64(sc.radius) + (float64(circle.radius)-float64(sc.radius))*(1-ratio) + + return NewSimpleCircle(V(newRadius), NewPoint(V(newX), V(newY))) } // Length 获取圆的周长 diff --git a/utils/random/number.go b/utils/random/number.go index 2a42e4a..8ad0c13 100644 --- a/utils/random/number.go +++ b/utils/random/number.go @@ -13,6 +13,30 @@ func Int64(min int64, max int64) int64 { return min + rand.Int63n(max+1-min) } +// Int32 返回一个介于min和max之间的的int32类型的随机数。 +func Int32(min int32, max int32) int32 { + if min == max { + return min + } + return int32(Int64(int64(min), int64(max))) +} + +// Float32Range 返回一个介于min和max之间的的float32类型的随机数。 +func Float32Range(min float32, max float32) float32 { + if min == max { + return min + } + return min + rand.Float32()*(max-min) +} + +// Float64Range 返回一个介于min和max之间的的float64类型的随机数。 +func Float64Range(min float64, max float64) float64 { + if min == max { + return min + } + return min + rand.Float64()*(max-min) +} + // Int 返回一个介于min和max之间的的int类型的随机数。 func Int(min int, max int) int { if min == max { diff --git a/utils/timer/options.go b/utils/timer/options.go index 133b4b7..255f8c6 100644 --- a/utils/timer/options.go +++ b/utils/timer/options.go @@ -2,10 +2,12 @@ package timer type Option func(ticker *Ticker) -// WithCaller 通过其他的 handle 执行 Caller -func WithCaller(handle func(name string, caller func())) Option { +// WithCaller 通过其他的 handler 执行 Caller +func WithCaller(handler func(name string, caller func())) Option { return func(ticker *Ticker) { - ticker.handle = handle + ticker.lock.Lock() + ticker.handler = handler + ticker.lock.Unlock() } } diff --git a/utils/timer/ticker.go b/utils/timer/ticker.go index 337419c..c739a12 100644 --- a/utils/timer/ticker.go +++ b/utils/timer/ticker.go @@ -16,8 +16,8 @@ type Ticker struct { timers map[string]*Scheduler lock sync.RWMutex - handle func(name string, caller func()) - mark string + handler func(name string, caller func()) + mark string } // Mark 获取定时器的标记 @@ -33,6 +33,7 @@ func (slf *Ticker) Release() { slf.lock.Lock() slf.mark = "" + slf.handler = nil for name, scheduler := range slf.timers { scheduler.close() delete(slf.timers, name) @@ -94,8 +95,8 @@ func (slf *Ticker) CronByInstantly(name, expression string, handleFunc interface f := reflect.ValueOf(handleFunc) slf.lock.RLock() defer slf.lock.RUnlock() - if slf.handle != nil { - slf.handle(name, func() { + if slf.handler != nil { + slf.handler(name, func() { f.Call(values) }) } else { @@ -147,9 +148,13 @@ func (slf *Ticker) loop(name string, after, interval time.Duration, expr *cronex slf.lock.Lock() slf.timers[name] = scheduler - if slf.handle != nil { + if slf.handler != nil { scheduler.timer = slf.wheel.ScheduleFunc(scheduler, func() { - slf.handle(scheduler.Name(), scheduler.Caller) + slf.lock.RLock() + defer slf.lock.RUnlock() + if slf.handler != nil { + slf.handler(scheduler.Name(), scheduler.Caller) + } }) } else { scheduler.timer = slf.wheel.ScheduleFunc(scheduler, scheduler.Caller) diff --git a/utils/times/format.go b/utils/times/format.go index 7f549c7..8b1a474 100644 --- a/utils/times/format.go +++ b/utils/times/format.go @@ -2,6 +2,7 @@ package times import ( "fmt" + "github.com/kercylan98/minotaur/utils/generic" "math" "strconv" "time" @@ -60,3 +61,8 @@ func IntervalFormat(time1, time2 time.Time) string { } return res } + +// ToSecDuration 转换为秒级 time.Duration +func ToSecDuration[V generic.Number](v V) time.Duration { + return Second * time.Duration(int64(v)) +}