重构服务器
This commit is contained in:
parent
45a9d9b4e3
commit
481ccc182a
|
@ -1,5 +1,5 @@
|
|||
### Minotaur
|
||||
app/monopoly/
|
||||
old/app/monopoly/
|
||||
|
||||
### VisualStudioCode template
|
||||
.vscode/*
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"minotaur/game"
|
||||
)
|
||||
|
||||
var State = new(game.StateMachine).Init()
|
|
@ -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())
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
183
game/channel.go
183
game/channel.go
|
@ -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
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package game
|
||||
|
||||
// Conn 用户连接抽象
|
||||
//
|
||||
// 连接支持使用 tag 进行参数读取
|
||||
type Conn interface {
|
||||
GetConn() any
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package feature
|
||||
|
||||
// Actor 玩家演员接口定义
|
||||
type Actor interface {
|
||||
// GetGuid 获取演员 guid
|
||||
GetGuid() int64
|
||||
// GetPlayer 获取所属玩家
|
||||
GetPlayer() Player
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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]]
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package feature
|
||||
|
||||
// Player 玩家接口定义
|
||||
type Player interface {
|
||||
// GetGuid 获取玩家 guid
|
||||
GetGuid() int64
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package standard
|
||||
|
||||
import (
|
||||
"minotaur/game"
|
||||
)
|
||||
|
||||
type Player struct {
|
||||
*game.Player
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package feature
|
||||
|
||||
import "minotaur/utils/timer"
|
||||
|
||||
// Timer 定时器接口定义
|
||||
type Timer interface {
|
||||
// Timer 获取定时器
|
||||
Timer() *timer.Manager
|
||||
}
|
|
@ -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)
|
||||
}
|
147
game/gateway.go
147
game/gateway.go
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
192
game/player.go
192
game/player.go
|
@ -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),
|
||||
)
|
||||
}
|
||||
}))
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 {}
|
|
@ -1,8 +0,0 @@
|
|||
package game
|
||||
|
||||
// PushFail 消息推送失败信息
|
||||
type PushFail struct {
|
||||
Player *Player
|
||||
Data []byte
|
||||
Err error
|
||||
}
|
123
game/router.go
123
game/router.go
|
@ -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]))
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
34
go.mod
34
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
|
||||
)
|
||||
|
|
121
go.sum
121
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=
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
import "minotaur/server"
|
||||
|
||||
func main() {
|
||||
s := server.New(server.NetworkWebsocket)
|
||||
|
||||
s.Run(":9999")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
)
|
|
@ -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"))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
)
|
|
@ -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()))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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] {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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]
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue