From 481ccc182a33ddb4f8b936b53eafbbfac87f733a Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Wed, 19 Apr 2023 17:13:34 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=9C=8D=E5=8A=A1=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- app/template/app/app.go | 7 - app/template/main.go | 19 --- app/template/main_test.go | 45 ------ game/channel.go | 183 --------------------- game/conn.go | 8 - game/conn/ordinary.go | 18 --- game/feature/actor.go | 9 -- game/feature/chatroom.go | 16 -- game/feature/components/room_manager.go | 48 ------ game/feature/player.go | 7 - game/feature/room.go | 21 --- game/feature/room_game.go | 10 -- game/feature/room_ready.go | 14 -- game/feature/room_spectator.go | 16 -- game/feature/room_team.go | 20 --- game/feature/standard/player.go | 9 -- game/feature/standard/room.go | 61 ------- game/feature/standard/room_game.go | 39 ----- game/feature/standard/room_ready.go | 48 ------ game/feature/standard/room_spectator.go | 72 --------- game/feature/standard/timer.go | 15 -- game/feature/timer.go | 9 -- game/gametime.go | 22 --- game/gateway.go | 147 ----------------- game/message.go | 34 ---- game/player.go | 192 ---------------------- game/protobuf/message.proto | 15 -- game/protobuf/messagepack/pack.go | 21 --- game/protobuf/protobuf/message.pb.go | 201 ----------------------- game/protobuf/protobuf/system.pb.go | 134 ---------------- game/protobuf/system.proto | 8 - game/pushfail.go | 8 - game/router.go | 123 -------------- game/statemachine.go | 150 ----------------- go.mod | 34 +++- go.sum | 121 ++++++++++++++ main.go | 9 ++ server/conn.go | 45 ++++++ server/errors.go | 12 ++ server/event.go | 65 ++++++++ server/gserver.go | 58 +++++++ server/message.go | 67 ++++++++ server/network.go | 15 ++ server/server.go | 203 ++++++++++++++++++++++++ utils/file/file.go | 24 +++ utils/g2d/g2d.go | 4 +- utils/gin/middlewares/cros.go | 43 ----- utils/gin/middlewares/logger.go | 28 ---- utils/log/log.go | 26 +-- utils/runtimes/path.go | 49 ++++++ utils/sole/guid.go | 37 +++++ utils/sole/sonyflake.go | 29 ++++ utils/synchronization/map.go | 162 +++++++++++++++++++ 54 files changed, 946 insertions(+), 1836 deletions(-) delete mode 100644 app/template/app/app.go delete mode 100644 app/template/main.go delete mode 100644 app/template/main_test.go delete mode 100644 game/channel.go delete mode 100644 game/conn.go delete mode 100644 game/conn/ordinary.go delete mode 100644 game/feature/actor.go delete mode 100644 game/feature/chatroom.go delete mode 100644 game/feature/components/room_manager.go delete mode 100644 game/feature/player.go delete mode 100644 game/feature/room.go delete mode 100644 game/feature/room_game.go delete mode 100644 game/feature/room_ready.go delete mode 100644 game/feature/room_spectator.go delete mode 100644 game/feature/room_team.go delete mode 100644 game/feature/standard/player.go delete mode 100644 game/feature/standard/room.go delete mode 100644 game/feature/standard/room_game.go delete mode 100644 game/feature/standard/room_ready.go delete mode 100644 game/feature/standard/room_spectator.go delete mode 100644 game/feature/standard/timer.go delete mode 100644 game/feature/timer.go delete mode 100644 game/gametime.go delete mode 100644 game/gateway.go delete mode 100644 game/message.go delete mode 100644 game/player.go delete mode 100644 game/protobuf/message.proto delete mode 100644 game/protobuf/messagepack/pack.go delete mode 100644 game/protobuf/protobuf/message.pb.go delete mode 100644 game/protobuf/protobuf/system.pb.go delete mode 100644 game/protobuf/system.proto delete mode 100644 game/pushfail.go delete mode 100644 game/router.go delete mode 100644 game/statemachine.go create mode 100644 main.go create mode 100644 server/conn.go create mode 100644 server/errors.go create mode 100644 server/event.go create mode 100644 server/gserver.go create mode 100644 server/message.go create mode 100644 server/network.go create mode 100644 server/server.go create mode 100644 utils/file/file.go delete mode 100644 utils/gin/middlewares/cros.go delete mode 100644 utils/gin/middlewares/logger.go create mode 100644 utils/runtimes/path.go create mode 100644 utils/sole/guid.go create mode 100644 utils/sole/sonyflake.go create mode 100644 utils/synchronization/map.go diff --git a/.gitignore b/.gitignore index 404c8b7..1492558 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ ### Minotaur -app/monopoly/ +old/app/monopoly/ ### VisualStudioCode template .vscode/* diff --git a/app/template/app/app.go b/app/template/app/app.go deleted file mode 100644 index 4fd17f5..0000000 --- a/app/template/app/app.go +++ /dev/null @@ -1,7 +0,0 @@ -package app - -import ( - "minotaur/game" -) - -var State = new(game.StateMachine).Init() diff --git a/app/template/main.go b/app/template/main.go deleted file mode 100644 index 6e3373c..0000000 --- a/app/template/main.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "fmt" - "minotaur/app/template/app" - "minotaur/game" - "minotaur/game/conn" - "minotaur/game/protobuf/protobuf" -) - -func init() { - app.State.RegMessagePlayer(int32(protobuf.MessageCode_SystemHeartbeat), func(player *game.Player) { - fmt.Println("hhha") - }) -} - -func main() { - app.State.Run("/test", 8888, conn.NewOrdinary()) -} diff --git a/app/template/main_test.go b/app/template/main_test.go deleted file mode 100644 index 93e2123..0000000 --- a/app/template/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import ( - "fmt" - "github.com/gorilla/websocket" - "google.golang.org/protobuf/proto" - "minotaur/game/protobuf/protobuf" - "testing" - "time" -) - -func TestA(t *testing.T) { -run: - { - } - for { - ws := `ws://127.0.0.1:9000/test` - c, _, err := websocket.DefaultDialer.Dial(ws, nil) - if err != nil { - continue - } - - req := &protobuf.SystemHeartbeatClient{} - d, _ := proto.Marshal(req) - - data, err := proto.Marshal(&protobuf.Message{ - Code: int32(protobuf.MessageCode_SystemHeartbeat), - Data: d, - }) - if err != nil { - panic(err) - } - - for { - //fmt.Println(c, data) - if err := c.WriteMessage(2, data); err != nil { - fmt.Println(err) - goto run - } - - time.Sleep(3 * time.Second) - } - - } -} diff --git a/game/channel.go b/game/channel.go deleted file mode 100644 index 4d53065..0000000 --- a/game/channel.go +++ /dev/null @@ -1,183 +0,0 @@ -package game - -import ( - "fmt" - "go.uber.org/zap" - "google.golang.org/protobuf/proto" - "minotaur/utils/log" - "minotaur/utils/super" - "minotaur/utils/timer" - "reflect" - "strconv" - "sync" - "time" -) - -// channel 频道 -type channel struct { - id any // 频道ID - size int // 最大人数 - stateMachine *StateMachine // 状态机 - timer *timer.Manager // 定时器 - lock sync.RWMutex // 玩家锁 - players map[int64]*Player // 所有玩家 - alone bool // 频道运行逻辑是否单线程 - - // alone mode - messages chan *Message - - // multi mode -} - -// run 开始运行频道 -func (slf *channel) run() { - slf.messages = make(chan *Message, 4096*1000) - go func() { - for { - msg, open := <-slf.messages - if !open { - return - } - slf.dispatch(msg) - } - }() -} - -// tryRelease 尝试释放频道 -func (slf *channel) tryRelease() { - slf.timer.After("channelRelease", time.Second*10, func() { - slf.stateMachine.channelRWMutex.Lock() - defer slf.stateMachine.channelRWMutex.Unlock() - if len(slf.players) > 0 { - return - } - slf.timer.Release() - close(slf.messages) - delete(slf.stateMachine.channels, slf.id) - log.Info("ChannelRelease", zap.Any("channelID", slf.id), zap.Int("channelCount", len(slf.stateMachine.channels))) - }) -} - -// tryRelease 强制释放频道 -func (slf *channel) release() { - slf.stateMachine.channelRWMutex.Lock() - defer slf.stateMachine.channelRWMutex.Unlock() - slf.timer.Release() - close(slf.messages) - delete(slf.stateMachine.channels, slf.id) - log.Info("ChannelRelease", - zap.Any("channelID", slf.id), - zap.Int("channelCount", len(slf.stateMachine.channels)), - zap.String("type", "force"), - ) -} - -// join 加入频道 -func (slf *channel) join(player *Player) (*Player, error) { - slf.lock.Lock() - if slf.size > 0 && len(slf.players) > slf.size { - return player, fmt.Errorf("join channel[%v] failed, maximum[%d] number of player", slf.id, slf.size) - } - if player.channel != nil { - player.channel.lock.Lock() - delete(player.channel.players, player.guid) - player.channel.lock.Unlock() - player.channel.tryRelease() - } - slf.players[player.guid] = player - slf.lock.Unlock() - player.channel = slf - player.ChannelTimer = slf.timer - log.Info("ChannelJoin", - zap.Any("channelID", slf.id), - zap.Int64("playerGuid", player.guid), - zap.String("size", super.If(slf.size <= 0, "NaN", strconv.Itoa(slf.size))), - zap.Int("headcount", len(slf.players)), - zap.String("address", player.ip), - ) - return player, nil -} - -// push 向频道内推送消息 -func (slf *channel) push(message *Message) { - switch message.Type() { - case MessageTypePlayer: - if slf.alone { - slf.messages <- message - } else { - slf.dispatch(message) - } - case MessageTypeEvent: - slf.messages <- message - } - -} - -// dispatch 消息分发 -func (slf *channel) dispatch(message *Message) { - args := message.Args() - switch message.Type() { - case MessageTypePlayer: - player, code, data := args[0], args[1], args[2] - h := slf.stateMachine.handles[code.(int32)] - var in = []reflect.Value{reflect.ValueOf(player)} - if h[1] != nil { - messageType := reflect.New(h[1].(reflect.Type).Elem()) - if err := proto.Unmarshal(data.([]byte), messageType.Interface().(proto.Message)); err != nil { - panic(err) - } - in = append(in, messageType) - } - h[0].(reflect.Value).Call(in) - case MessageTypeEvent: - event, player := args[0].(byte), args[1].(*Player) - switch event { - case EventTypeGuestJoin: - slf.stateMachine.router.OnGuestPlayerJoinEvent(player) - case EventTypeGuestLeave: - callback := args[2].(func(player *Player)) - slf.stateMachine.router.OnGuestPlayerLeaveEvent(player) - callback(player) - } - } -} - -// GetPlayers 获取频道内所有玩家 -func (slf *channel) GetPlayers() map[int64]*Player { - var players = make(map[int64]*Player) - if !slf.alone { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - for id, player := range slf.players { - players[id] = player - } - return players -} - -// GetAllChannelPlayers 获取所有频道玩家 -func (slf *channel) GetAllChannelPlayers() map[int64]*Player { - if !slf.alone { - slf.stateMachine.channelRWMutex.RLock() - } - var channels = make([]*channel, 0, len(slf.stateMachine.channels)) - for _, channel := range slf.stateMachine.channels { - channels = append(channels, channel) - } - if !slf.alone { - slf.stateMachine.channelRWMutex.RUnlock() - } - var players = make(map[int64]*Player) - for _, channel := range channels { - if !slf.alone { - channel.lock.RLock() - } - for id, player := range channel.players { - players[id] = player - } - if !slf.alone { - channel.lock.RUnlock() - } - } - return players -} diff --git a/game/conn.go b/game/conn.go deleted file mode 100644 index bc9ba49..0000000 --- a/game/conn.go +++ /dev/null @@ -1,8 +0,0 @@ -package game - -// Conn 用户连接抽象 -// -// 连接支持使用 tag 进行参数读取 -type Conn interface { - GetConn() any -} diff --git a/game/conn/ordinary.go b/game/conn/ordinary.go deleted file mode 100644 index b7915a8..0000000 --- a/game/conn/ordinary.go +++ /dev/null @@ -1,18 +0,0 @@ -package conn - -import ( - "minotaur/game" -) - -func NewOrdinary() game.OnCreateConnHandleFunc { - return func() game.Conn { - return new(Ordinary) - } -} - -type Ordinary struct { -} - -func (slf *Ordinary) GetConn() any { - return slf -} diff --git a/game/feature/actor.go b/game/feature/actor.go deleted file mode 100644 index 12ea346..0000000 --- a/game/feature/actor.go +++ /dev/null @@ -1,9 +0,0 @@ -package feature - -// Actor 玩家演员接口定义 -type Actor interface { - // GetGuid 获取演员 guid - GetGuid() int64 - // GetPlayer 获取所属玩家 - GetPlayer() Player -} diff --git a/game/feature/chatroom.go b/game/feature/chatroom.go deleted file mode 100644 index 2b6bd08..0000000 --- a/game/feature/chatroom.go +++ /dev/null @@ -1,16 +0,0 @@ -package feature - -// ChatRoom 聊天室接口定义 -type ChatRoom interface { - // JoinChatRoom 加入聊天室 - JoinChatRoom(chat PlayerChat) - // LeaveChatRoom 离开聊天室 - LeaveChatRoom(chat PlayerChat) -} - -// PlayerChat 玩家聊天接口定义 -type PlayerChat interface { - Player - // SendChatMessage 发送聊天消息给该玩家 - SendChatMessage(message string) -} diff --git a/game/feature/components/room_manager.go b/game/feature/components/room_manager.go deleted file mode 100644 index 9572304..0000000 --- a/game/feature/components/room_manager.go +++ /dev/null @@ -1,48 +0,0 @@ -package components - -import ( - "minotaur/game/feature" - "reflect" -) - -// NewRoomManager 创建房间管理组件 -func NewRoomManager[P feature.Player, R feature.Room[P]]() *RoomManager[P, R] { - return &RoomManager[P, R]{} -} - -// RoomManager 房间管理组件 -type RoomManager[P feature.Player, R feature.Room[P]] struct { - rooms map[int64]R - playerRoomRef map[int64]int64 -} - -func (slf *RoomManager[P, R]) JoinRoom(player feature.Player, room R) { - if slf.rooms == nil { - slf.rooms = map[int64]R{} - slf.playerRoomRef = map[int64]int64{} - } - slf.rooms[room.GetGuid()] = room - slf.playerRoomRef[player.GetGuid()] = room.GetGuid() -} - -func (slf *RoomManager[P, R]) LeaveRoom(playerGuid int64) { - roomId := slf.playerRoomRef[playerGuid] - room := slf.rooms[roomId] - if !reflect.ValueOf(room).IsNil() { - room.LeaveRoom(playerGuid) - } - delete(slf.rooms, roomId) - delete(slf.playerRoomRef, playerGuid) -} - -func (slf *RoomManager[P, R]) GetRoom(guid int64) R { - return slf.rooms[guid] -} - -func (slf *RoomManager[P, R]) GetPlayer(guid int64) P { - return slf.rooms[slf.playerRoomRef[guid]].GetPlayer(guid) -} - -func (slf *RoomManager[P, R]) GetPlayerRoom(guid int64) R { - return slf.rooms[slf.playerRoomRef[guid]] -} diff --git a/game/feature/player.go b/game/feature/player.go deleted file mode 100644 index 9b87215..0000000 --- a/game/feature/player.go +++ /dev/null @@ -1,7 +0,0 @@ -package feature - -// Player 玩家接口定义 -type Player interface { - // GetGuid 获取玩家 guid - GetGuid() int64 -} diff --git a/game/feature/room.go b/game/feature/room.go deleted file mode 100644 index bbeb59e..0000000 --- a/game/feature/room.go +++ /dev/null @@ -1,21 +0,0 @@ -package feature - -// Room 游戏房间接口定义 -type Room[P Player] interface { - // GetGuid 获取房间 guid - GetGuid() int64 - // JoinRoom 加入房间 - JoinRoom(player P) error - // LeaveRoom 离开房间 - LeaveRoom(guid int64) - // GetPlayerMaximum 获取游戏参与人上限 - GetPlayerMaximum() int - // GetPlayer 获取特定玩家 - GetPlayer(guid int64) P - // GetPlayers 获取所有玩家 - GetPlayers() map[int64]P - // GetPlayerCount 获取玩家数量 - GetPlayerCount() int - // IsExist 玩家是否存在 - IsExist(playerGuid int64) bool -} diff --git a/game/feature/room_game.go b/game/feature/room_game.go deleted file mode 100644 index 88c1fba..0000000 --- a/game/feature/room_game.go +++ /dev/null @@ -1,10 +0,0 @@ -package feature - -// RoomGame 房间游戏接口定义 -type RoomGame[P Player] interface { - Room[P] - // GameStart 游戏开始 - GameStart(startTimeSecond int64, loop func(game RoomGame[P])) - // GameOver 游戏结束 - GameOver() -} diff --git a/game/feature/room_ready.go b/game/feature/room_ready.go deleted file mode 100644 index 326138d..0000000 --- a/game/feature/room_ready.go +++ /dev/null @@ -1,14 +0,0 @@ -package feature - -// RoomReady 房间准备接口定义 -type RoomReady[P Player] interface { - Room[P] - // Ready 设置玩家准备状态 - Ready(playerGuid int64, ready bool) - // IsAllReady 是否全部玩家已准备 - IsAllReady() bool - // GetReadyCount 获取已准备玩家数量 - GetReadyCount() int - // GetUnready 获取未准备的玩家 - GetUnready() map[int64]P -} diff --git a/game/feature/room_spectator.go b/game/feature/room_spectator.go deleted file mode 100644 index 6bf3cb1..0000000 --- a/game/feature/room_spectator.go +++ /dev/null @@ -1,16 +0,0 @@ -package feature - -// RoomSpectator 房间观众席接口定义 -type RoomSpectator[P Player] interface { - Room[P] - // GetSpectatorMaximum 获取观众人数上限 - GetSpectatorMaximum() int - // JoinSpectator 加入观众席 - JoinSpectator(player P) error - // LeaveSpectator 离开观众席 - LeaveSpectator(guid int64) error - // GetSpectatorPlayer 获取特定观众席玩家 - GetSpectatorPlayer(guid int64) P - // GetSpectatorPlayers 获取观众席玩家 - GetSpectatorPlayers() map[int64]P -} diff --git a/game/feature/room_team.go b/game/feature/room_team.go deleted file mode 100644 index 5155970..0000000 --- a/game/feature/room_team.go +++ /dev/null @@ -1,20 +0,0 @@ -package feature - -// RoomTeam 房间小队接口定义 -type RoomTeam interface { - // GetTeamMaximum 获取最大小队数量 - GetTeamMaximum() int - // GetTeam 获取特定队伍 - GetTeam(guid int64) Team - // GetTeams 获取所有队伍 - GetTeams() map[int64]Team -} - -type Team interface { - // GetGuid 获取小队 guid - GetGuid() int64 - // GetPlayer 获取小队特定玩家 - GetPlayer(guid int64) Player - // GetPlayers 获取小队玩家 - GetPlayers() map[int64]Player -} diff --git a/game/feature/standard/player.go b/game/feature/standard/player.go deleted file mode 100644 index 4045ec5..0000000 --- a/game/feature/standard/player.go +++ /dev/null @@ -1,9 +0,0 @@ -package standard - -import ( - "minotaur/game" -) - -type Player struct { - *game.Player -} diff --git a/game/feature/standard/room.go b/game/feature/standard/room.go deleted file mode 100644 index 3b5c15f..0000000 --- a/game/feature/standard/room.go +++ /dev/null @@ -1,61 +0,0 @@ -package standard - -import ( - "errors" - "minotaur/game/feature" - "reflect" -) - -// NewRoom 普通房间实例 -func NewRoom[P feature.Player](guid int64, playerMaximum int) *Room[P] { - room := &Room[P]{ - guid: guid, - playerMaximum: playerMaximum, - } - return room -} - -type Room[P feature.Player] struct { - guid int64 // 房间 guid - playerMaximum int // 房间最大人数,小于等于0不限 - players map[int64]P // 房间玩家 -} - -func (slf *Room[P]) GetGuid() int64 { - return slf.guid -} - -func (slf *Room[P]) JoinRoom(player P) error { - if slf.playerMaximum > 0 && len(slf.players) >= slf.playerMaximum { - return errors.New("maximum number of player") - } - if slf.players == nil { - slf.players = map[int64]P{} - } - slf.players[player.GetGuid()] = player - return nil -} - -func (slf *Room[P]) LeaveRoom(guid int64) { - delete(slf.players, guid) -} - -func (slf *Room[P]) GetPlayerMaximum() int { - return slf.playerMaximum -} - -func (slf *Room[P]) GetPlayer(guid int64) P { - return slf.players[guid] -} - -func (slf *Room[P]) GetPlayers() map[int64]P { - return slf.players -} - -func (slf *Room[P]) GetPlayerCount() int { - return len(slf.players) -} - -func (slf *Room[P]) IsExist(playerGuid int64) bool { - return !reflect.ValueOf(slf.players[playerGuid]).IsNil() -} diff --git a/game/feature/standard/room_game.go b/game/feature/standard/room_game.go deleted file mode 100644 index db5e5c0..0000000 --- a/game/feature/standard/room_game.go +++ /dev/null @@ -1,39 +0,0 @@ -package standard - -import ( - "minotaur/game/feature" - "time" -) - -// NewRoomGame 对普通房间附加游戏功能的实例 -// -// startCountDown 游戏开始倒计时,大于0生效 -func NewRoomGame[P feature.Player](room *Room[P], startCountDown time.Duration) *RoomGame[P] { - return &RoomGame[P]{ - Room: room, - startCountDown: startCountDown, - } -} - -type RoomGame[P feature.Player] struct { - *Room[P] - *Timer - startTimeSecond int64 // 游戏开始时间戳(秒) - startCountDown time.Duration // 游戏开始倒计时 -} - -func (slf *RoomGame[P]) GameStart(startTimeSecond int64, loop func(game feature.RoomGame[P])) { - slf.startTimeSecond = startTimeSecond - - if slf.startCountDown > 0 { - slf.After("RoomGame.GameStart.StartCountDown", slf.startCountDown, func() { - loop(slf) - }) - } else { - loop(slf) - } -} - -func (slf *RoomGame[P]) GameOver() { - slf.Release() -} diff --git a/game/feature/standard/room_ready.go b/game/feature/standard/room_ready.go deleted file mode 100644 index 1d2612e..0000000 --- a/game/feature/standard/room_ready.go +++ /dev/null @@ -1,48 +0,0 @@ -package standard - -import "minotaur/game/feature" - -// NewRoomReady 对普通房间附加准备功能的实例 -func NewRoomReady[P feature.Player](room *Room[P]) *RoomReady[P] { - return &RoomReady[P]{} -} - -type RoomReady[P feature.Player] struct { - *Room[P] - readies map[int64]bool -} - -func (slf *RoomReady[P]) Ready(playerGuid int64, ready bool) { - if ready { - if !slf.IsExist(playerGuid) { - if _, exist := slf.readies[playerGuid]; exist { - delete(slf.readies, playerGuid) - } - return - } - if slf.readies == nil { - slf.readies = map[int64]bool{} - } - slf.readies[playerGuid] = true - } else { - delete(slf.readies, playerGuid) - } -} - -func (slf *RoomReady[P]) IsAllReady() bool { - return len(slf.readies) >= slf.GetPlayerCount() -} - -func (slf *RoomReady[P]) GetReadyCount() int { - return len(slf.readies) -} - -func (slf *RoomReady[P]) GetUnready() map[int64]P { - var unreadiest = make(map[int64]P) - for guid, player := range slf.GetPlayers() { - if !slf.readies[guid] { - unreadiest[guid] = player - } - } - return unreadiest -} diff --git a/game/feature/standard/room_spectator.go b/game/feature/standard/room_spectator.go deleted file mode 100644 index 5bbff28..0000000 --- a/game/feature/standard/room_spectator.go +++ /dev/null @@ -1,72 +0,0 @@ -package standard - -import ( - "errors" - "minotaur/game/feature" - "reflect" -) - -// NewRoomSpectator 对普通房间附加观众席功能的实例 -func NewRoomSpectator[P feature.Player](room *Room[P], spectatorMaximum int) *RoomSpectator[P] { - return &RoomSpectator[P]{ - Room: room, - spectatorMaximum: spectatorMaximum, - } -} - -type RoomSpectator[P feature.Player] struct { - *Room[P] - spectatorMaximum int // 观众席最大人数,小于等于0不限 - spectatorPlayers map[int64]P // 观众席玩家 - recordInRoom map[int64]bool // 记录玩家是否从房间中移动到观众席 -} - -func (slf *RoomSpectator[P]) GetSpectatorMaximum() int { - return slf.spectatorMaximum -} - -func (slf *RoomSpectator[P]) JoinSpectator(player P) error { - if slf.spectatorMaximum > 0 && len(slf.spectatorPlayers) >= slf.spectatorMaximum { - return errors.New("maximum number of player") - } - - var guid = player.GetGuid() - - if !reflect.ValueOf(slf.GetPlayer(guid)).IsNil() { - if slf.recordInRoom == nil { - slf.recordInRoom = map[int64]bool{} - } - slf.recordInRoom[guid] = true - slf.LeaveRoom(player.GetGuid()) - } - if slf.spectatorPlayers == nil { - slf.spectatorPlayers = map[int64]P{} - } - slf.spectatorPlayers[guid] = player - return nil -} - -func (slf *RoomSpectator[P]) LeaveSpectator(guid int64) error { - var ( - inRoom = slf.recordInRoom[guid] - inSpectator = !reflect.ValueOf(slf.spectatorPlayers[guid]).IsNil() - ) - - if inSpectator { - delete(slf.spectatorPlayers, guid) - delete(slf.recordInRoom, guid) - if inRoom { - return slf.JoinRoom(slf.GetPlayer(guid)) - } - } - - return nil -} - -func (slf *RoomSpectator[P]) GetSpectatorPlayer(guid int64) P { - return slf.spectatorPlayers[guid] -} - -func (slf *RoomSpectator[P]) GetSpectatorPlayers() map[int64]P { - return slf.spectatorPlayers -} diff --git a/game/feature/standard/timer.go b/game/feature/standard/timer.go deleted file mode 100644 index 5c37651..0000000 --- a/game/feature/standard/timer.go +++ /dev/null @@ -1,15 +0,0 @@ -package standard - -import "minotaur/utils/timer" - -// Timer 附加定时器功能 -type Timer struct { - *timer.Manager -} - -func (slf *Timer) Timer() *timer.Manager { - if slf.Manager == nil { - slf.Manager = timer.GetManager(64) - } - return slf.Manager -} diff --git a/game/feature/timer.go b/game/feature/timer.go deleted file mode 100644 index be7d79c..0000000 --- a/game/feature/timer.go +++ /dev/null @@ -1,9 +0,0 @@ -package feature - -import "minotaur/utils/timer" - -// Timer 定时器接口定义 -type Timer interface { - // Timer 获取定时器 - Timer() *timer.Manager -} diff --git a/game/gametime.go b/game/gametime.go deleted file mode 100644 index 305c967..0000000 --- a/game/gametime.go +++ /dev/null @@ -1,22 +0,0 @@ -package game - -import "time" - -// Time 带有偏移的游戏时间 -type Time struct { - Offset time.Duration -} - -// Now 获取包含偏移的当前时间 -func (slf *Time) Now() time.Time { - t := time.Now() - if slf.Offset == 0 { - return t - } - return t.Add(slf.Offset) -} - -// Since 获取两个时间之间的差 -func (slf *Time) Since(t time.Time) time.Duration { - return slf.Now().Sub(t) -} diff --git a/game/gateway.go b/game/gateway.go deleted file mode 100644 index d6bb456..0000000 --- a/game/gateway.go +++ /dev/null @@ -1,147 +0,0 @@ -package game - -import ( - "context" - "fmt" - "github.com/gin-contrib/pprof" - "github.com/gin-gonic/gin" - "github.com/gorilla/websocket" - "go.uber.org/zap" - "google.golang.org/protobuf/proto" - "minotaur/game/protobuf/protobuf" - "minotaur/utils/gin/middlewares" - "minotaur/utils/log" - "minotaur/utils/timer" - "net/http" - "strings" - "time" -) - -const ( - loginTimeoutTimerName = "player_login_timeout_timer" -) - -type OnCreateConnHandleFunc func() Conn -type gateway struct { - httpServer *http.Server -} - -func (slf *gateway) run(stateMachine *StateMachine, appName string, port int, onCreateConnHandleFunc OnCreateConnHandleFunc) *gateway { - - // Gin WebSocket - gin.SetMode(gin.ReleaseMode) - router := gin.New() - router.Use(middlewares.Logger(log.Logger), middlewares.Cors()) - pprof.Register(router) // pprof 可视化分析 - - var upgrade = websocket.Upgrader{ - ReadBufferSize: 4096, - WriteBufferSize: 4096, - CheckOrigin: func(r *http.Request) bool { - return true - }, - } - - router.GET(fmt.Sprintf("/%s", appName), func(context *gin.Context) { - ip := context.GetHeader("X-Real-IP") - ws, err := upgrade.Upgrade(context.Writer, context.Request, nil) - if err != nil { - log.Error("Gateway", zap.Error(err)) - return - } - if len(ip) == 0 { - addr := ws.RemoteAddr().String() - if index := strings.LastIndex(addr, ":"); index != -1 { - ip = addr[0:index] - } - } - conn := onCreateConnHandleFunc() - if err := context.ShouldBind(conn); err != nil { - log.Error("Gateway", zap.Error(err)) - context.AbortWithStatus(http.StatusBadRequest) - return - } - - playerGuid, err := stateMachine.sonyflake.NextID() - if err != nil { - log.Error("Gateway", zap.Error(err)) - context.AbortWithStatus(http.StatusBadRequest) - return - } - - player := &Player{ - guid: int64(playerGuid), - conn: conn, - ws: ws, - ip: ip, - Manager: timer.GetManager(64), - GameTimer: stateMachine.Manager, - } - - channelId, size := stateMachine.channelStrategy() - channel := stateMachine.channel(channelId, size) - player, err = channel.join(player) - if err != nil { - player.exit(err.Error()) - return - } - - player.channel.push(new(Message).Init(MessageTypeEvent, EventTypeGuestJoin, player)) - if stateMachine.loginTimeout > 0 { - player.After(loginTimeoutTimerName, stateMachine.loginTimeout, func() { - player.exit("login timeout") - }) - } - - defer func() { - if err := recover(); err != nil { - player.exit() - } - }() - - for { - if err := player.ws.SetReadDeadline(time.Now().Add(time.Second * 30)); err != nil { - panic(err) - } - - _, data, err := player.ws.ReadMessage() - if err != nil { - panic(err) - } - - var msg = new(protobuf.Message) - if err = proto.Unmarshal(data, msg); err != nil { - continue - } - - player.channel.push(new(Message).Init(MessageTypePlayer, player, msg.Code, msg.Data)) - - } - - }) - - // HttpServer - slf.httpServer = &http.Server{ - Addr: fmt.Sprintf(":%d", port), - Handler: router, - } - log.Info("GatewayStart", zap.String("listen", slf.httpServer.Addr)) - - go func() { - if err := slf.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { - stateMachine.errChannel <- err - return - } - }() - - return slf -} - -func (slf *gateway) shutdown(context context.Context) error { - log.Info("GatewayShutdown", zap.String("stateMachine", "start")) - if err := slf.httpServer.Shutdown(context); err != nil { - return err - } - log.Info("GatewayShutdown", zap.String("stateMachine", "normal")) - return nil -} diff --git a/game/message.go b/game/message.go deleted file mode 100644 index c04fde5..0000000 --- a/game/message.go +++ /dev/null @@ -1,34 +0,0 @@ -package game - -const ( - // MessageTypePlayer 玩家通过客户端发送到服务端的消息 - MessageTypePlayer byte = iota - // MessageTypeEvent 服务端内部事件消息 - MessageTypeEvent -) - -// EventTypeGuestJoin 服务器内部事件消息类型 -const ( - EventTypeGuestJoin byte = iota // 访客加入事件 - EventTypeGuestLeave // 访客离开事件 -) - -// Message 游戏消息数据结构 -type Message struct { - t byte - args []any -} - -func (slf *Message) Init(t byte, args ...any) *Message { - slf.t = t - slf.args = args - return slf -} - -func (slf *Message) Type() byte { - return slf.t -} - -func (slf *Message) Args() []any { - return slf.args -} diff --git a/game/player.go b/game/player.go deleted file mode 100644 index 40394ed..0000000 --- a/game/player.go +++ /dev/null @@ -1,192 +0,0 @@ -package game - -import ( - "fmt" - "github.com/gorilla/websocket" - "go.uber.org/zap" - "google.golang.org/protobuf/proto" - "minotaur/game/protobuf/messagepack" - "minotaur/game/protobuf/protobuf" - "minotaur/utils/log" - "minotaur/utils/timer" -) - -// Player 玩家数据结构 -type Player struct { - *channel // 玩家所在频道信息 - - guid int64 // 玩家guid - ip string // 玩家IP - conn Conn // 玩家连接信息 - ws *websocket.Conn // 玩家 WebSocket 连接 - login byte // 登录状态 0: 无需登录 1: 登录成功 - - // 游戏定时器 - // - // GameTimer: 状态机级别定时器; - // ChannelTimer: 频道级别定时器; - // Manager: 玩家级别定时器。 - *timer.Manager - GameTimer, ChannelTimer *timer.Manager -} - -// Login 设置玩家是否登录成功 -func (slf *Player) Login(success bool) { - slf.stateMachine.router.OnPlayerLoginEvent(slf, success) - if success { - slf.StopTimer(loginTimeoutTimerName) - slf.login = 1 - } else { - slf.exit("login failed") - } -} - -// IsLogin 获取玩家是否已登录 -func (slf *Player) IsLogin() bool { - return slf.login == 1 -} - -// GetGuid 获取玩家GUID -func (slf *Player) GetGuid() int64 { - return slf.guid -} - -// GetChannelId 返回玩家所在频道id -func (slf *Player) GetChannelId() any { - return slf.channel.id -} - -// GetConn 获取连接信息 -func (slf *Player) GetConn() any { - return slf.conn.GetConn() -} - -// Close 关闭玩家连接 -func (slf *Player) Close() { - _ = slf.ws.Close() -} - -// PushToClient 推送消息到客户端 -func (slf *Player) PushToClient(messageCode int32, message proto.Message) { - data, err := messagepack.Pack(messageCode, message) - if err != nil { - panic(err) - } - - if err = slf.ws.WriteMessage(2, data); err != nil { - panic(err) - } -} - -// PushToClients 推送消息到多个玩家客户端 -func (slf *Player) PushToClients(messageCode int32, message proto.Message, playerGuids ...int64) []PushFail { - data, err := messagepack.Pack(messageCode, message) - if err != nil { - panic(err) - } - - if !slf.channel.alone { - slf.channel.lock.RLock() - defer slf.channel.lock.RUnlock() - } - - var pushFails []PushFail - for _, id := range playerGuids { - player := slf.channel.players[id] - if player == nil { - pushFails = append(pushFails, PushFail{ - Data: data, - Err: fmt.Errorf("player [%v] not found", id), - }) - continue - } - - if err = player.ws.WriteMessage(2, data); err != nil { - pushFails = append(pushFails, PushFail{ - Player: player, - Data: data, - Err: err, - }) - } - } - return pushFails -} - -// PushToChannelPlayerClients 推送消息到频道内所有玩家客户端,可通过指定玩家或玩家id排除特定玩家 -func (slf *Player) PushToChannelPlayerClients(messageCode int32, message proto.Message, excludeGuids ...int64) []PushFail { - data, err := messagepack.Pack(messageCode, message) - if err != nil { - panic(err) - } - - var pushFails []PushFail - if !slf.channel.alone { - slf.channel.lock.RLock() - defer slf.channel.lock.RUnlock() - } - for id, player := range slf.channel.players { - var exclude bool - for _, target := range excludeGuids { - if id == target { - exclude = true - break - } - } - if exclude { - break - } - - if err = player.ws.WriteMessage(2, data); err != nil { - pushFails = append(pushFails, PushFail{ - Player: player, - Data: data, - Err: err, - }) - } - } - return pushFails -} - -// PushToChannel 推送消息到频道 -func (slf *Player) PushToChannel(messageCode protobuf.MessageCode, message proto.Message) { - slf.channel.push(new(Message).Init(MessageTypePlayer, slf, messageCode, message)) -} - -// ChangeChannel 改变玩家所在频道 -func (slf *Player) ChangeChannel(channelId any) error { - channel := slf.channel.stateMachine.channel(channelId, slf.channel.size, slf.channel.alone) - _, err := channel.join(slf) - return err -} - -// exit 退出游戏 -func (slf *Player) exit(reason ...string) { - var r = "normal exit" - if len(reason) > 0 { - r = reason[0] - } - - slf.channel.push(new(Message).Init(MessageTypeEvent, EventTypeGuestLeave, slf, func(player *Player) { - slf.Release() - _ = slf.ws.Close() - if slf.channel != nil { - slf.channel.lock.Lock() - defer slf.channel.lock.Unlock() - delete(slf.channel.players, slf.guid) - log.Info("ChannelLeave", - zap.Any("channelID", slf.channel.id), - zap.Int64("playerGuid", slf.guid), - zap.Int("headcount", len(slf.channel.players)), - zap.String("address", slf.ip), - zap.String("reason", r), - ) - slf.channel.tryRelease() - } else { - log.Info("ChannelLeave", - zap.Int64("playerGuid", slf.guid), - zap.String("address", slf.ip), - zap.String("reason", r), - ) - } - })) -} diff --git a/game/protobuf/message.proto b/game/protobuf/message.proto deleted file mode 100644 index 5e1cfb4..0000000 --- a/game/protobuf/message.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; -// protoc --proto_path . --go_out=./game/protobuf/protobuf/ --go-grpc_out=./game/protobuf/protobuf/ ./game/protobuf/*.proto - -package protobuf; -option go_package = "./;protobuf"; - -enum MessageCode { - SystemHeartbeat = 0; // 心跳 -} - -//通用消息 -message Message { - int32 code = 1; - bytes data = 2; -} \ No newline at end of file diff --git a/game/protobuf/messagepack/pack.go b/game/protobuf/messagepack/pack.go deleted file mode 100644 index 2323fdb..0000000 --- a/game/protobuf/messagepack/pack.go +++ /dev/null @@ -1,21 +0,0 @@ -package messagepack - -import ( - "google.golang.org/protobuf/proto" - "minotaur/game/protobuf/protobuf" -) - -// Pack 消息打包 -func Pack(messageCode int32, message proto.Message) ([]byte, error) { - data, err := proto.Marshal(message) - if err != nil { - return nil, err - } - - msg := &protobuf.Message{ - Code: messageCode, - Data: data, - } - - return proto.Marshal(msg) -} diff --git a/game/protobuf/protobuf/message.pb.go b/game/protobuf/protobuf/message.pb.go deleted file mode 100644 index 2a56162..0000000 --- a/game/protobuf/protobuf/message.pb.go +++ /dev/null @@ -1,201 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v4.22.1 -// source: game/protobuf/message.proto - -package protobuf - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type MessageCode int32 - -const ( - MessageCode_SystemHeartbeat MessageCode = 0 // 心跳 -) - -// Enum value maps for MessageCode. -var ( - MessageCode_name = map[int32]string{ - 0: "SystemHeartbeat", - } - MessageCode_value = map[string]int32{ - "SystemHeartbeat": 0, - } -) - -func (x MessageCode) Enum() *MessageCode { - p := new(MessageCode) - *p = x - return p -} - -func (x MessageCode) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (MessageCode) Descriptor() protoreflect.EnumDescriptor { - return file_game_protobuf_message_proto_enumTypes[0].Descriptor() -} - -func (MessageCode) Type() protoreflect.EnumType { - return &file_game_protobuf_message_proto_enumTypes[0] -} - -func (x MessageCode) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use MessageCode.Descriptor instead. -func (MessageCode) EnumDescriptor() ([]byte, []int) { - return file_game_protobuf_message_proto_rawDescGZIP(), []int{0} -} - -// 通用消息 -type Message struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` -} - -func (x *Message) Reset() { - *x = Message{} - if protoimpl.UnsafeEnabled { - mi := &file_game_protobuf_message_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Message) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Message) ProtoMessage() {} - -func (x *Message) ProtoReflect() protoreflect.Message { - mi := &file_game_protobuf_message_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Message.ProtoReflect.Descriptor instead. -func (*Message) Descriptor() ([]byte, []int) { - return file_game_protobuf_message_proto_rawDescGZIP(), []int{0} -} - -func (x *Message) GetCode() int32 { - if x != nil { - return x.Code - } - return 0 -} - -func (x *Message) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -var File_game_protobuf_message_proto protoreflect.FileDescriptor - -var file_game_protobuf_message_proto_rawDesc = []byte{ - 0x0a, 0x1b, 0x67, 0x61, 0x6d, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0x31, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x2a, 0x22, 0x0a, 0x0b, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x10, 0x00, 0x42, 0x0d, - 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_game_protobuf_message_proto_rawDescOnce sync.Once - file_game_protobuf_message_proto_rawDescData = file_game_protobuf_message_proto_rawDesc -) - -func file_game_protobuf_message_proto_rawDescGZIP() []byte { - file_game_protobuf_message_proto_rawDescOnce.Do(func() { - file_game_protobuf_message_proto_rawDescData = protoimpl.X.CompressGZIP(file_game_protobuf_message_proto_rawDescData) - }) - return file_game_protobuf_message_proto_rawDescData -} - -var file_game_protobuf_message_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_game_protobuf_message_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_game_protobuf_message_proto_goTypes = []interface{}{ - (MessageCode)(0), // 0: protobuf.MessageCode - (*Message)(nil), // 1: protobuf.Message -} -var file_game_protobuf_message_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_game_protobuf_message_proto_init() } -func file_game_protobuf_message_proto_init() { - if File_game_protobuf_message_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_game_protobuf_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Message); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_game_protobuf_message_proto_rawDesc, - NumEnums: 1, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_game_protobuf_message_proto_goTypes, - DependencyIndexes: file_game_protobuf_message_proto_depIdxs, - EnumInfos: file_game_protobuf_message_proto_enumTypes, - MessageInfos: file_game_protobuf_message_proto_msgTypes, - }.Build() - File_game_protobuf_message_proto = out.File - file_game_protobuf_message_proto_rawDesc = nil - file_game_protobuf_message_proto_goTypes = nil - file_game_protobuf_message_proto_depIdxs = nil -} diff --git a/game/protobuf/protobuf/system.pb.go b/game/protobuf/protobuf/system.pb.go deleted file mode 100644 index 798bf70..0000000 --- a/game/protobuf/protobuf/system.pb.go +++ /dev/null @@ -1,134 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v4.22.1 -// source: game/protobuf/system.proto - -package protobuf - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// 心跳 -type SystemHeartbeatClient struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SystemHeartbeatClient) Reset() { - *x = SystemHeartbeatClient{} - if protoimpl.UnsafeEnabled { - mi := &file_game_protobuf_system_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SystemHeartbeatClient) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SystemHeartbeatClient) ProtoMessage() {} - -func (x *SystemHeartbeatClient) ProtoReflect() protoreflect.Message { - mi := &file_game_protobuf_system_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SystemHeartbeatClient.ProtoReflect.Descriptor instead. -func (*SystemHeartbeatClient) Descriptor() ([]byte, []int) { - return file_game_protobuf_system_proto_rawDescGZIP(), []int{0} -} - -var File_game_protobuf_system_proto protoreflect.FileDescriptor - -var file_game_protobuf_system_proto_rawDesc = []byte{ - 0x0a, 0x1a, 0x67, 0x61, 0x6d, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, - 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_game_protobuf_system_proto_rawDescOnce sync.Once - file_game_protobuf_system_proto_rawDescData = file_game_protobuf_system_proto_rawDesc -) - -func file_game_protobuf_system_proto_rawDescGZIP() []byte { - file_game_protobuf_system_proto_rawDescOnce.Do(func() { - file_game_protobuf_system_proto_rawDescData = protoimpl.X.CompressGZIP(file_game_protobuf_system_proto_rawDescData) - }) - return file_game_protobuf_system_proto_rawDescData -} - -var file_game_protobuf_system_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_game_protobuf_system_proto_goTypes = []interface{}{ - (*SystemHeartbeatClient)(nil), // 0: protobuf.SystemHeartbeatClient -} -var file_game_protobuf_system_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_game_protobuf_system_proto_init() } -func file_game_protobuf_system_proto_init() { - if File_game_protobuf_system_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_game_protobuf_system_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SystemHeartbeatClient); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_game_protobuf_system_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_game_protobuf_system_proto_goTypes, - DependencyIndexes: file_game_protobuf_system_proto_depIdxs, - MessageInfos: file_game_protobuf_system_proto_msgTypes, - }.Build() - File_game_protobuf_system_proto = out.File - file_game_protobuf_system_proto_rawDesc = nil - file_game_protobuf_system_proto_goTypes = nil - file_game_protobuf_system_proto_depIdxs = nil -} diff --git a/game/protobuf/system.proto b/game/protobuf/system.proto deleted file mode 100644 index bf00a13..0000000 --- a/game/protobuf/system.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; -// protoc --proto_path . --go_out=./game/protobuf/protobuf/ --go-grpc_out=./game/protobuf/protobuf/ ./game/protobuf/*.proto - -package protobuf; -option go_package = "./;protobuf"; - -// 心跳 -message SystemHeartbeatClient {} \ No newline at end of file diff --git a/game/pushfail.go b/game/pushfail.go deleted file mode 100644 index ac8c006..0000000 --- a/game/pushfail.go +++ /dev/null @@ -1,8 +0,0 @@ -package game - -// PushFail 消息推送失败信息 -type PushFail struct { - Player *Player - Data []byte - Err error -} diff --git a/game/router.go b/game/router.go deleted file mode 100644 index 9e4b456..0000000 --- a/game/router.go +++ /dev/null @@ -1,123 +0,0 @@ -package game - -import ( - "fmt" - "go.uber.org/zap" - "minotaur/game/protobuf/protobuf" - "minotaur/utils/log" - "reflect" -) - -var allowReplaceMessages = map[int32]bool{ - int32(protobuf.MessageCode_SystemHeartbeat): true, -} - -type router struct { - handles map[int32][2]any // 玩家消息表 - - guestPlayerJoinEvents []func(guest *Player) - guestPlayerLeaveEvents []func(guest *Player) - heartbeatEvents []func(player *Player) - playerLoginEvents []func(player *Player, success bool) - gameLaunchEvents []func() -} - -func (slf *router) init(stateMachine *StateMachine) *router { - slf.RegMessagePlayer(int32(protobuf.MessageCode_SystemHeartbeat), func(player *Player) { - slf.OnHeartbeatEvent(player) - }) - - return slf -} - -// RegGameLaunchEvent 注册游戏启动完成事件 -func (slf *router) RegGameLaunchEvent(handleFunc func()) { - slf.gameLaunchEvents = append(slf.gameLaunchEvents, handleFunc) -} - -// OnGameLaunchEvent 游戏启动完成事件发生时 -func (slf *router) OnGameLaunchEvent() { - for _, event := range slf.gameLaunchEvents { - event() - } -} - -// RegPlayerLoginEvent 注册玩家登录事件 -func (slf *router) RegPlayerLoginEvent(handleFunc func(player *Player, success bool)) { - slf.playerLoginEvents = append(slf.playerLoginEvents, handleFunc) -} - -// OnPlayerLoginEvent 玩家登录事件发生时 -func (slf *router) OnPlayerLoginEvent(player *Player, success bool) { - for _, event := range slf.playerLoginEvents { - event(player, success) - } -} - -// RegHeartbeatEvent 注册客户端心跳事件 -func (slf *router) RegHeartbeatEvent(handleFunc func(guest *Player)) { - slf.heartbeatEvents = append(slf.heartbeatEvents, handleFunc) -} - -// OnHeartbeatEvent 客户端心跳事件发生时 -func (slf *router) OnHeartbeatEvent(guest *Player) { - for _, event := range slf.heartbeatEvents { - event(guest) - } -} - -// RegGuestPlayerJoinEvent 注册访客玩家加入事件 -func (slf *router) RegGuestPlayerJoinEvent(handleFunc func(guest *Player)) { - slf.guestPlayerJoinEvents = append(slf.guestPlayerJoinEvents, handleFunc) -} - -// OnGuestPlayerJoinEvent 访问玩家加入时 -func (slf *router) OnGuestPlayerJoinEvent(guest *Player) { - for _, event := range slf.guestPlayerJoinEvents { - event(guest) - } -} - -// RegGuestPlayerLeaveEvent 注册访客玩家离开事件 -func (slf *router) RegGuestPlayerLeaveEvent(handleFunc func(guest *Player)) { - slf.guestPlayerLeaveEvents = append(slf.guestPlayerLeaveEvents, handleFunc) -} - -// OnGuestPlayerLeaveEvent 访问玩家离开时 -func (slf *router) OnGuestPlayerLeaveEvent(guest *Player) { - for _, event := range slf.guestPlayerLeaveEvents { - event(guest) - } -} - -// RegMessagePlayer 注册玩家消息 -func (slf *router) RegMessagePlayer(messageCode int32, handleFunc any) { - if slf.handles == nil { - slf.handles = map[int32][2]any{} - } - - typeOf := reflect.TypeOf(handleFunc) - if typeOf.Kind() != reflect.Func { - panic(fmt.Errorf("register player message failed, handleFunc must is func(player *Player, message *protobufType)")) - } - if typeOf.NumIn() == 0 { - panic(fmt.Errorf("register player message failed, handleFunc must is func(player *Player) or func(player *Player, message *protobufType)")) - } - handle, exist := slf.handles[messageCode] - if exist && !allowReplaceMessages[messageCode] { - panic(fmt.Errorf("register player message failed, message %s existed and cannot be replaced", messageCode)) - } - - handle = [2]any{reflect.ValueOf(handleFunc), nil} - if typeOf.NumIn() == 2 { - messageType := typeOf.In(1) - handle[1] = messageType - } - - slf.handles[messageCode] = handle - if exist && allowReplaceMessages[messageCode] { - log.Info("RouterRegMessagePlayer", zap.Any("code", messageCode), zap.Any("messageType", handle[1]), zap.String("type", "replace")) - } else { - log.Info("RouterRegMessagePlayer", zap.Any("code", messageCode), zap.Any("messageType", handle[1])) - } -} diff --git a/game/statemachine.go b/game/statemachine.go deleted file mode 100644 index 66cf66f..0000000 --- a/game/statemachine.go +++ /dev/null @@ -1,150 +0,0 @@ -package game - -import ( - "context" - "github.com/sony/sonyflake" - "go.uber.org/zap" - "minotaur/utils/log" - "minotaur/utils/super" - "minotaur/utils/timer" - "os" - "os/signal" - "strconv" - "sync" - "syscall" - "time" -) - -// StateMachine 游戏状态机,维护全局游戏信息 -type StateMachine struct { - *gateway // 网关 - *router // 路由器 - *timer.Manager // 定时器 - *Time // 游戏时间 - channelRWMutex sync.RWMutex // 频道锁 - channels map[any]*channel // 所有频道 - channelStrategy func() (channelId any, size int) // 频道策略 - errChannel chan error // 异步错误管道 - loginTimeout time.Duration // 玩家登录超时时间 - - closeErrors []error // 关闭错误集 - closingMutex sync.Mutex // 关闭锁 - closed bool // 是否已关闭 - - sonyflake *sonyflake.Sonyflake // 雪花id生成器 -} - -// SetLoginTimeout 设置玩家登录超时时间,登录超时时,玩家将被踢出游戏(默认无超时) -func (slf *StateMachine) SetLoginTimeout(duration time.Duration) { - slf.loginTimeout = duration -} - -// SetChannelStrategy 设置频道策略,返回频道id和最大容纳人数 -func (slf *StateMachine) SetChannelStrategy(onGetChannelId func() (channelId any, size int)) { - slf.channelStrategy = onGetChannelId -} - -func (slf *StateMachine) Init() *StateMachine { - slf.router = new(router).init(slf) - slf.Time = new(Time) - return slf -} - -// Run 运行状态机 -func (slf *StateMachine) Run(appName string, port int, onCreateConnHandleFunc OnCreateConnHandleFunc) { - slf.errChannel = make(chan error, 1) - slf.channels = map[any]*channel{} - if slf.channelStrategy == nil { - slf.channelStrategy = func() (channelId any, size int) { - return 0, 0 - } - } - - slf.gateway = new(gateway).run(slf, appName, port, onCreateConnHandleFunc) - slf.Manager = timer.GetManager(64) - slf.sonyflake = sonyflake.NewSonyflake(sonyflake.Settings{}) - - log.Info("StateMachineRun", zap.String("stateMachine", "finish")) - - systemSignal := make(chan os.Signal, 1) - signal.Notify(systemSignal, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) - select { - case <-systemSignal: - slf.Shutdown(nil) - case err := <-slf.errChannel: - slf.Shutdown(err) - } -} - -// Shutdown 停止状态机 -func (slf *StateMachine) Shutdown(err error) { - if err != nil { - slf.closeErrors = append(slf.closeErrors, err) - } - - slf.closingMutex.Lock() - defer slf.closingMutex.Unlock() - if slf.closed { - return - } - slf.closed = true - - var shutDownTimeoutContext, cancel = context.WithTimeout(context.Background(), time.Second*10) - defer cancel() - - if err = slf.gateway.shutdown(shutDownTimeoutContext); err != nil { - slf.closeErrors = append(slf.closeErrors, err) - } - for _, c := range slf.channels { - c.release() - } - - slf.Manager.Release() - if len(slf.closeErrors) > 0 { - log.Error("StateMachineShutdown", zap.Errors("errors", slf.closeErrors)) - } else { - log.Info("StateMachineShutdown", zap.String("stateMachine", "normal")) - } -} - -// channel 获取频道,如果频道不存在将会进行创建 -// -// size: 指定频道最大容纳人数( <= 0 无限制) -// alone: 指定频道创建时候的逻辑是否以单线程运行 (default: true) -func (slf *StateMachine) channel(channelId any, size int, alone ...bool) *channel { - slf.channelRWMutex.Lock() - defer slf.channelRWMutex.Unlock() - - isAlone := true - if len(alone) > 0 { - isAlone = alone[0] - } - c := slf.channels[channelId] - if c == nil { - c = &channel{ - id: channelId, - size: size, - stateMachine: slf, - alone: isAlone, - players: map[int64]*Player{}, - timer: timer.GetManager(64), - } - slf.channels[channelId] = c - c.run() - log.Info("ChannelCreate", - zap.Any("channelID", channelId), - zap.Bool("alone", isAlone), - zap.Int("count", len(slf.channels)), - zap.String("size", super.If(c.size <= 0, "NaN", strconv.Itoa(c.size)))) - } - return c -} - -// GenerateGuid 生成一个 Guid -func (slf *StateMachine) GenerateGuid() int64 { - guid, err := slf.sonyflake.NextID() - if err != nil { - panic(err) - } - return int64(guid) -} diff --git a/go.mod b/go.mod index f33bcb1..82d039f 100644 --- a/go.mod +++ b/go.mod @@ -12,38 +12,64 @@ require ( require ( github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108 // indirect + github.com/aceld/zinx v1.1.16 // indirect + github.com/andeya/cfgo v0.0.0-20220626152948-7980c5d761c8 // indirect + github.com/andeya/erpc/v7 v7.2.0 // indirect + github.com/andeya/goutil v1.0.0 // indirect + github.com/apache/thrift v0.17.0 // indirect github.com/bytedance/sonic v1.8.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.11.2 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.2.0 // indirect github.com/goccy/go-json v0.10.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect github.com/jonboulle/clockwork v0.3.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.1.1 // indirect + github.com/klauspost/cpuid/v2 v2.2.2 // indirect github.com/klauspost/reedsolomon v1.11.7 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lestrrat-go/strftime v1.0.6 // indirect + github.com/lucas-clemente/quic-go v0.31.1 // indirect + github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect + github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/onsi/ginkgo/v2 v2.6.0 // indirect + github.com/panjf2000/gnet v1.6.6 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sony/sonyflake v1.1.0 // indirect + github.com/templexxx/cpu v0.0.9 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect + github.com/templexxx/xorsimd v0.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.9 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/xtaci/kcp-go v5.4.20+incompatible // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.6.0 // indirect + github.com/xtaci/kcp-go/v5 v5.6.1 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.5.0 // indirect + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect + golang.org/x/mod v0.7.0 // indirect golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/sys v0.7.0 // indirect golang.org/x/text v0.7.0 // indirect + golang.org/x/tools v0.4.0 // indirect google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c2a58c2..3af84d9 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,18 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108 h1:iPugyBI7oFtbDZXC4dnY093M1kZx6k/95sen92gafbY= github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108/go.mod h1:WAMLHwunr1hi3u7OjGV6/VWG9QbdMhGpEKjROiSFd10= +github.com/aceld/zinx v1.1.16 h1:KBgpa0NhpK6+RMDTQXPWGQjV5f/8P6KdPtrh5fIjctY= +github.com/aceld/zinx v1.1.16/go.mod h1:/ITipOv81IO2WIxcfXYI/S0sG62r9opKgHu1r7Qgjb0= +github.com/andeya/cfgo v0.0.0-20220626152948-7980c5d761c8 h1:2+qycdjXX/+EOQ3Rxkt39P5Vy5aQ0GeidW/6L4YWxtA= +github.com/andeya/cfgo v0.0.0-20220626152948-7980c5d761c8/go.mod h1:tVXGyZHyEkmXSzfmP5P3r4ymUI70sKFMaVqEsRnYtBE= +github.com/andeya/erpc/v7 v7.2.0 h1:Hv3c33azBzAk0OAF2JCU2I8/2VMGVzwtpuc0N3OsOxc= +github.com/andeya/erpc/v7 v7.2.0/go.mod h1:FY4XODBtYJnBLLE6xuWcxxyspwEZdECTjkQhRUfggYg= +github.com/andeya/goutil v1.0.0 h1:ZBF/1dParfzTTiZ3SivkrV3Rd50SnXPqqf4IRvULvNQ= +github.com/andeya/goutil v1.0.0/go.mod h1:jEG5/QnnhG7yGxwFUX6Q+JGMif7sjdHmmNVjn7nhJDo= +github.com/apache/thrift v0.17.0 h1:cMd2aj52n+8VoAtvSvLn4kDC3aZ6IAkBuqWQ2IDu7wo= +github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -37,11 +48,23 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.0 h1:u0p9s3xLYpZCA1z5JgCkMeB34CKCMMQbM+G8Ii7YD0I= +github.com/gobwas/ws v1.2.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= @@ -59,22 +82,32 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= +github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0= github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.2 h1:xPMwiykqNK9VK0NYC3+jTMYv9I6Vl3YdjZgPZKG3zO0= +github.com/klauspost/cpuid/v2 v2.2.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU= github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -87,17 +120,30 @@ github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkL github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ= github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= +github.com/lucas-clemente/quic-go v0.31.1 h1:O8Od7hfioqq0PMYHDyBkxU2aA7iZ2W9pjbrWuja2YR4= +github.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g= +github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= +github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE= +github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= +github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/panjf2000/ants/v2 v2.4.7/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= +github.com/panjf2000/gnet v1.6.6 h1:P6bApc54hnVcJVgH+SMe41mn47ECCajB6E/dKq27Y0c= +github.com/panjf2000/gnet v1.6.6/go.mod h1:KcOU7QsCaCBjeD5kyshBIamG3d9kAQtlob4Y0v0E+sc= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -112,16 +158,25 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= +github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= +github.com/templexxx/cpu v0.0.9 h1:cGGLK8twbc1J1S/fHnZW7BylXYaFP+0fR2s+nzsFDiU= +github.com/templexxx/cpu v0.0.9/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg= +github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -130,52 +185,99 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg= github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= +github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI= +github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo= +github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= +golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211204120058-94396e421777/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -187,8 +289,20 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -212,6 +326,13 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..b49b787 --- /dev/null +++ b/main.go @@ -0,0 +1,9 @@ +package main + +import "minotaur/server" + +func main() { + s := server.New(server.NetworkWebsocket) + + s.Run(":9999") +} diff --git a/server/conn.go b/server/conn.go new file mode 100644 index 0000000..4374c63 --- /dev/null +++ b/server/conn.go @@ -0,0 +1,45 @@ +package server + +import ( + "github.com/gorilla/websocket" + "github.com/panjf2000/gnet" +) + +func newGNetConn(conn gnet.Conn) *Conn { + return &Conn{ + ip: conn.RemoteAddr().String(), + gn: conn, + write: func(data []byte) error { + return conn.AsyncWrite(data) + }, + } +} + +func newWebsocketConn(ws *websocket.Conn) *Conn { + return &Conn{ + ws: ws, + write: func(data []byte) error { + return ws.WriteMessage(websocket.BinaryMessage, data) + }, + } +} + +type Conn struct { + ip string + ws *websocket.Conn + gn gnet.Conn + write func(data []byte) error +} + +func (slf *Conn) Write(data []byte) error { + return slf.write(data) +} + +func (slf *Conn) Close() { + if slf.ws != nil { + slf.ws.Close() + } else if slf.gn != nil { + slf.gn.Close() + } + slf.write = nil +} diff --git a/server/errors.go b/server/errors.go new file mode 100644 index 0000000..6513727 --- /dev/null +++ b/server/errors.go @@ -0,0 +1,12 @@ +package server + +import "errors" + +var ( + ErrConstructed = errors.New("the Server must be constructed using the server.New function") + ErrCanNotSupportNetwork = errors.New("can not support network") + ErrMessageTypePacketAttrs = errors.New("MessageTypePacket must contain *Conn and []byte") + ErrMessageTypeErrorAttrs = errors.New("MessageTypePacket must contain error and MessageErrorAction") + ErrNetworkOnlySupportHttp = errors.New("the current network mode is not compatible with HttpRouter, only NetworkHttp is supported") + ErrNetworkIncompatibleHttp = errors.New("the current network mode is not compatible with NetworkHttp") +) diff --git a/server/event.go b/server/event.go new file mode 100644 index 0000000..0ab17d1 --- /dev/null +++ b/server/event.go @@ -0,0 +1,65 @@ +package server + +import ( + "go.uber.org/zap" + "minotaur/utils/log" +) + +type ReceivePacketEventHandle func(conn *Conn, packet []byte) +type ConnectionOpenedEventHandle func(conn *Conn) +type ConnectionClosedEventHandle func(conn *Conn) + +type event struct { + *Server + receivePacketEventHandles []ReceivePacketEventHandle + connectionOpenedEventHandles []ConnectionOpenedEventHandle + connectionClosedEventHandles []ConnectionClosedEventHandle +} + +// RegConnectionClosedEvent 在连接关闭后将立刻执行被注册的事件处理函数 +func (slf *event) RegConnectionClosedEvent(handle ConnectionClosedEventHandle) { + if slf.network == NetworkHttp { + panic(ErrNetworkIncompatibleHttp) + } + slf.connectionClosedEventHandles = append(slf.connectionClosedEventHandles, handle) +} + +func (slf *event) OnConnectionClosedEvent(conn *Conn) { + for _, handle := range slf.connectionClosedEventHandles { + handle(conn) + } +} + +// RegConnectionOpenedEvent 在连接打开后将立刻执行被注册的事件处理函数 +func (slf *event) RegConnectionOpenedEvent(handle ConnectionOpenedEventHandle) { + if slf.network == NetworkHttp { + panic(ErrNetworkIncompatibleHttp) + } + slf.connectionOpenedEventHandles = append(slf.connectionOpenedEventHandles, handle) +} + +func (slf *event) OnConnectionOpenedEvent(conn *Conn) { + for _, handle := range slf.connectionOpenedEventHandles { + handle(conn) + } +} + +// RegReceivePacketEvent 在接收到数据包时将立刻执行被注册的事件处理函数 +func (slf *event) RegReceivePacketEvent(handle ReceivePacketEventHandle) { + if slf.network == NetworkHttp { + panic(ErrNetworkIncompatibleHttp) + } + slf.receivePacketEventHandles = append(slf.receivePacketEventHandles, handle) +} + +func (slf *event) OnReceivePacketEvent(conn *Conn, packet []byte) { + for _, handle := range slf.receivePacketEventHandles { + handle(conn, packet) + } +} + +func (slf *event) check() { + if len(slf.receivePacketEventHandles) == 0 { + log.Warn("Server", zap.String("ReceivePacketEvent", "Invalid server, no packets processed")) + } +} diff --git a/server/gserver.go b/server/gserver.go new file mode 100644 index 0000000..2e2e15d --- /dev/null +++ b/server/gserver.go @@ -0,0 +1,58 @@ +package server + +import ( + "github.com/panjf2000/gnet" + "minotaur/utils/synchronization" + "time" +) + +type gServer struct { + *Server + connections *synchronization.Map[string, *Conn] +} + +func (slf *gServer) OnInitComplete(server gnet.Server) (action gnet.Action) { + slf.connections = synchronization.NewMap[string, *Conn]() + return +} + +func (slf *gServer) OnShutdown(server gnet.Server) { + for k := range slf.connections.Map() { + slf.connections.Delete(k) + } + slf.connections = nil + return +} + +func (slf *gServer) OnOpened(c gnet.Conn) (out []byte, action gnet.Action) { + conn := newGNetConn(c) + slf.connections.Set(c.RemoteAddr().String(), conn) + slf.OnConnectionOpenedEvent(conn) + return +} + +func (slf *gServer) OnClosed(c gnet.Conn, err error) (action gnet.Action) { + slf.OnConnectionClosedEvent(slf.connections.DeleteGet(c.RemoteAddr().String())) + return +} + +func (slf *gServer) PreWrite(c gnet.Conn) { + return +} + +func (slf *gServer) AfterWrite(c gnet.Conn, b []byte) { + return +} + +func (slf *gServer) React(packet []byte, c gnet.Conn) (out []byte, action gnet.Action) { + if conn, exist := slf.connections.GetExist(c.RemoteAddr().String()); exist { + slf.Server.PushMessage(MessageTypePacket, conn, packet) + return nil, gnet.None + } else { + return nil, gnet.Close + } +} + +func (slf *gServer) Tick() (delay time.Duration, action gnet.Action) { + return +} diff --git a/server/message.go b/server/message.go new file mode 100644 index 0000000..3b87cff --- /dev/null +++ b/server/message.go @@ -0,0 +1,67 @@ +package server + +const ( + MessageTypePacket MessageType = iota + MessageTypeError +) + +var messageNames = map[MessageType]string{ + MessageTypePacket: "MessageTypePacket", + MessageTypeError: "MessageTypeError", +} + +const ( + MessageErrorActionNone MessageErrorAction = iota + MessageErrorActionShutdown +) + +var messageErrorActionNames = map[MessageErrorAction]string{ + MessageErrorActionNone: "None", + MessageErrorActionShutdown: "Shutdown", +} + +type ( + MessageType byte + MessageErrorAction byte +) + +func (slf MessageErrorAction) String() string { + return messageErrorActionNames[slf] +} + +type message struct { + t MessageType + attrs []any +} + +func (slf MessageType) String() string { + return messageNames[slf] +} + +func (slf MessageType) deconstructPacket(attrs ...any) (conn *Conn, packet []byte) { + if len(attrs) != 2 { + panic(ErrMessageTypePacketAttrs) + } + var ok bool + if conn, ok = attrs[0].(*Conn); !ok { + panic(ErrMessageTypePacketAttrs) + } + if packet, ok = attrs[1].([]byte); !ok { + panic(ErrMessageTypePacketAttrs) + } + return +} + +func (slf MessageType) deconstructError(attrs ...any) (err error, action MessageErrorAction) { + if len(attrs) != 2 { + panic(ErrMessageTypeErrorAttrs) + } + var ok bool + if err, ok = attrs[0].(error); !ok { + panic(ErrMessageTypeErrorAttrs) + } + if action, ok = attrs[1].(MessageErrorAction); !ok { + panic(ErrMessageTypeErrorAttrs) + } + return +} diff --git a/server/network.go b/server/network.go new file mode 100644 index 0000000..a80d37c --- /dev/null +++ b/server/network.go @@ -0,0 +1,15 @@ +package server + +type Network string + +const ( + NetworkTCP Network = "tcp" + NetworkTCP4 Network = "tcp4" + NetworkTCP6 Network = "tcp6" + NetworkUdp Network = "udp" + NetworkUdp4 Network = "udp4" + NetworkUdp6 Network = "udp6" + NetworkUnix Network = "unix" + NetworkHttp Network = "http" + NetworkWebsocket Network = "websocket" +) diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..89512cb --- /dev/null +++ b/server/server.go @@ -0,0 +1,203 @@ +package server + +import ( + "fmt" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/panjf2000/gnet" + "go.uber.org/zap" + "minotaur/utils/log" + "net/http" + "os" + "os/signal" + "strings" + "syscall" + "time" +) + +func New(network Network) *Server { + server := &Server{ + network: network, + } + server.event = &event{Server: server} + + if network == NetworkHttp { + server.httpServer = gin.New() + } + return server +} + +type Server struct { + *event + network Network + addr string + httpServer *gin.Engine + gServer *gServer + messageChannel chan *message +} + +// Run 使用特定网络模式运行服务器 +// +// server.NetworkTCP (addr:":8888") +// server.NetworkTCP4 (addr:":8888") +// server.NetworkTCP6 (addr:":8888") +// server.NetworkUDP (addr:":8888") +// server.NetworkUDP4 (addr:":8888") +// server.NetworkUDP6 (addr:":8888") +// server.Unix (addr:"socketPath") +// server.Http (addr:":8888") +// server.Websocket (addr:":8888/ws") +func (slf *Server) Run(addr string) error { + if slf.event == nil { + return ErrConstructed + } + slf.event.check() + slf.addr = addr + var protoAddr = fmt.Sprintf("%s://%s", slf.network, slf.addr) + var connectionInitHandle = func() { + slf.messageChannel = make(chan *message, 4096*1000) + if slf.network != NetworkHttp && slf.network != NetworkWebsocket { + slf.gServer = &gServer{Server: slf} + } + for message := range slf.messageChannel { + slf.dispatchMessage(message) + } + } + + switch slf.network { + case NetworkTCP, NetworkTCP4, NetworkTCP6, NetworkUdp, NetworkUdp4, NetworkUdp6, NetworkUnix: + go connectionInitHandle() + go func() { + if err := gnet.Serve(slf.gServer, protoAddr); err != nil { + slf.PushMessage(MessageTypeError, err, MessageErrorActionShutdown) + } + }() + case NetworkHttp: + go func() { + if err := slf.httpServer.Run(addr); err != nil { + slf.PushMessage(MessageTypeError, err, MessageErrorActionShutdown) + } + }() + case NetworkWebsocket: + go connectionInitHandle() + var pattern string + var index = strings.Index(addr, "/") + if index == -1 { + pattern = "/" + } else { + pattern = addr[index:] + } + var upgrade = websocket.Upgrader{ + ReadBufferSize: 4096, + WriteBufferSize: 4096, + CheckOrigin: func(r *http.Request) bool { + return true + }, + } + http.HandleFunc(pattern, func(writer http.ResponseWriter, request *http.Request) { + ip := request.Header.Get("X-Real-IP") + ws, err := upgrade.Upgrade(writer, request, nil) + if err != nil { + return + } + if len(ip) == 0 { + addr := ws.RemoteAddr().String() + if index := strings.LastIndex(addr, ":"); index != -1 { + ip = addr[0:index] + } + } + + conn := newWebsocketConn(ws) + conn.ip = ip + + defer func() { + if err := recover(); err != nil { + conn.Close() + slf.OnConnectionClosedEvent(conn) + } + }() + + for { + if err := ws.SetReadDeadline(time.Now().Add(time.Second * 30)); err != nil { + panic(err) + } + _, packet, err := ws.ReadMessage() + if err != nil { + panic(err) + } + slf.PushMessage(MessageTypePacket, conn, packet) + + } + }) + go func() { + if err := http.ListenAndServe(slf.addr, nil); err != nil { + slf.PushMessage(MessageTypeError, err, MessageErrorActionShutdown) + } + }() + default: + return ErrCanNotSupportNetwork + } + + log.Info("Server", zap.String("Minotaur Server", "====================================================================")) + log.Info("Server", zap.String("Minotaur Server", "RunningInfo"), + zap.Any("network", slf.network), + zap.String("listen", slf.addr), + ) + log.Info("Server", zap.String("Minotaur Server", "====================================================================")) + + systemSignal := make(chan os.Signal, 1) + signal.Notify(systemSignal, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + select { + case <-systemSignal: + slf.Shutdown(nil) + } + return nil +} + +func (slf *Server) Shutdown(err error) { + close(slf.messageChannel) + if err != nil { + log.Error("Server", zap.String("action", "shutdown"), zap.String("state", "exception"), zap.Error(err)) + } else { + log.Info("Server", zap.String("action", "shutdown"), zap.String("state", "normal")) + } +} + +func (slf *Server) HttpRouter() gin.IRouter { + if slf.httpServer == nil { + panic(ErrNetworkOnlySupportHttp) + } + return slf.httpServer +} + +func (slf *Server) PushMessage(messageType MessageType, attrs ...any) { + slf.messageChannel <- &message{ + t: messageType, + attrs: attrs, + } +} + +func (slf *Server) dispatchMessage(msg *message) { + defer func() { + if err := recover(); err != nil { + log.Error("Server", zap.Any("error", err)) + } + }() + switch msg.t { + case MessageTypePacket: + conn, packet := msg.t.deconstructPacket(msg.attrs...) + slf.OnReceivePacketEvent(conn, packet) + case MessageTypeError: + err, action := msg.t.deconstructError(msg.attrs...) + switch action { + case MessageErrorActionNone: + log.Error("Server", zap.Error(err)) + case MessageErrorActionShutdown: + slf.Shutdown(err) + default: + log.Warn("Server", zap.String("not support message error action", action.String())) + } + default: + log.Warn("Server", zap.String("not support message type", msg.t.String())) + } +} diff --git a/utils/file/file.go b/utils/file/file.go new file mode 100644 index 0000000..e257bd5 --- /dev/null +++ b/utils/file/file.go @@ -0,0 +1,24 @@ +package file + +import "os" + +// PathExist 路径是否存在 +func PathExist(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +// IsDir 路径是否是文件夹 +func IsDir(path string) (bool, error) { + fileInfo, err := os.Stat(path) + if err == nil { + return fileInfo.IsDir(), nil + } + return false, err +} diff --git a/utils/g2d/g2d.go b/utils/g2d/g2d.go index 3e17831..63686d6 100644 --- a/utils/g2d/g2d.go +++ b/utils/g2d/g2d.go @@ -1,6 +1,8 @@ package g2d -import "minotaur/utils/g2d/matrix" +import ( + "minotaur/utils/g2d/matrix" +) // NewMatrix 生成特定宽高的二维矩阵 func NewMatrix[T any](width, height int) *matrix.Matrix[T] { diff --git a/utils/gin/middlewares/cros.go b/utils/gin/middlewares/cros.go deleted file mode 100644 index c965b14..0000000 --- a/utils/gin/middlewares/cros.go +++ /dev/null @@ -1,43 +0,0 @@ -package middlewares - -import ( - "github.com/gin-gonic/gin" - "go.uber.org/zap" - "minotaur/utils/log" - "net/http" - "runtime/debug" -) - -// Cors 设置跨域 -func Cors() gin.HandlerFunc { - return func(c *gin.Context) { - method := c.Request.Method - origin := c.Request.Header.Get("Origin") // 请求头部 - if origin != "" { - // 接收客户端发送的origin (重要!) - c.Writer.Header().Set("Access-Control-Allow-Origin", origin) - // 服务器支持的所有跨域请求的方法 - c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") - // 允许跨域设置可以返回其他子段,可以自定义字段 - c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session") - // 允许浏览器(客户端)可以解析的头部 (重要) - c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers") - // 设置缓存时间 - // c.Header("Access-Control-Max-Age", "172800") - // 允许客户端传递校验信息比如 cookie (重要) - c.Header("Access-Control-Allow-Credentials", "true") - } - - // 允许类型校验 - if method == "OPTIONS" { - c.AbortWithStatus(http.StatusNoContent) - } - - defer func() { - if err := recover(); err != nil { - log.Error("Panic info is", zap.Any("err", err), zap.Any("stack\n", string(debug.Stack()))) - } - }() - c.Next() - } -} diff --git a/utils/gin/middlewares/logger.go b/utils/gin/middlewares/logger.go deleted file mode 100644 index 52a99be..0000000 --- a/utils/gin/middlewares/logger.go +++ /dev/null @@ -1,28 +0,0 @@ -package middlewares - -import ( - "github.com/gin-gonic/gin" - "go.uber.org/zap" - "time" -) - -func Logger(logger *zap.Logger) gin.HandlerFunc { - return func(c *gin.Context) { - start := time.Now() - path := c.Request.URL.Path - query := c.Request.URL.RawQuery - c.Next() - - cost := time.Since(start) - logger.Info(path, - zap.Int("status", c.Writer.Status()), - zap.String("method", c.Request.Method), - zap.String("path", path), - zap.String("query", query), - zap.String("ip", c.ClientIP()), - zap.String("user-agent", c.Request.UserAgent()), - zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()), - zap.Duration("cost", cost), - ) - } -} diff --git a/utils/log/log.go b/utils/log/log.go index 4d4e44e..521f8f7 100644 --- a/utils/log/log.go +++ b/utils/log/log.go @@ -12,11 +12,6 @@ import ( var ( Logger *zap.Logger - - Info func(msg string, fields ...zap.Field) - Warn func(msg string, fields ...zap.Field) - Debug func(msg string, fields ...zap.Field) - Error func(msg string, fields ...zap.Field) ) const ( @@ -27,11 +22,6 @@ const ( func init() { Logger = newLogger() - - Info = Logger.Info - Warn = Logger.Warn - Debug = Logger.Debug - Error = Logger.Error } func newLogger() *zap.Logger { @@ -94,3 +84,19 @@ func getWriter(filename string, times int32) io.Writer { } return hook } + +func Info(msg string, fields ...zap.Field) { + Logger.Info(msg, fields...) +} + +func Warn(msg string, fields ...zap.Field) { + Logger.Warn(msg, fields...) +} + +func Debug(msg string, fields ...zap.Field) { + Logger.Debug(msg, fields...) +} + +func Error(msg string, fields ...zap.Field) { + Logger.Error(msg, fields...) +} diff --git a/utils/runtimes/path.go b/utils/runtimes/path.go new file mode 100644 index 0000000..4ffc4e7 --- /dev/null +++ b/utils/runtimes/path.go @@ -0,0 +1,49 @@ +package runtimes + +import ( + "log" + "os" + "path" + "path/filepath" + "runtime" + "strings" +) + +// GetWorkingDir 获取工作目录绝对路径 +func GetWorkingDir() string { + dir := GetExecutablePathByBuild() + if strings.Contains(dir, GetTempDir()) { + return GetExecutablePathByCaller() + } + return dir +} + +// GetTempDir 获取系统临时目录 +func GetTempDir() string { + dir := os.Getenv("TEMP") + if dir == "" { + dir = os.Getenv("TMP") + } + res, _ := filepath.EvalSymlinks(dir) + return res +} + +// GetExecutablePathByBuild 获取当前执行文件绝对路径(go build) +func GetExecutablePathByBuild() string { + exePath, err := os.Executable() + if err != nil { + log.Fatal(err) + } + res, _ := filepath.EvalSymlinks(filepath.Dir(exePath)) + return res +} + +// GetExecutablePathByCaller 获取当前执行文件绝对路径(go run) +func GetExecutablePathByCaller() string { + var abPath string + _, filename, _, ok := runtime.Caller(3) + if ok { + abPath = path.Dir(filename) + } + return abPath +} diff --git a/utils/sole/guid.go b/utils/sole/guid.go new file mode 100644 index 0000000..c5d84dd --- /dev/null +++ b/utils/sole/guid.go @@ -0,0 +1,37 @@ +package sole + +import "sync" + +var ( + global int64 + namespace map[any]int64 + mutex sync.Mutex +) + +func init() { + namespace = map[any]int64{} +} + +func Get() int64 { + global++ + return global +} + +func GetWith(name any) int64 { + namespace[name]++ + return namespace[name] +} + +func GetSync() int64 { + mutex.Lock() + defer mutex.Unlock() + global++ + return global +} + +func GetSyncWith(name any) int64 { + mutex.Lock() + defer mutex.Unlock() + namespace[name]++ + return namespace[name] +} diff --git a/utils/sole/sonyflake.go b/utils/sole/sonyflake.go new file mode 100644 index 0000000..d3c6dac --- /dev/null +++ b/utils/sole/sonyflake.go @@ -0,0 +1,29 @@ +package sole + +import "github.com/sony/sonyflake" + +var sonyflakeGenerator *sonyflake.Sonyflake + +func init() { + sonyflakeGenerator = sonyflake.NewSonyflake(sonyflake.Settings{}) +} + +// SonyflakeIDE 获取一个雪花id +func SonyflakeIDE() (int64, error) { + id, err := sonyflakeGenerator.NextID() + return int64(id), err +} + +// SonyflakeID 获取一个雪花id +func SonyflakeID() int64 { + id, err := sonyflakeGenerator.NextID() + if err != nil { + panic(err) + } + return int64(id) +} + +// SonyflakeSetting 配置雪花id生成策略 +func SonyflakeSetting(settings sonyflake.Settings) { + sonyflakeGenerator = sonyflake.NewSonyflake(settings) +} diff --git a/utils/synchronization/map.go b/utils/synchronization/map.go new file mode 100644 index 0000000..e67d81b --- /dev/null +++ b/utils/synchronization/map.go @@ -0,0 +1,162 @@ +package synchronization + +import "sync" + +func NewMap[Key comparable, value any]() *Map[Key, value] { + return &Map[Key, value]{ + data: make(map[Key]value), + } +} + +type Map[Key comparable, Value any] struct { + lock sync.RWMutex + data map[Key]Value +} + +func (slf *Map[Key, Value]) Set(key Key, value Value) { + slf.lock.Lock() + defer slf.lock.Unlock() + slf.data[key] = value +} + +func (slf *Map[Key, Value]) Get(key Key) Value { + slf.lock.RLock() + defer slf.lock.RUnlock() + return slf.data[key] +} + +func (slf *Map[Key, Value]) Exist(key Key) bool { + slf.lock.RLock() + _, exist := slf.data[key] + slf.lock.RUnlock() + return exist +} + +func (slf *Map[Key, Value]) GetExist(key Key) (Value, bool) { + slf.lock.RLock() + value, exist := slf.data[key] + slf.lock.RUnlock() + return value, exist +} + +func (slf *Map[Key, Value]) Length() int { + slf.lock.RLock() + defer slf.lock.RUnlock() + return len(slf.data) +} + +func (slf *Map[Key, Value]) Delete(key Key) { + slf.lock.Lock() + defer slf.lock.Unlock() + delete(slf.data, key) +} + +func (slf *Map[Key, Value]) DeleteGet(key Key) Value { + slf.lock.Lock() + defer slf.lock.Unlock() + v := slf.data[key] + delete(slf.data, key) + return v +} + +func (slf *Map[Key, Value]) DeleteGetExist(key Key) (Value, bool) { + slf.lock.Lock() + defer slf.lock.Unlock() + v, exist := slf.data[key] + delete(slf.data, key) + return v, exist +} + +func (slf *Map[Key, Value]) DeleteExist(key Key) bool { + slf.lock.Lock() + if _, exist := slf.data[key]; !exist { + slf.lock.Unlock() + return exist + } + delete(slf.data, key) + slf.lock.Unlock() + return true +} + +func (slf *Map[Key, Value]) Range(handle func(key Key, value Value)) { + slf.lock.RLock() + defer slf.lock.RUnlock() + for k, v := range slf.data { + key, value := k, v + handle(key, value) + } +} + +func (slf *Map[Key, Value]) RangeSkip(handle func(key Key, value Value) bool) { + slf.lock.RLock() + defer slf.lock.RUnlock() + for k, v := range slf.data { + key, value := k, v + if !handle(key, value) { + continue + } + } +} + +func (slf *Map[Key, Value]) RangeBreakout(handle func(key Key, value Value) bool) { + slf.lock.RLock() + defer slf.lock.RUnlock() + for k, v := range slf.data { + key, value := k, v + if !handle(key, value) { + break + } + } +} + +func (slf *Map[Key, Value]) RangeFree(handle func(key Key, value Value, skip func(), breakout func())) { + var skipExec, breakoutExec bool + var skip = func() { + skipExec = true + } + var breakout = func() { + breakoutExec = true + } + slf.lock.RLock() + defer slf.lock.RUnlock() + for k, v := range slf.data { + key, value := k, v + handle(key, value, skip, breakout) + if skipExec { + continue + } + if breakoutExec { + break + } + } +} + +func (slf *Map[Key, Value]) Keys() []Key { + slf.lock.RLock() + var s = make([]Key, 0, len(slf.data)) + for k, _ := range slf.data { + s = append(s, k) + } + slf.lock.RUnlock() + return s +} + +func (slf *Map[Key, Value]) Slice() []Value { + slf.lock.RLock() + var s = make([]Value, 0, len(slf.data)) + for _, v := range slf.data { + s = append(s, v) + } + slf.lock.RUnlock() + return s +} + +func (slf *Map[Key, Value]) Map() map[Key]Value { + var m = make(map[Key]Value) + slf.lock.RLock() + for k, v := range slf.data { + m[k] = v + } + slf.lock.RUnlock() + return m +}