简单游戏demo实现

This commit is contained in:
kercylan98 2023-04-27 13:25:11 +08:00
parent a722ef46e2
commit 723e618df2
13 changed files with 216 additions and 67 deletions

View File

@ -0,0 +1 @@
简单游戏实现demo

View File

@ -0,0 +1,23 @@
package game
import (
"minotaur/game"
"minotaur/game/builtin"
"minotaur/server"
)
var (
Server *server.Server
Game *app
)
func init() {
Server = server.New(server.NetworkTCP)
Game = &app{
World: builtin.NewWorld[int64, *Player](0),
}
}
type app struct {
game.World[int64, *Player]
}

View File

@ -0,0 +1,43 @@
package game
import (
"log"
"minotaur/game"
"minotaur/game/builtin"
"minotaur/server"
"time"
)
func NewPlayer(id int64, conn *server.Conn) *Player {
player := &Player{
Player: builtin.NewPlayer[int64](id, conn),
}
gameplay := builtin.NewGameplay()
gameplayOver := builtin.NewGameplayOver()
gameplayTime := builtin.NewGameplayTime(gameplay, gameplayOver)
player.GameplayTime = gameplayTime
return player
}
type Player struct {
*builtin.Player[int64]
game.GameplayTime
}
func (slf *Player) Start() {
_ = slf.GameStart(func() error {
log.Println("game start, init map...")
return nil
})
}
func (slf *Player) onGameplayStart(t time.Time) {
slf.SetEndTime(t.Add(60 * time.Second))
log.Println("the game will end in 60 seconds")
}
func (slf *Player) onGameplayOver() {
log.Println("game over")
}

View File

@ -0,0 +1,39 @@
package game
import (
"minotaur/server"
"minotaur/utils/sole"
)
func init() {
Server.RegConnectionOpenedEvent(onConnectionOpened)
Game.RegPlayerLeaveWorldEvent(onConnectionClosed)
Server.RegConnectionReceivePacketEvent(onConnectionReceivePacket)
}
func onConnectionReceivePacket(conn *server.Conn, packet []byte) {
player := Game.World.GetPlayerWithConnID(conn.GetID())
if player == nil {
return
}
player.RegGameplayStartEvent(player.onGameplayStart)
player.RegGameplayOverEvent(player.onGameplayOver)
switch string(packet) {
case "start":
player.Start()
}
}
func onConnectionClosed(player *Player) {
Game.Leave(player.GetID())
player.Close()
}
func onConnectionOpened(conn *server.Conn) {
player := NewPlayer(sole.GetSync(), conn)
if err := Game.World.Join(player); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,9 @@
package main
import "minotaur/game/builtin/examples/game/game"
func main() {
if err := game.Server.Run(":9999"); err != nil {
panic(err)
}
}

View File

@ -19,6 +19,10 @@ func (slf *Player[ID]) GetID() ID {
return slf.id
}
func (slf *Player[ID]) GetConnID() string {
return slf.conn.GetID()
}
// Send 向该玩家发送数据
func (slf *Player[ID]) Send(packet []byte) error {
return slf.conn.Write(packet)

View File

@ -6,10 +6,11 @@ import (
"sync/atomic"
)
func NewWorld[PlayerID comparable](guid int64, options ...WorldOption[PlayerID]) *World[PlayerID] {
world := &World[PlayerID]{
func NewWorld[PlayerID comparable, Player game.Player[PlayerID]](guid int64, options ...WorldOption[PlayerID, Player]) *World[PlayerID, Player] {
world := &World[PlayerID, Player]{
guid: guid,
players: synchronization.NewMap[PlayerID, game.Player[PlayerID]](),
playersConn: synchronization.NewMap[string, Player](),
players: synchronization.NewMap[PlayerID, Player](),
playerActors: synchronization.NewMap[PlayerID, *synchronization.Map[int64, game.Actor]](),
owners: synchronization.NewMap[int64, PlayerID](),
actors: synchronization.NewMap[int64, game.Actor](),
@ -20,70 +21,75 @@ func NewWorld[PlayerID comparable](guid int64, options ...WorldOption[PlayerID])
return world
}
type World[PlayerID comparable] struct {
type World[PlayerID comparable, Player game.Player[PlayerID]] struct {
guid int64
actorGuid atomic.Int64
playerLimit int
players *synchronization.Map[PlayerID, game.Player[PlayerID]]
playersConn *synchronization.Map[string, Player]
players *synchronization.Map[PlayerID, Player]
playerActors *synchronization.Map[PlayerID, *synchronization.Map[int64, game.Actor]]
owners *synchronization.Map[int64, PlayerID]
actors *synchronization.Map[int64, game.Actor]
playerJoinWorldEventHandles []game.PlayerJoinWorldEventHandle[PlayerID]
playerLeaveWorldEventHandles []game.PlayerLeaveWorldEventHandle[PlayerID]
playerJoinWorldEventHandles []game.PlayerJoinWorldEventHandle[PlayerID, Player]
playerLeaveWorldEventHandles []game.PlayerLeaveWorldEventHandle[PlayerID, Player]
actorGeneratedEventHandles []game.ActorGeneratedEventHandle
actorAnnihilationEventHandles []game.ActorAnnihilationEventHandle
actorOwnerChangeEventHandles []game.ActorOwnerChangeEventHandle[PlayerID]
worldResetEventHandles []game.WorldResetEventHandle[PlayerID]
worldReleasedEventHandles []game.WorldReleaseEventHandle[PlayerID]
worldResetEventHandles []game.WorldResetEventHandle[PlayerID, Player]
worldReleasedEventHandles []game.WorldReleaseEventHandle[PlayerID, Player]
released atomic.Bool
}
func (slf *World[PlayerID]) GetGuid() int64 {
func (slf *World[PlayerID, Player]) GetGuid() int64 {
return slf.guid
}
func (slf *World[PlayerID]) GetPlayerLimit() int {
func (slf *World[PlayerID, Player]) GetPlayerLimit() int {
return slf.playerLimit
}
func (slf *World[PlayerID]) GetPlayer(id PlayerID) game.Player[PlayerID] {
func (slf *World[PlayerID, Player]) GetPlayerWithConnID(id string) Player {
return slf.playersConn.Get(id)
}
func (slf *World[PlayerID, Player]) GetPlayer(id PlayerID) Player {
return slf.players.Get(id)
}
func (slf *World[PlayerID]) GetPlayers() map[PlayerID]game.Player[PlayerID] {
func (slf *World[PlayerID, Player]) GetPlayers() map[PlayerID]Player {
return slf.players.Map()
}
func (slf *World[PlayerID]) GetActor(guid int64) game.Actor {
func (slf *World[PlayerID, Player]) GetActor(guid int64) game.Actor {
return slf.actors.Get(guid)
}
func (slf *World[PlayerID]) GetActors() map[int64]game.Actor {
func (slf *World[PlayerID, Player]) GetActors() map[int64]game.Actor {
return slf.actors.Map()
}
func (slf *World[PlayerID]) GetPlayerActor(id PlayerID, guid int64) game.Actor {
func (slf *World[PlayerID, Player]) GetPlayerActor(id PlayerID, guid int64) game.Actor {
if actors := slf.playerActors.Get(id); actors != nil {
return actors.Get(guid)
}
return nil
}
func (slf *World[PlayerID]) GetPlayerActors(id PlayerID) map[int64]game.Actor {
func (slf *World[PlayerID, Player]) GetPlayerActors(id PlayerID) map[int64]game.Actor {
return slf.playerActors.Get(id).Map()
}
func (slf *World[PlayerID]) IsExistPlayer(id PlayerID) bool {
func (slf *World[PlayerID, Player]) IsExistPlayer(id PlayerID) bool {
return slf.players.Exist(id)
}
func (slf *World[PlayerID]) IsExistActor(guid int64) bool {
func (slf *World[PlayerID, Player]) IsExistActor(guid int64) bool {
return slf.actors.Exist(guid)
}
func (slf *World[PlayerID]) IsOwner(id PlayerID, guid int64) bool {
func (slf *World[PlayerID, Player]) IsOwner(id PlayerID, guid int64) bool {
actors := slf.playerActors.Get(id)
if actors != nil {
return actors.Exist(guid)
@ -91,7 +97,7 @@ func (slf *World[PlayerID]) IsOwner(id PlayerID, guid int64) bool {
return false
}
func (slf *World[PlayerID]) Join(player game.Player[PlayerID]) error {
func (slf *World[PlayerID, Player]) Join(player Player) error {
if slf.released.Load() {
return ErrWorldReleased
}
@ -103,12 +109,14 @@ func (slf *World[PlayerID]) Join(player game.Player[PlayerID]) error {
actors = synchronization.NewMap[int64, game.Actor]()
slf.playerActors.Set(player.GetID(), actors)
}
slf.playersConn.Set(player.GetConnID(), player)
slf.OnPlayerJoinWorldEvent(player)
return nil
}
func (slf *World[PlayerID]) Leave(player game.Player[PlayerID]) {
if !slf.players.Exist(player.GetID()) {
func (slf *World[PlayerID, Player]) Leave(id PlayerID) {
player, exist := slf.players.GetExist(id)
if !exist {
return
}
slf.OnPlayerLeaveWorldEvent(player)
@ -118,16 +126,17 @@ func (slf *World[PlayerID]) Leave(player game.Player[PlayerID]) {
})
slf.playerActors.Delete(player.GetID())
slf.players.Delete(player.GetID())
slf.playersConn.Delete(player.GetConnID())
}
func (slf *World[PlayerID]) AddActor(actor game.Actor) {
func (slf *World[PlayerID, Player]) AddActor(actor game.Actor) {
guid := slf.actorGuid.Add(1)
actor.SetGuid(guid)
slf.actors.Set(actor.GetGuid(), actor)
slf.OnActorGeneratedEvent(actor)
}
func (slf *World[PlayerID]) RemoveActor(guid int64) {
func (slf *World[PlayerID, Player]) RemoveActor(guid int64) {
if actor, exist := slf.actors.GetExist(guid); exist {
slf.OnActorAnnihilationEvent(actor)
if id, exist := slf.owners.DeleteGetExist(guid); exist {
@ -137,7 +146,7 @@ func (slf *World[PlayerID]) RemoveActor(guid int64) {
}
}
func (slf *World[PlayerID]) SetActorOwner(guid int64, id PlayerID) {
func (slf *World[PlayerID, Player]) SetActorOwner(guid int64, id PlayerID) {
oldId, exist := slf.owners.GetExist(guid)
if exist && oldId == id {
return
@ -151,7 +160,7 @@ func (slf *World[PlayerID]) SetActorOwner(guid int64, id PlayerID) {
slf.OnActorOwnerChangeEvent(actor, oldId, id, false)
}
func (slf *World[PlayerID]) RemoveActorOwner(guid int64) {
func (slf *World[PlayerID, Player]) RemoveActorOwner(guid int64) {
id, exist := slf.owners.GetExist(guid)
if !exist {
return
@ -161,7 +170,7 @@ func (slf *World[PlayerID]) RemoveActorOwner(guid int64) {
slf.OnActorOwnerChangeEvent(slf.GetActor(guid), id, id, true)
}
func (slf *World[PlayerID]) Reset() {
func (slf *World[PlayerID, Player]) Reset() {
slf.players.Clear()
slf.playerActors.Range(func(id PlayerID, actors *synchronization.Map[int64, game.Actor]) {
actors.Clear()
@ -170,10 +179,11 @@ func (slf *World[PlayerID]) Reset() {
slf.owners.Clear()
slf.actors.Clear()
slf.actorGuid.Store(0)
slf.playersConn.Clear()
slf.OnWorldResetEvent()
}
func (slf *World[PlayerID]) Release() {
func (slf *World[PlayerID, Player]) Release() {
if !slf.released.Swap(true) {
slf.OnWorldReleaseEvent()
slf.Reset()
@ -190,71 +200,71 @@ func (slf *World[PlayerID]) Release() {
}
}
func (slf *World[PlayerID]) RegWorldResetEvent(handle game.WorldResetEventHandle[PlayerID]) {
func (slf *World[PlayerID, Player]) RegWorldResetEvent(handle game.WorldResetEventHandle[PlayerID, Player]) {
slf.worldResetEventHandles = append(slf.worldResetEventHandles, handle)
}
func (slf *World[PlayerID]) OnWorldResetEvent() {
func (slf *World[PlayerID, Player]) OnWorldResetEvent() {
for _, handle := range slf.worldResetEventHandles {
handle(slf)
}
}
func (slf *World[PlayerID]) RegWorldReleaseEvent(handle game.WorldReleaseEventHandle[PlayerID]) {
func (slf *World[PlayerID, Player]) RegWorldReleaseEvent(handle game.WorldReleaseEventHandle[PlayerID, Player]) {
slf.worldReleasedEventHandles = append(slf.worldReleasedEventHandles, handle)
}
func (slf *World[PlayerID]) OnWorldReleaseEvent() {
func (slf *World[PlayerID, Player]) OnWorldReleaseEvent() {
for _, handle := range slf.worldReleasedEventHandles {
handle(slf)
}
}
func (slf *World[PlayerID]) RegPlayerJoinWorldEvent(handle game.PlayerJoinWorldEventHandle[PlayerID]) {
func (slf *World[PlayerID, Player]) RegPlayerJoinWorldEvent(handle game.PlayerJoinWorldEventHandle[PlayerID, Player]) {
slf.playerJoinWorldEventHandles = append(slf.playerJoinWorldEventHandles, handle)
}
func (slf *World[PlayerID]) OnPlayerJoinWorldEvent(player game.Player[PlayerID]) {
func (slf *World[PlayerID, Player]) OnPlayerJoinWorldEvent(player Player) {
for _, handle := range slf.playerJoinWorldEventHandles {
handle(player)
}
}
func (slf *World[PlayerID]) RegPlayerLeaveWorldEvent(handle game.PlayerLeaveWorldEventHandle[PlayerID]) {
func (slf *World[PlayerID, Player]) RegPlayerLeaveWorldEvent(handle game.PlayerLeaveWorldEventHandle[PlayerID, Player]) {
slf.playerLeaveWorldEventHandles = append(slf.playerLeaveWorldEventHandles, handle)
}
func (slf *World[PlayerID]) OnPlayerLeaveWorldEvent(player game.Player[PlayerID]) {
func (slf *World[PlayerID, Player]) OnPlayerLeaveWorldEvent(player Player) {
for _, handle := range slf.playerLeaveWorldEventHandles {
handle(player)
}
}
func (slf *World[PlayerID]) RegActorGeneratedEvent(handle game.ActorGeneratedEventHandle) {
func (slf *World[PlayerID, Player]) RegActorGeneratedEvent(handle game.ActorGeneratedEventHandle) {
slf.actorGeneratedEventHandles = append(slf.actorGeneratedEventHandles, handle)
}
func (slf *World[PlayerID]) OnActorGeneratedEvent(actor game.Actor) {
func (slf *World[PlayerID, Player]) OnActorGeneratedEvent(actor game.Actor) {
for _, handle := range slf.actorGeneratedEventHandles {
handle(actor)
}
}
func (slf *World[PlayerID]) RegActorAnnihilationEvent(handle game.ActorAnnihilationEventHandle) {
func (slf *World[PlayerID, Player]) RegActorAnnihilationEvent(handle game.ActorAnnihilationEventHandle) {
slf.actorAnnihilationEventHandles = append(slf.actorAnnihilationEventHandles, handle)
}
func (slf *World[PlayerID]) OnActorAnnihilationEvent(actor game.Actor) {
func (slf *World[PlayerID, Player]) OnActorAnnihilationEvent(actor game.Actor) {
for _, handle := range slf.actorAnnihilationEventHandles {
handle(actor)
}
}
func (slf *World[PlayerID]) RegActorOwnerChangeEvent(handle game.ActorOwnerChangeEventHandle[PlayerID]) {
func (slf *World[PlayerID, Player]) RegActorOwnerChangeEvent(handle game.ActorOwnerChangeEventHandle[PlayerID]) {
slf.actorOwnerChangeEventHandles = append(slf.actorOwnerChangeEventHandles, handle)
}
func (slf *World[PlayerID]) OnActorOwnerChangeEvent(actor game.Actor, old, new PlayerID, isolated bool) {
func (slf *World[PlayerID, Player]) OnActorOwnerChangeEvent(actor game.Actor, old, new PlayerID, isolated bool) {
for _, handle := range slf.actorOwnerChangeEventHandles {
handle(actor, old, new, isolated)
}

View File

@ -1,9 +1,11 @@
package builtin
type WorldOption[PlayerID comparable] func(world *World[PlayerID])
import "minotaur/game"
func WithWorldPlayerLimit[PlayerID comparable](playerLimit int) WorldOption[PlayerID] {
return func(world *World[PlayerID]) {
type WorldOption[PlayerID comparable, Player game.Player[PlayerID]] func(world *World[PlayerID, Player])
func WithWorldPlayerLimit[PlayerID comparable, Player game.Player[PlayerID]](playerLimit int) WorldOption[PlayerID, Player] {
return func(world *World[PlayerID, Player]) {
world.playerLimit = playerLimit
}
}

View File

@ -4,6 +4,8 @@ package game
type Player[ID comparable] interface {
// GetID 用户玩家ID
GetID() ID
// GetConnID 获取连接ID
GetConnID() string
// Send 发送数据包
Send(packet []byte) error
// Close 关闭玩家并且释放其资源

View File

@ -1,15 +1,17 @@
package game
// World 游戏世界接口定义
type World[PlayerID comparable] interface {
type World[PlayerID comparable, P Player[PlayerID]] interface {
// GetGuid 获取世界的唯一标识符
GetGuid() int64
// GetPlayerLimit 获取玩家人数上限
GetPlayerLimit() int
// GetPlayerWithConnID 根据连接ID获取玩家
GetPlayerWithConnID(id string) P
// GetPlayer 根据玩家id获取玩家
GetPlayer(id PlayerID) Player[PlayerID]
GetPlayer(id PlayerID) P
// GetPlayers 获取世界中的所有玩家
GetPlayers() map[PlayerID]Player[PlayerID]
GetPlayers() map[PlayerID]P
// GetActor 根据唯一标识符获取世界中的游戏对象
GetActor(guid int64) Actor
// GetActors 获取世界中的所有游戏对象
@ -26,9 +28,9 @@ type World[PlayerID comparable] interface {
IsOwner(id PlayerID, guid int64) bool
// Join 使特定玩家加入游戏世界
Join(player Player[PlayerID]) error
Join(player P) error
// Leave 使特定玩家离开游戏世界
Leave(player Player[PlayerID])
Leave(id PlayerID)
// AddActor 添加游戏对象
AddActor(actor Actor)
@ -45,17 +47,17 @@ type World[PlayerID comparable] interface {
Release()
// RegWorldResetEvent 世界被重置后将立即执行被注册的事件处理函数
RegWorldResetEvent(handle WorldResetEventHandle[PlayerID])
RegWorldResetEvent(handle WorldResetEventHandle[PlayerID, P])
OnWorldResetEvent()
// RegWorldReleaseEvent 直接被释放前将立即执行被注册的事件处理函数,此刻世界仍然可用
RegWorldReleaseEvent(handle WorldReleaseEventHandle[PlayerID])
RegWorldReleaseEvent(handle WorldReleaseEventHandle[PlayerID, P])
OnWorldReleaseEvent()
// RegPlayerJoinWorldEvent 玩家进入世界时将立即执行被注册的事件处理函数
RegPlayerJoinWorldEvent(handle PlayerJoinWorldEventHandle[PlayerID])
OnPlayerJoinWorldEvent(player Player[PlayerID])
RegPlayerJoinWorldEvent(handle PlayerJoinWorldEventHandle[PlayerID, P])
OnPlayerJoinWorldEvent(player P)
// RegPlayerLeaveWorldEvent 玩家离开世界时将立即执行被注册的事件处理函数
RegPlayerLeaveWorldEvent(handle PlayerLeaveWorldEventHandle[PlayerID])
OnPlayerLeaveWorldEvent(player Player[PlayerID])
RegPlayerLeaveWorldEvent(handle PlayerLeaveWorldEventHandle[PlayerID, P])
OnPlayerLeaveWorldEvent(player P)
// RegActorGeneratedEvent 游戏世界中的游戏对象生成完成时将立即执行被注册的事件处理函数
RegActorGeneratedEvent(handle ActorGeneratedEventHandle)
OnActorGeneratedEvent(actor Actor)
@ -68,11 +70,11 @@ type World[PlayerID comparable] interface {
}
type (
WorldResetEventHandle[ID comparable] func(world World[ID])
WorldReleaseEventHandle[ID comparable] func(world World[ID])
PlayerJoinWorldEventHandle[ID comparable] func(player Player[ID])
PlayerLeaveWorldEventHandle[ID comparable] func(player Player[ID])
ActorGeneratedEventHandle func(actor Actor)
ActorAnnihilationEventHandle func(actor Actor)
ActorOwnerChangeEventHandle[ID comparable] func(actor Actor, old, new ID, isolated bool)
WorldResetEventHandle[ID comparable, P Player[ID]] func(world World[ID, P])
WorldReleaseEventHandle[ID comparable, P Player[ID]] func(world World[ID, P])
PlayerJoinWorldEventHandle[ID comparable, P Player[ID]] func(player P)
PlayerLeaveWorldEventHandle[ID comparable, P Player[ID]] func(player P)
ActorGeneratedEventHandle func(actor Actor)
ActorAnnihilationEventHandle func(actor Actor)
ActorOwnerChangeEventHandle[ID comparable] func(actor Actor, old, new ID, isolated bool)
)

View File

@ -32,6 +32,7 @@ func newGNetConn(conn gnet.Conn) *Conn {
// newKcpConn 创建一个处理WebSocket的连接
func newWebsocketConn(ws *websocket.Conn) *Conn {
return &Conn{
ip: ws.RemoteAddr().String(),
ws: ws,
write: func(data []byte) error {
return ws.WriteMessage(websocket.BinaryMessage, data)
@ -49,6 +50,10 @@ type Conn struct {
data map[any]any
}
func (slf *Conn) GetID() string {
return slf.ip
}
// Write 向连接中写入数据
func (slf *Conn) Write(data []byte) error {
return slf.write(data)

View File

@ -56,6 +56,7 @@ func (slf *event) RegConnectionClosedEvent(handle ConnectionClosedEventHandle) {
}
func (slf *event) OnConnectionClosedEvent(conn *Conn) {
slf.Server.connections.Delete(conn.ip)
for _, handle := range slf.connectionClosedEventHandles {
handle(conn)
}
@ -71,6 +72,7 @@ func (slf *event) RegConnectionOpenedEvent(handle ConnectionOpenedEventHandle) {
}
func (slf *event) OnConnectionOpenedEvent(conn *Conn) {
slf.Server.connections.Set(conn.ip, conn)
for _, handle := range slf.connectionOpenedEventHandles {
handle(conn)
}

View File

@ -9,6 +9,7 @@ import (
"go.uber.org/zap"
"google.golang.org/grpc"
"minotaur/utils/log"
"minotaur/utils/synchronization"
"net"
"net/http"
"os"
@ -37,8 +38,9 @@ func New(network Network, options ...Option) *Server {
// Server 网络服务器
type Server struct {
*event
network Network // 网络类型
addr string // 侦听地址
network Network // 网络类型
addr string // 侦听地址
connections *synchronization.Map[string, *Conn]
httpServer *gin.Engine // HTTP模式下的服务器
grpcServer *grpc.Server // GRPC模式下的服务器
gServer *gNet // TCP或UDP模式下的服务器
@ -68,6 +70,7 @@ func (slf *Server) Run(addr string) error {
slf.addr = addr
var protoAddr = fmt.Sprintf("%s://%s", slf.network, slf.addr)
var connectionInitHandle = func(callback func()) {
slf.connections = synchronization.NewMap[string, *Conn]()
slf.initMessageChannel = true
slf.messageChannel = make(chan *message, 4096*1000)
if slf.network != NetworkHttp && slf.network != NetworkWebsocket {
@ -176,7 +179,6 @@ func (slf *Server) Run(addr string) error {
}
conn := newWebsocketConn(ws)
conn.ip = ip
slf.OnConnectionOpenedEvent(conn)
defer func() {
@ -237,6 +239,11 @@ func (slf *Server) IsProd() bool {
// Shutdown 停止运行服务器
func (slf *Server) Shutdown(err error) {
if slf.connections != nil {
slf.connections.Range(func(connId string, conn *Conn) {
conn.Close()
})
}
if slf.initMessageChannel {
close(slf.messageChannel)
}