refactor: 移除分段锁map实现及 hash.Map、hash.ReadonlyMap 接口,移除 asynchronous 包,同步包更名为 concurrent

This commit is contained in:
kercylan 2023-07-21 23:32:46 +08:00
parent 2cbffbf967
commit d0d2087fee
30 changed files with 363 additions and 922 deletions

View File

@ -3,7 +3,7 @@ package components
import ( import (
"encoding/json" "encoding/json"
"github.com/kercylan98/minotaur/component" "github.com/kercylan98/minotaur/component"
"github.com/kercylan98/minotaur/utils/synchronization" "github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/timer" "github.com/kercylan98/minotaur/utils/timer"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -13,8 +13,8 @@ import (
// NewLockstep 创建一个锁步(帧)同步默认实现的组件(Lockstep)进行返回 // NewLockstep 创建一个锁步(帧)同步默认实现的组件(Lockstep)进行返回
func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[ClientID, Command]) *Lockstep[ClientID, Command] { func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[ClientID, Command]) *Lockstep[ClientID, Command] {
lockstep := &Lockstep[ClientID, Command]{ lockstep := &Lockstep[ClientID, Command]{
clients: synchronization.NewMap[ClientID, component.LockstepClient[ClientID]](), clients: concurrent.NewBalanceMap[ClientID, component.LockstepClient[ClientID]](),
frames: synchronization.NewMap[int, []Command](), frames: concurrent.NewBalanceMap[int, []Command](),
ticker: timer.GetTicker(10), ticker: timer.GetTicker(10),
frameRate: 15, frameRate: 15,
serialization: func(frame int, commands []Command) []byte { serialization: func(frame int, commands []Command) []byte {
@ -25,7 +25,7 @@ func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[Cli
data, _ := json.Marshal(frameStruct) data, _ := json.Marshal(frameStruct)
return data return data
}, },
clientCurrentFrame: synchronization.NewMap[ClientID, int](), clientCurrentFrame: concurrent.NewBalanceMap[ClientID, int](),
} }
for _, option := range options { for _, option := range options {
option(lockstep) option(lockstep)
@ -40,12 +40,12 @@ func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[Cli
// - 从特定帧开始追帧 // - 从特定帧开始追帧
// - 兼容各种基于TCP/UDP/Unix的网络类型可通过客户端实现其他网络类型同步 // - 兼容各种基于TCP/UDP/Unix的网络类型可通过客户端实现其他网络类型同步
type Lockstep[ClientID comparable, Command any] struct { type Lockstep[ClientID comparable, Command any] struct {
clients *synchronization.Map[ClientID, component.LockstepClient[ClientID]] // 接受广播的客户端 clients *concurrent.BalanceMap[ClientID, component.LockstepClient[ClientID]] // 接受广播的客户端
frames *synchronization.Map[int, []Command] // 所有帧指令 frames *concurrent.BalanceMap[int, []Command] // 所有帧指令
ticker *timer.Ticker // 定时器 ticker *timer.Ticker // 定时器
frameMutex sync.Mutex // 帧锁 frameMutex sync.Mutex // 帧锁
currentFrame int // 当前帧 currentFrame int // 当前帧
clientCurrentFrame *synchronization.Map[ClientID, int] // 客户端当前帧数 clientCurrentFrame *concurrent.BalanceMap[ClientID, int] // 客户端当前帧数
running atomic.Bool running atomic.Bool
frameRate int // 帧率每秒N帧 frameRate int // 帧率每秒N帧
@ -123,8 +123,8 @@ func (slf *Lockstep[ClientID, Command]) StopBroadcast() {
// AddCommand 添加命令到当前帧 // AddCommand 添加命令到当前帧
func (slf *Lockstep[ClientID, Command]) AddCommand(command Command) { func (slf *Lockstep[ClientID, Command]) AddCommand(command Command) {
slf.frames.AtomGetSet(slf.currentFrame, func(value []Command, exist bool) (newValue []Command, isSet bool) { slf.frames.Atom(func(m map[int][]Command) {
return append(value, command), true m[slf.currentFrame] = append(m[slf.currentFrame], command)
}) })
} }

View File

@ -2,18 +2,18 @@ package builtin
import ( import (
"github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/huge" "github.com/kercylan98/minotaur/utils/huge"
"github.com/kercylan98/minotaur/utils/synchronization"
) )
func NewAttrs() *Attrs { func NewAttrs() *Attrs {
return &Attrs{ return &Attrs{
attrs: synchronization.NewMap[int, any](), attrs: concurrent.NewBalanceMap[int, any](),
} }
} }
type Attrs struct { type Attrs struct {
attrs *synchronization.Map[int, any] attrs *concurrent.BalanceMap[int, any]
attrChangeEventHandles []game.AttrChangeEventHandle attrChangeEventHandles []game.AttrChangeEventHandle
attrIdChangeEventHandles map[int][]game.AttrChangeEventHandle attrIdChangeEventHandles map[int][]game.AttrChangeEventHandle

View File

@ -3,15 +3,15 @@ package builtin
import ( import (
"encoding/json" "encoding/json"
"github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/generic" "github.com/kercylan98/minotaur/utils/generic"
"github.com/kercylan98/minotaur/utils/synchronization"
) )
// NewRankingList 创建一个排名从0开始的排行榜 // NewRankingList 创建一个排名从0开始的排行榜
func NewRankingList[CompetitorID comparable, Score generic.Ordered](options ...RankingListOption[CompetitorID, Score]) *RankingList[CompetitorID, Score] { func NewRankingList[CompetitorID comparable, Score generic.Ordered](options ...RankingListOption[CompetitorID, Score]) *RankingList[CompetitorID, Score] {
rankingList := &RankingList[CompetitorID, Score]{ rankingList := &RankingList[CompetitorID, Score]{
rankCount: 100, rankCount: 100,
competitors: synchronization.NewMap[CompetitorID, Score](), competitors: concurrent.NewBalanceMap[CompetitorID, Score](),
} }
for _, option := range options { for _, option := range options {
option(rankingList) option(rankingList)
@ -22,7 +22,7 @@ func NewRankingList[CompetitorID comparable, Score generic.Ordered](options ...R
type RankingList[CompetitorID comparable, Score generic.Ordered] struct { type RankingList[CompetitorID comparable, Score generic.Ordered] struct {
asc bool asc bool
rankCount int rankCount int
competitors *synchronization.Map[CompetitorID, Score] competitors *concurrent.BalanceMap[CompetitorID, Score]
scores []*scoreItem[CompetitorID, Score] // CompetitorID, Score scores []*scoreItem[CompetitorID, Score] // CompetitorID, Score
rankChangeEventHandles []game.RankChangeEventHandle[CompetitorID, Score] rankChangeEventHandles []game.RankChangeEventHandle[CompetitorID, Score]
@ -244,11 +244,11 @@ func (slf *RankingList[CompetitorID, Score]) competitor(competitorId CompetitorI
func (slf *RankingList[CompetitorID, Score]) UnmarshalJSON(bytes []byte) error { func (slf *RankingList[CompetitorID, Score]) UnmarshalJSON(bytes []byte) error {
var t struct { var t struct {
Competitors *synchronization.Map[CompetitorID, Score] `json:"competitors,omitempty"` Competitors *concurrent.BalanceMap[CompetitorID, Score] `json:"competitors,omitempty"`
Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"`
Asc bool `json:"asc,omitempty"` Asc bool `json:"asc,omitempty"`
} }
t.Competitors = synchronization.NewMap[CompetitorID, Score]() t.Competitors = concurrent.NewBalanceMap[CompetitorID, Score]()
if err := json.Unmarshal(bytes, &t); err != nil { if err := json.Unmarshal(bytes, &t); err != nil {
return err return err
} }
@ -260,9 +260,9 @@ func (slf *RankingList[CompetitorID, Score]) UnmarshalJSON(bytes []byte) error {
func (slf *RankingList[CompetitorID, Score]) MarshalJSON() ([]byte, error) { func (slf *RankingList[CompetitorID, Score]) MarshalJSON() ([]byte, error) {
var t struct { var t struct {
Competitors *synchronization.Map[CompetitorID, Score] `json:"competitors,omitempty"` Competitors *concurrent.BalanceMap[CompetitorID, Score] `json:"competitors,omitempty"`
Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"`
Asc bool `json:"asc,omitempty"` Asc bool `json:"asc,omitempty"`
} }
t.Competitors = slf.competitors t.Competitors = slf.competitors
t.Scores = slf.scores t.Scores = slf.scores

View File

@ -2,8 +2,7 @@ package builtin
import ( import (
"github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/asynchronous" "github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/hash"
"github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/log"
) )
@ -11,7 +10,7 @@ import (
func NewRoom[PlayerID comparable, Player game.Player[PlayerID]](guid int64, options ...RoomOption[PlayerID, Player]) *Room[PlayerID, Player] { func NewRoom[PlayerID comparable, Player game.Player[PlayerID]](guid int64, options ...RoomOption[PlayerID, Player]) *Room[PlayerID, Player] {
room := &Room[PlayerID, Player]{ room := &Room[PlayerID, Player]{
guid: guid, guid: guid,
players: asynchronous.NewMap[PlayerID, Player](), players: concurrent.NewBalanceMap[PlayerID, Player](),
} }
for _, option := range options { for _, option := range options {
option(room) option(room)
@ -27,7 +26,7 @@ type Room[PlayerID comparable, Player game.Player[PlayerID]] struct {
owner PlayerID owner PlayerID
noMaster bool noMaster bool
playerLimit int playerLimit int
players hash.Map[PlayerID, Player] players *concurrent.BalanceMap[PlayerID, Player]
kickCheckHandle func(room *Room[PlayerID, Player], id, target PlayerID) error kickCheckHandle func(room *Room[PlayerID, Player], id, target PlayerID) error
playerJoinRoomEventHandles []game.PlayerJoinRoomEventHandle[PlayerID, Player] playerJoinRoomEventHandles []game.PlayerJoinRoomEventHandle[PlayerID, Player]
@ -51,8 +50,8 @@ func (slf *Room[PlayerID, Player]) GetPlayer(id PlayerID) Player {
} }
// GetPlayers 获取所有玩家 // GetPlayers 获取所有玩家
func (slf *Room[PlayerID, Player]) GetPlayers() hash.MapReadonly[PlayerID, Player] { func (slf *Room[PlayerID, Player]) GetPlayers() map[PlayerID]Player {
return slf.players.(hash.MapReadonly[PlayerID, Player]) return slf.players.Map()
} }
// GetPlayerCount 获取玩家数量 // GetPlayerCount 获取玩家数量

View File

@ -2,20 +2,20 @@ package builtin
import ( import (
"github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/synchronization" "github.com/kercylan98/minotaur/utils/concurrent"
"sync/atomic" "sync/atomic"
) )
func NewRoomManager[PlayerID comparable, Room game.Room[PlayerID, game.Player[PlayerID]]]() *RoomManager[PlayerID, Room] { func NewRoomManager[PlayerID comparable, Room game.Room[PlayerID, game.Player[PlayerID]]]() *RoomManager[PlayerID, Room] {
return &RoomManager[PlayerID, Room]{ return &RoomManager[PlayerID, Room]{
rooms: synchronization.NewMap[int64, Room](), rooms: concurrent.NewBalanceMap[int64, Room](),
} }
} }
// RoomManager 房间管理器 // RoomManager 房间管理器
type RoomManager[PlayerID comparable, Room game.Room[PlayerID, game.Player[PlayerID]]] struct { type RoomManager[PlayerID comparable, Room game.Room[PlayerID, game.Player[PlayerID]]] struct {
guid atomic.Int64 guid atomic.Int64
rooms *synchronization.Map[int64, Room] rooms *concurrent.BalanceMap[int64, Room]
} }
// GenGuid 生成一个新的房间guid // GenGuid 生成一个新的房间guid

View File

@ -2,7 +2,7 @@ package builtin
import ( import (
"github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/synchronization" "github.com/kercylan98/minotaur/utils/concurrent"
) )
// RoomOption 房间构建可选项 // RoomOption 房间构建可选项
@ -11,7 +11,7 @@ type RoomOption[PlayerID comparable, Player game.Player[PlayerID]] func(room *Ro
// WithRoomSync 通过线程安全的方式创建房间 // WithRoomSync 通过线程安全的方式创建房间
func WithRoomSync[PlayerID comparable, Player game.Player[PlayerID]]() RoomOption[PlayerID, Player] { func WithRoomSync[PlayerID comparable, Player game.Player[PlayerID]]() RoomOption[PlayerID, Player] {
return func(room *Room[PlayerID, Player]) { return func(room *Room[PlayerID, Player]) {
room.players = synchronization.NewMap[PlayerID, Player]() room.players = concurrent.NewBalanceMap[PlayerID, Player]()
} }
} }

View File

@ -2,7 +2,7 @@ package builtin
import ( import (
"github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/asynchronous" "github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/hash" "github.com/kercylan98/minotaur/utils/hash"
"github.com/kercylan98/minotaur/utils/slice" "github.com/kercylan98/minotaur/utils/slice"
"sync" "sync"
@ -12,7 +12,7 @@ import (
func NewRoomSeat[PlayerID comparable, Player game.Player[PlayerID]](room game.Room[PlayerID, Player], options ...RoomSeatOption[PlayerID, Player]) *RoomSeat[PlayerID, Player] { func NewRoomSeat[PlayerID comparable, Player game.Player[PlayerID]](room game.Room[PlayerID, Player], options ...RoomSeatOption[PlayerID, Player]) *RoomSeat[PlayerID, Player] {
roomSeat := &RoomSeat[PlayerID, Player]{ roomSeat := &RoomSeat[PlayerID, Player]{
Room: room, Room: room,
seatPS: asynchronous.NewMap[PlayerID, int](), seatPS: concurrent.NewBalanceMap[PlayerID, int](),
} }
for _, option := range options { for _, option := range options {
option(roomSeat) option(roomSeat)
@ -26,7 +26,7 @@ type RoomSeat[PlayerID comparable, Player game.Player[PlayerID]] struct {
game.Room[PlayerID, Player] game.Room[PlayerID, Player]
mutex sync.RWMutex mutex sync.RWMutex
vacancy []int vacancy []int
seatPS hash.Map[PlayerID, int] seatPS *concurrent.BalanceMap[PlayerID, int]
seatSP []*PlayerID seatSP []*PlayerID
duplicateLock bool duplicateLock bool
fillIn bool fillIn bool
@ -240,6 +240,41 @@ func (slf *RoomSeat[PlayerID, Player]) GetNextSeatVacancy(seat int) int {
return seat return seat
} }
// GetPrevSeat 获取特定座位号上一个未缺席的座位号
func (slf *RoomSeat[PlayerID, Player]) GetPrevSeat(seat int) int {
l := len(slf.seatSP)
if l == 0 || seat >= l || seat < 0 {
return -1
}
var target = seat
for {
target--
if target < 0 {
target = l - 1
}
if target == seat {
return seat
}
if slf.seatSP[target] != nil {
return target
}
}
}
// GetPrevSeatVacancy 获取特定座位号上一个座位号
// - 缺席将不会被忽略
func (slf *RoomSeat[PlayerID, Player]) GetPrevSeatVacancy(seat int) int {
l := len(slf.seatSP)
if l == 0 || seat >= l || seat < 0 {
return -1
}
seat--
if seat < 0 {
seat = l - 1
}
return seat
}
func (slf *RoomSeat[PlayerID, Player]) onJoinRoom(room game.Room[PlayerID, Player], player Player) { func (slf *RoomSeat[PlayerID, Player]) onJoinRoom(room game.Room[PlayerID, Player], player Player) {
slf.AddSeat(player.GetID()) slf.AddSeat(player.GetID())
} }

View File

@ -2,9 +2,8 @@ package builtin
import ( import (
"github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/hash" "github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/log"
"github.com/kercylan98/minotaur/utils/synchronization"
"sync/atomic" "sync/atomic"
) )
@ -12,10 +11,10 @@ import (
func NewWorld[PlayerID comparable, Player game.Player[PlayerID]](guid int64, options ...WorldOption[PlayerID, Player]) *World[PlayerID, Player] { func NewWorld[PlayerID comparable, Player game.Player[PlayerID]](guid int64, options ...WorldOption[PlayerID, Player]) *World[PlayerID, Player] {
world := &World[PlayerID, Player]{ world := &World[PlayerID, Player]{
guid: guid, guid: guid,
players: synchronization.NewMap[PlayerID, Player](), players: concurrent.NewBalanceMap[PlayerID, Player](),
playerActors: synchronization.NewMap[PlayerID, hash.Map[int64, game.Actor]](), playerActors: concurrent.NewBalanceMap[PlayerID, *concurrent.BalanceMap[int64, game.Actor]](),
owners: synchronization.NewMap[int64, PlayerID](), owners: concurrent.NewBalanceMap[int64, PlayerID](),
actors: synchronization.NewMap[int64, game.Actor](), actors: concurrent.NewBalanceMap[int64, game.Actor](),
} }
for _, option := range options { for _, option := range options {
option(world) option(world)
@ -28,10 +27,10 @@ type World[PlayerID comparable, Player game.Player[PlayerID]] struct {
guid int64 guid int64
actorGuid atomic.Int64 actorGuid atomic.Int64
playerLimit int playerLimit int
players hash.Map[PlayerID, Player] players *concurrent.BalanceMap[PlayerID, Player]
playerActors hash.Map[PlayerID, hash.Map[int64, game.Actor]] playerActors *concurrent.BalanceMap[PlayerID, *concurrent.BalanceMap[int64, game.Actor]]
owners hash.Map[int64, PlayerID] owners *concurrent.BalanceMap[int64, PlayerID]
actors hash.Map[int64, game.Actor] actors *concurrent.BalanceMap[int64, game.Actor]
playerJoinWorldEventHandles []game.PlayerJoinWorldEventHandle[PlayerID, Player] playerJoinWorldEventHandles []game.PlayerJoinWorldEventHandle[PlayerID, Player]
playerLeaveWorldEventHandles []game.PlayerLeaveWorldEventHandle[PlayerID, Player] playerLeaveWorldEventHandles []game.PlayerLeaveWorldEventHandle[PlayerID, Player]
@ -56,16 +55,16 @@ func (slf *World[PlayerID, Player]) GetPlayer(id PlayerID) Player {
return slf.players.Get(id) return slf.players.Get(id)
} }
func (slf *World[PlayerID, Player]) GetPlayers() hash.MapReadonly[PlayerID, Player] { func (slf *World[PlayerID, Player]) GetPlayers() map[PlayerID]Player {
return slf.players.(hash.MapReadonly[PlayerID, Player]) return slf.players.Map()
} }
func (slf *World[PlayerID, Player]) GetActor(guid int64) game.Actor { func (slf *World[PlayerID, Player]) GetActor(guid int64) game.Actor {
return slf.actors.Get(guid) return slf.actors.Get(guid)
} }
func (slf *World[PlayerID, Player]) GetActors() hash.MapReadonly[int64, game.Actor] { func (slf *World[PlayerID, Player]) GetActors() map[int64]game.Actor {
return slf.actors.(hash.MapReadonly[int64, game.Actor]) return slf.actors.Map()
} }
func (slf *World[PlayerID, Player]) GetPlayerActor(id PlayerID, guid int64) game.Actor { func (slf *World[PlayerID, Player]) GetPlayerActor(id PlayerID, guid int64) game.Actor {
@ -75,8 +74,8 @@ func (slf *World[PlayerID, Player]) GetPlayerActor(id PlayerID, guid int64) game
return nil return nil
} }
func (slf *World[PlayerID, Player]) GetPlayerActors(id PlayerID) hash.MapReadonly[int64, game.Actor] { func (slf *World[PlayerID, Player]) GetPlayerActors(id PlayerID) map[int64]game.Actor {
return slf.playerActors.Get(id).(hash.MapReadonly[int64, game.Actor]) return slf.playerActors.Get(id).Map()
} }
func (slf *World[PlayerID, Player]) IsExistPlayer(id PlayerID) bool { func (slf *World[PlayerID, Player]) IsExistPlayer(id PlayerID) bool {
@ -105,7 +104,7 @@ func (slf *World[PlayerID, Player]) Join(player Player) error {
log.Debug("World.Join", log.Int64("guid", slf.GetGuid()), log.Any("player", player.GetID())) log.Debug("World.Join", log.Int64("guid", slf.GetGuid()), log.Any("player", player.GetID()))
slf.players.Set(player.GetID(), player) slf.players.Set(player.GetID(), player)
if actors := slf.playerActors.Get(player.GetID()); actors == nil { if actors := slf.playerActors.Get(player.GetID()); actors == nil {
actors = synchronization.NewMap[int64, game.Actor]() actors = concurrent.NewBalanceMap[int64, game.Actor]()
slf.playerActors.Set(player.GetID(), actors) slf.playerActors.Set(player.GetID(), actors)
} }
slf.OnPlayerJoinWorldEvent(player) slf.OnPlayerJoinWorldEvent(player)
@ -119,9 +118,10 @@ func (slf *World[PlayerID, Player]) Leave(id PlayerID) {
} }
log.Debug("World.Leave", log.Int64("guid", slf.GetGuid()), log.Any("player", player.GetID())) log.Debug("World.Leave", log.Int64("guid", slf.GetGuid()), log.Any("player", player.GetID()))
slf.OnPlayerLeaveWorldEvent(player) slf.OnPlayerLeaveWorldEvent(player)
slf.playerActors.Get(player.GetID()).Range(func(guid int64, actor game.Actor) { slf.playerActors.Get(player.GetID()).Range(func(guid int64, actor game.Actor) bool {
slf.OnActorAnnihilationEvent(actor) slf.OnActorAnnihilationEvent(actor)
slf.owners.Delete(guid) slf.owners.Delete(guid)
return false
}) })
slf.playerActors.Delete(player.GetID()) slf.playerActors.Delete(player.GetID())
slf.players.Delete(player.GetID()) slf.players.Delete(player.GetID())
@ -171,8 +171,9 @@ func (slf *World[PlayerID, Player]) RemoveActorOwner(guid int64) {
func (slf *World[PlayerID, Player]) Reset() { func (slf *World[PlayerID, Player]) Reset() {
log.Debug("World", log.Int64("Reset", slf.guid)) log.Debug("World", log.Int64("Reset", slf.guid))
slf.players.Clear() slf.players.Clear()
slf.playerActors.Range(func(id PlayerID, actors hash.Map[int64, game.Actor]) { slf.playerActors.Range(func(id PlayerID, actors *concurrent.BalanceMap[int64, game.Actor]) bool {
actors.Clear() actors.Clear()
return false
}) })
slf.playerActors.Clear() slf.playerActors.Clear()
slf.owners.Clear() slf.owners.Clear()

View File

@ -68,3 +68,23 @@ func GetCardsColor(cards ...Card) []Color {
} }
return colors return colors
} }
// IsContain 一组扑克牌是否包含某张牌
func IsContain(cards []Card, card Card) bool {
for _, c := range cards {
if c.Equal(card) {
return true
}
}
return false
}
// IsContainAll 一组扑克牌是否包含另一组扑克牌
func IsContainAll(cards []Card, cards2 []Card) bool {
for _, card := range cards2 {
if !IsContain(cards, card) {
return false
}
}
return true
}

View File

@ -1,9 +1,5 @@
package game package game
import (
"github.com/kercylan98/minotaur/utils/hash"
)
// Room 房间类似于简版的游戏世界(World),不过没有游戏实体 // Room 房间类似于简版的游戏世界(World),不过没有游戏实体
type Room[PlayerID comparable, P Player[PlayerID]] interface { type Room[PlayerID comparable, P Player[PlayerID]] interface {
// GetGuid 获取房间的唯一标识符 // GetGuid 获取房间的唯一标识符
@ -13,7 +9,7 @@ type Room[PlayerID comparable, P Player[PlayerID]] interface {
// GetPlayer 根据玩家id获取玩家 // GetPlayer 根据玩家id获取玩家
GetPlayer(id PlayerID) P GetPlayer(id PlayerID) P
// GetPlayers 获取房间中的所有玩家 // GetPlayers 获取房间中的所有玩家
GetPlayers() hash.MapReadonly[PlayerID, P] GetPlayers() map[PlayerID]P
// GetPlayerCount 获取玩家数量 // GetPlayerCount 获取玩家数量
GetPlayerCount() int GetPlayerCount() int
// IsExistPlayer 检查房间中是否存在特定玩家 // IsExistPlayer 检查房间中是否存在特定玩家

View File

@ -36,4 +36,12 @@ type RoomSeat[PlayerID comparable, P Player[PlayerID]] interface {
// - 超出范围将返回-1 // - 超出范围将返回-1
// - 当没有下一个座位号时将始终返回本身 // - 当没有下一个座位号时将始终返回本身
GetNextSeatVacancy(seat int) int GetNextSeatVacancy(seat int) int
// GetPrevSeat 获取上一个座位号,空缺的位置将会被跳过
// - 超出范围将返回-1
// - 当没有上一个座位号时将始终返回本身
GetPrevSeat(seat int) int
// GetPrevSeatVacancy 获取上一个座位号,空缺的位置将被保留
// - 超出范围将返回-1
// - 当没有上一个座位号时将始终返回本身
GetPrevSeatVacancy(seat int) int
} }

View File

@ -1,9 +1,5 @@
package game package game
import (
"github.com/kercylan98/minotaur/utils/hash"
)
// World 游戏世界接口定义 // World 游戏世界接口定义
type World[PlayerID comparable, P Player[PlayerID]] interface { type World[PlayerID comparable, P Player[PlayerID]] interface {
// GetGuid 获取世界的唯一标识符 // GetGuid 获取世界的唯一标识符
@ -13,15 +9,15 @@ type World[PlayerID comparable, P Player[PlayerID]] interface {
// GetPlayer 根据玩家id获取玩家 // GetPlayer 根据玩家id获取玩家
GetPlayer(id PlayerID) P GetPlayer(id PlayerID) P
// GetPlayers 获取世界中的所有玩家 // GetPlayers 获取世界中的所有玩家
GetPlayers() hash.MapReadonly[PlayerID, P] GetPlayers() map[PlayerID]P
// GetActor 根据唯一标识符获取世界中的游戏对象 // GetActor 根据唯一标识符获取世界中的游戏对象
GetActor(guid int64) Actor GetActor(guid int64) Actor
// GetActors 获取世界中的所有游戏对象 // GetActors 获取世界中的所有游戏对象
GetActors() hash.MapReadonly[int64, Actor] GetActors() map[int64]Actor
// GetPlayerActor 获取游戏世界中归属特定玩家的特定游戏对象 // GetPlayerActor 获取游戏世界中归属特定玩家的特定游戏对象
GetPlayerActor(id PlayerID, guid int64) Actor GetPlayerActor(id PlayerID, guid int64) Actor
// GetPlayerActors 获取游戏世界中归属特定玩家的所有游戏对象 // GetPlayerActors 获取游戏世界中归属特定玩家的所有游戏对象
GetPlayerActors(id PlayerID) hash.MapReadonly[int64, Actor] GetPlayerActors(id PlayerID) map[int64]Actor
// IsExistPlayer 检查游戏世界中是否存在特定玩家 // IsExistPlayer 检查游戏世界中是否存在特定玩家
IsExistPlayer(id PlayerID) bool IsExistPlayer(id PlayerID) bool
// IsExistActor 检查游戏世界中是否存在特定游戏对象 // IsExistActor 检查游戏世界中是否存在特定游戏对象

2
go.mod
View File

@ -23,6 +23,7 @@ require (
) )
require ( require (
github.com/alphadose/haxmap v1.2.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect
@ -61,6 +62,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.9.0 // indirect golang.org/x/crypto v0.9.0 // indirect
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect

4
go.sum
View File

@ -2,6 +2,8 @@ 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/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 h1:iPugyBI7oFtbDZXC4dnY093M1kZx6k/95sen92gafbY=
github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108/go.mod h1:WAMLHwunr1hi3u7OjGV6/VWG9QbdMhGpEKjROiSFd10= github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108/go.mod h1:WAMLHwunr1hi3u7OjGV6/VWG9QbdMhGpEKjROiSFd10=
github.com/alphadose/haxmap v1.2.0 h1:noGrAmCE+gNheZ4KpW+sYj9W5uMcO1UAjbAq9XBOAfM=
github.com/alphadose/haxmap v1.2.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
@ -219,6 +221,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/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-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-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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=

View File

@ -2,7 +2,7 @@ package server
import ( import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/kercylan98/minotaur/utils/synchronization" "github.com/kercylan98/minotaur/utils/concurrent"
"github.com/panjf2000/gnet" "github.com/panjf2000/gnet"
"github.com/xtaci/kcp-go/v5" "github.com/xtaci/kcp-go/v5"
"net" "net"
@ -75,7 +75,7 @@ type Conn struct {
kcp *kcp.UDPSession kcp *kcp.UDPSession
data map[any]any data map[any]any
mutex sync.Mutex mutex sync.Mutex
packetPool *synchronization.Pool[*connPacket] packetPool *concurrent.Pool[*connPacket]
packets []*connPacket packets []*connPacket
} }
@ -187,7 +187,7 @@ func (slf *Conn) WriteWithCallback(packet Packet, callback func(err error), mess
// writeLoop 写循环 // writeLoop 写循环
func (slf *Conn) writeLoop(wait *sync.WaitGroup) { func (slf *Conn) writeLoop(wait *sync.WaitGroup) {
slf.packetPool = synchronization.NewPool[*connPacket](10*1024, slf.packetPool = concurrent.NewPool[*connPacket](10*1024,
func() *connPacket { func() *connPacket {
return &connPacket{} return &connPacket{}
}, func(data *connPacket) { }, func(data *connPacket) {

View File

@ -4,8 +4,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/kercylan98/minotaur/server" "github.com/kercylan98/minotaur/server"
"github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/log"
"github.com/kercylan98/minotaur/utils/synchronization"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
"time" "time"
) )
@ -18,7 +18,7 @@ func NewNats(url string, options ...NatsOption) *Nats {
n := &Nats{ n := &Nats{
url: url, url: url,
subject: "MINOTAUR_CROSS", subject: "MINOTAUR_CROSS",
messagePool: synchronization.NewPool[*Message](1024*100, func() *Message { messagePool: concurrent.NewPool[*Message](1024*100, func() *Message {
return new(Message) return new(Message)
}, func(data *Message) { }, func(data *Message) {
data.ServerId = 0 data.ServerId = 0
@ -36,7 +36,7 @@ type Nats struct {
url string url string
subject string subject string
options []nats.Option options []nats.Option
messagePool *synchronization.Pool[*Message] messagePool *concurrent.Pool[*Message]
} }
func (slf *Nats) Init(server *server.Server, packetHandle func(serverId int64, packet []byte)) (err error) { func (slf *Nats) Init(server *server.Server, packetHandle func(serverId int64, packet []byte)) (err error) {

View File

@ -5,9 +5,9 @@ import (
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/log"
"github.com/kercylan98/minotaur/utils/super" "github.com/kercylan98/minotaur/utils/super"
"github.com/kercylan98/minotaur/utils/synchronization"
"github.com/kercylan98/minotaur/utils/timer" "github.com/kercylan98/minotaur/utils/timer"
"github.com/kercylan98/minotaur/utils/times" "github.com/kercylan98/minotaur/utils/times"
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
@ -33,7 +33,7 @@ func New(network Network, options ...Option) *Server {
runtime: &runtime{messagePoolSize: DefaultMessageBufferSize, messageChannelSize: DefaultMessageChannelSize}, runtime: &runtime{messagePoolSize: DefaultMessageBufferSize, messageChannelSize: DefaultMessageChannelSize},
option: &option{}, option: &option{},
network: network, network: network,
online: synchronization.NewMap[string, *Conn](), online: concurrent.NewBalanceMap[string, *Conn](),
closeChannel: make(chan struct{}, 1), closeChannel: make(chan struct{}, 1),
systemSignal: make(chan os.Signal, 1), systemSignal: make(chan os.Signal, 1),
} }
@ -72,26 +72,26 @@ func New(network Network, options ...Option) *Server {
// Server 网络服务器 // Server 网络服务器
type Server struct { type Server struct {
*event // 事件 *event // 事件
*runtime // 运行时 *runtime // 运行时
*option // 可选项 *option // 可选项
network Network // 网络类型 network Network // 网络类型
addr string // 侦听地址 addr string // 侦听地址
systemSignal chan os.Signal // 系统信号 systemSignal chan os.Signal // 系统信号
online *synchronization.Map[string, *Conn] // 在线连接 online *concurrent.BalanceMap[string, *Conn] // 在线连接
ginServer *gin.Engine // HTTP模式下的路由器 ginServer *gin.Engine // HTTP模式下的路由器
httpServer *http.Server // HTTP模式下的服务器 httpServer *http.Server // HTTP模式下的服务器
grpcServer *grpc.Server // GRPC模式下的服务器 grpcServer *grpc.Server // GRPC模式下的服务器
gServer *gNet // TCP或UDP模式下的服务器 gServer *gNet // TCP或UDP模式下的服务器
isRunning bool // 是否正在运行 isRunning bool // 是否正在运行
isShutdown atomic.Bool // 是否已关闭 isShutdown atomic.Bool // 是否已关闭
closeChannel chan struct{} // 关闭信号 closeChannel chan struct{} // 关闭信号
ants *ants.Pool // 协程池 ants *ants.Pool // 协程池
messagePool *synchronization.Pool[*Message] // 消息池 messagePool *concurrent.Pool[*Message] // 消息池
messageChannel chan *Message // 消息管道 messageChannel chan *Message // 消息管道
multiple *MultipleServer // 多服务器模式下的服务器 multiple *MultipleServer // 多服务器模式下的服务器
multipleRuntimeErrorChan chan error // 多服务器模式下的运行时错误 multipleRuntimeErrorChan chan error // 多服务器模式下的运行时错误
runMode RunMode // 运行模式 runMode RunMode // 运行模式
} }
// Run 使用特定地址运行服务器 // Run 使用特定地址运行服务器
@ -115,7 +115,7 @@ func (slf *Server) Run(addr string) error {
var protoAddr = fmt.Sprintf("%s://%s", slf.network, slf.addr) var protoAddr = fmt.Sprintf("%s://%s", slf.network, slf.addr)
var messageInitFinish = make(chan struct{}, 1) var messageInitFinish = make(chan struct{}, 1)
var connectionInitHandle = func(callback func()) { var connectionInitHandle = func(callback func()) {
slf.messagePool = synchronization.NewPool[*Message](slf.messagePoolSize, slf.messagePool = concurrent.NewPool[*Message](slf.messagePoolSize,
func() *Message { func() *Message {
return &Message{} return &Message{}
}, },

View File

@ -1,195 +0,0 @@
package asynchronous
import (
"encoding/json"
"github.com/kercylan98/minotaur/utils/hash"
)
func NewMap[Key comparable, Value any](options ...MapOption[Key, Value]) *Map[Key, Value] {
m := &Map[Key, Value]{
data: make(map[Key]Value),
}
for _, option := range options {
option(m)
}
return m
}
// Map 非并发安全的字典数据结构
// - 可用于对 synchronization.Map 的替代
type Map[Key comparable, Value any] struct {
data map[Key]Value
}
func (slf *Map[Key, Value]) Set(key Key, value Value) {
slf.data[key] = value
}
func (slf *Map[Key, Value]) Get(key Key) Value {
return slf.data[key]
}
// AtomGetSet 原子方式获取一个值并在之后进行赋值
func (slf *Map[Key, Value]) AtomGetSet(key Key, handle func(value Value, exist bool) (newValue Value, isSet bool)) {
value, exist := slf.data[key]
if newValue, isSet := handle(value, exist); isSet {
slf.data[key] = newValue
}
}
// Atom 原子操作
func (slf *Map[Key, Value]) Atom(handle func(m hash.Map[Key, Value])) {
handle(slf)
}
func (slf *Map[Key, Value]) Exist(key Key) bool {
_, exist := slf.data[key]
return exist
}
func (slf *Map[Key, Value]) GetExist(key Key) (Value, bool) {
value, exist := slf.data[key]
return value, exist
}
func (slf *Map[Key, Value]) Delete(key Key) {
delete(slf.data, key)
}
func (slf *Map[Key, Value]) DeleteGet(key Key) Value {
v := slf.data[key]
delete(slf.data, key)
return v
}
func (slf *Map[Key, Value]) DeleteGetExist(key Key) (Value, bool) {
v, exist := slf.data[key]
delete(slf.data, key)
return v, exist
}
func (slf *Map[Key, Value]) DeleteExist(key Key) bool {
if _, exist := slf.data[key]; !exist {
return exist
}
delete(slf.data, key)
return true
}
func (slf *Map[Key, Value]) Clear() {
for k := range slf.data {
delete(slf.data, k)
}
}
func (slf *Map[Key, Value]) ClearHandle(handle func(key Key, value Value)) {
for k, v := range slf.data {
handle(k, v)
delete(slf.data, k)
}
}
func (slf *Map[Key, Value]) Range(handle func(key Key, value Value)) {
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) {
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.rangeBreakout(handle)
}
func (slf *Map[Key, Value]) rangeBreakout(handle func(key Key, value Value) bool) bool {
for k, v := range slf.data {
key, value := k, v
if !handle(key, value) {
return true
}
}
return false
}
func (slf *Map[Key, Value]) RangeFree(handle func(key Key, value Value, skip func(), breakout func())) {
slf.rangeFree(handle)
}
func (slf *Map[Key, Value]) rangeFree(handle func(key Key, value Value, skip func(), breakout func())) bool {
var skipExec, breakoutExec bool
var skip = func() {
skipExec = true
}
var breakout = func() {
breakoutExec = true
}
for k, v := range slf.data {
key, value := k, v
handle(key, value, skip, breakout)
if skipExec {
continue
}
if breakoutExec {
break
}
}
return breakoutExec
}
func (slf *Map[Key, Value]) Keys() []Key {
var s = make([]Key, 0, len(slf.data))
for k := range slf.data {
s = append(s, k)
}
return s
}
func (slf *Map[Key, Value]) Slice() []Value {
var s = make([]Value, 0, len(slf.data))
for _, v := range slf.data {
s = append(s, v)
}
return s
}
func (slf *Map[Key, Value]) Map() map[Key]Value {
var m = make(map[Key]Value)
for k, v := range slf.data {
m[k] = v
}
return m
}
func (slf *Map[Key, Value]) Size() int {
return len(slf.data)
}
// GetOne 获取一个
func (slf *Map[Key, Value]) GetOne() (value Value) {
for _, v := range slf.data {
return v
}
return value
}
func (slf *Map[Key, Value]) MarshalJSON() ([]byte, error) {
m := slf.Map()
return json.Marshal(m)
}
func (slf *Map[Key, Value]) UnmarshalJSON(bytes []byte) error {
var m = make(map[Key]Value)
if err := json.Unmarshal(bytes, &m); err != nil {
return err
}
slf.data = m
return nil
}

View File

@ -1,10 +0,0 @@
package asynchronous
type MapOption[Key comparable, Value any] func(m *Map[Key, Value])
// WithMapSource 通过传入的 map 初始化
func WithMapSource[Key comparable, Value any](source map[Key]Value) MapOption[Key, Value] {
return func(m *Map[Key, Value]) {
m.data = source
}
}

View File

@ -0,0 +1,185 @@
package concurrent
import (
"encoding/json"
"sync"
)
// NewBalanceMap 创建一个并发安全且性能在普通读写和并发读写的情况下较为平衡的字典数据结构
func NewBalanceMap[Key comparable, value any](options ...BalanceMapOption[Key, value]) *BalanceMap[Key, value] {
m := &BalanceMap[Key, value]{
data: make(map[Key]value),
}
for _, option := range options {
option(m)
}
return m
}
// BalanceMap 并发安全且性能在普通读写和并发读写的情况下较为平衡的字典数据结构
// - 适用于要考虑并发读写但是并发读写的频率不高的情况
type BalanceMap[Key comparable, Value any] struct {
lock sync.RWMutex
data map[Key]Value
}
// Set 设置一个值
func (slf *BalanceMap[Key, Value]) Set(key Key, value Value) {
slf.lock.Lock()
defer slf.lock.Unlock()
slf.data[key] = value
}
// Get 获取一个值
func (slf *BalanceMap[Key, Value]) Get(key Key) Value {
slf.lock.RLock()
defer slf.lock.RUnlock()
return slf.data[key]
}
// Atom 原子操作
func (slf *BalanceMap[Key, Value]) Atom(handle func(m map[Key]Value)) {
slf.lock.Lock()
handle(slf.data)
slf.lock.Unlock()
}
// Exist 判断是否存在
func (slf *BalanceMap[Key, Value]) Exist(key Key) bool {
slf.lock.RLock()
_, exist := slf.data[key]
slf.lock.RUnlock()
return exist
}
// GetExist 获取一个值并判断是否存在
func (slf *BalanceMap[Key, Value]) GetExist(key Key) (Value, bool) {
slf.lock.RLock()
value, exist := slf.data[key]
slf.lock.RUnlock()
return value, exist
}
// Delete 删除一个值
func (slf *BalanceMap[Key, Value]) Delete(key Key) {
slf.lock.Lock()
delete(slf.data, key)
defer slf.lock.Unlock()
}
// DeleteGet 删除一个值并返回
func (slf *BalanceMap[Key, Value]) DeleteGet(key Key) Value {
slf.lock.Lock()
v := slf.data[key]
delete(slf.data, key)
slf.lock.Unlock()
return v
}
// DeleteGetExist 删除一个值并返回是否存在
func (slf *BalanceMap[Key, Value]) DeleteGetExist(key Key) (Value, bool) {
slf.lock.Lock()
v, exist := slf.data[key]
delete(slf.data, key)
defer slf.lock.Unlock()
return v, exist
}
// DeleteExist 删除一个值并返回是否存在
func (slf *BalanceMap[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
}
// Clear 清空
func (slf *BalanceMap[Key, Value]) Clear() {
slf.lock.Lock()
for k := range slf.data {
delete(slf.data, k)
}
slf.lock.Unlock()
}
// ClearHandle 清空并处理
func (slf *BalanceMap[Key, Value]) ClearHandle(handle func(key Key, value Value)) {
slf.lock.Lock()
for k, v := range slf.data {
handle(k, v)
delete(slf.data, k)
}
slf.lock.Unlock()
}
// Range 遍历所有值,如果 handle 返回 true 则停止遍历
func (slf *BalanceMap[Key, Value]) Range(handle func(key Key, value Value) bool) {
slf.lock.Lock()
for k, v := range slf.data {
key, value := k, v
if handle(key, value) {
break
}
}
slf.lock.Unlock()
}
// Keys 获取所有的键
func (slf *BalanceMap[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
}
// Slice 获取所有的值
func (slf *BalanceMap[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
}
// Map 转换为普通 map
func (slf *BalanceMap[Key, Value]) Map() map[Key]Value {
slf.lock.RLock()
var m = make(map[Key]Value)
for k, v := range slf.data {
m[k] = v
}
slf.lock.RUnlock()
return m
}
// Size 获取数量
func (slf *BalanceMap[Key, Value]) Size() int {
slf.lock.RLock()
defer slf.lock.RUnlock()
return len(slf.data)
}
func (slf *BalanceMap[Key, Value]) MarshalJSON() ([]byte, error) {
m := slf.Map()
return json.Marshal(m)
}
func (slf *BalanceMap[Key, Value]) UnmarshalJSON(bytes []byte) error {
var m = make(map[Key]Value)
if err := json.Unmarshal(bytes, &m); err != nil {
return err
}
slf.lock.Lock()
slf.data = m
slf.lock.Unlock()
return nil
}

View File

@ -0,0 +1,11 @@
package concurrent
// BalanceMapOption BalanceMap 的选项
type BalanceMapOption[Key comparable, Value any] func(m *BalanceMap[Key, Value])
// WithBalanceMapSource 通过传入的 map 初始化
func WithBalanceMapSource[Key comparable, Value any](source map[Key]Value) BalanceMapOption[Key, Value] {
return func(m *BalanceMap[Key, Value]) {
m.data = source
}
}

View File

@ -1,4 +1,4 @@
package synchronization package concurrent
import ( import (
"github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/log"

View File

@ -1,4 +1,4 @@
package synchronization package concurrent
import ( import (
"github.com/kercylan98/minotaur/utils/slice" "github.com/kercylan98/minotaur/utils/slice"

View File

@ -1,4 +1,4 @@
package synchronization package concurrent
type SliceOption[T any] func(slice *Slice[T]) type SliceOption[T any] func(slice *Slice[T])

View File

@ -1,29 +0,0 @@
package hash
// Map 提供了map集合接口
type Map[Key comparable, Value any] interface {
Set(key Key, value Value)
Get(key Key) Value
// AtomGetSet 原子方式获取一个值并在之后进行赋值
AtomGetSet(key Key, handle func(value Value, exist bool) (newValue Value, isSet bool))
// Atom 原子操作
Atom(handle func(m Map[Key, Value]))
Exist(key Key) bool
GetExist(key Key) (Value, bool)
Delete(key Key)
DeleteGet(key Key) Value
DeleteGetExist(key Key) (Value, bool)
DeleteExist(key Key) bool
Clear()
ClearHandle(handle func(key Key, value Value))
Range(handle func(key Key, value Value))
RangeSkip(handle func(key Key, value Value) bool)
RangeBreakout(handle func(key Key, value Value) bool)
RangeFree(handle func(key Key, value Value, skip func(), breakout func()))
Keys() []Key
Slice() []Value
Map() map[Key]Value
Size() int
// GetOne 获取一个
GetOne() (value Value)
}

View File

@ -1,17 +0,0 @@
package hash
// MapReadonly 只读字典接口
type MapReadonly[Key comparable, Value any] interface {
Get(key Key) Value
Exist(key Key) bool
GetExist(key Key) (Value, bool)
Range(handle func(key Key, value Value))
RangeSkip(handle func(key Key, value Value) bool)
RangeBreakout(handle func(key Key, value Value) bool)
RangeFree(handle func(key Key, value Value, skip func(), breakout func()))
Keys() []Key
Slice() []Value
Map() map[Key]Value
Size() int
GetOne() (value Value)
}

View File

@ -1,9 +1,8 @@
package stream package stream
import ( import (
"github.com/kercylan98/minotaur/utils/asynchronous" "github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/hash" "github.com/kercylan98/minotaur/utils/hash"
"github.com/kercylan98/minotaur/utils/synchronization"
"reflect" "reflect"
) )
@ -19,11 +18,6 @@ func WithMapCopy[K comparable, V any](m map[K]V) Map[K, V] {
return hash.Copy(m) return hash.Copy(m)
} }
// WithHashMap 使用传入的 map 执行链式操作
func WithHashMap[K comparable, V any](m hash.Map[K, V]) Map[K, V] {
return m.Map()
}
// Map 提供了 map 的链式操作 // Map 提供了 map 的链式操作
type Map[K comparable, V any] map[K]V type Map[K comparable, V any] map[K]V
@ -180,14 +174,9 @@ func (slf Map[K, V]) ToSliceStreamWithKey() Slice[K] {
return hash.KeyToSlice(slf) return hash.KeyToSlice(slf)
} }
// ToSyncMap 将当前 Map 转换为 synchronization.Map // ToSyncMap 将当前 Map 转换为 concurrent.BalanceMap
func (slf Map[K, V]) ToSyncMap() *synchronization.Map[K, V] { func (slf Map[K, V]) ToSyncMap() *concurrent.BalanceMap[K, V] {
return synchronization.NewMap[K, V](synchronization.WithMapSource(slf)) return concurrent.NewBalanceMap[K, V](concurrent.WithBalanceMapSource(slf))
}
// ToAsyncMap 将当前 Map 转换为 asynchronous.Map
func (slf Map[K, V]) ToAsyncMap() *asynchronous.Map[K, V] {
return asynchronous.NewMap[K, V](asynchronous.WithMapSource(slf))
} }
// ToMap 将当前 Map 转换为 map // ToMap 将当前 Map 转换为 map

View File

@ -1,285 +0,0 @@
package synchronization
import (
"encoding/json"
"github.com/kercylan98/minotaur/utils/hash"
"sync"
)
func NewMap[Key comparable, value any](options ...MapOption[Key, value]) *Map[Key, value] {
m := &Map[Key, value]{
data: make(map[Key]value),
}
for _, option := range options {
option(m)
}
return m
}
// Map 并发安全的字典数据结构
type Map[Key comparable, Value any] struct {
lock sync.RWMutex
data map[Key]Value
atom bool
}
func (slf *Map[Key, Value]) Set(key Key, value Value) {
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
slf.data[key] = value
}
func (slf *Map[Key, Value]) Get(key Key) Value {
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
return slf.data[key]
}
// AtomGetSet 原子方式获取一个值并在之后进行赋值
func (slf *Map[Key, Value]) AtomGetSet(key Key, handle func(value Value, exist bool) (newValue Value, isSet bool)) {
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
value, exist := slf.data[key]
if newValue, isSet := handle(value, exist); isSet {
slf.data[key] = newValue
}
}
// Atom 原子操作
func (slf *Map[Key, Value]) Atom(handle func(m hash.Map[Key, Value])) {
slf.lock.Lock()
slf.atom = true
handle(slf)
slf.atom = false
slf.lock.Unlock()
}
func (slf *Map[Key, Value]) Exist(key Key) bool {
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
_, exist := slf.data[key]
return exist
}
func (slf *Map[Key, Value]) GetExist(key Key) (Value, bool) {
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
value, exist := slf.data[key]
return value, exist
}
func (slf *Map[Key, Value]) Delete(key Key) {
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
delete(slf.data, key)
}
func (slf *Map[Key, Value]) DeleteGet(key Key) Value {
if !slf.atom {
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) {
if !slf.atom {
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 {
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
if _, exist := slf.data[key]; !exist {
return exist
}
delete(slf.data, key)
return true
}
func (slf *Map[Key, Value]) Clear() {
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
for k := range slf.data {
delete(slf.data, k)
}
}
func (slf *Map[Key, Value]) ClearHandle(handle func(key Key, value Value)) {
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
for k, v := range slf.data {
handle(k, v)
delete(slf.data, k)
}
}
func (slf *Map[Key, Value]) Range(handle func(key Key, value Value)) {
if !slf.atom {
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) {
if !slf.atom {
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.rangeBreakout(handle)
}
func (slf *Map[Key, Value]) rangeBreakout(handle func(key Key, value Value) bool) bool {
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
for k, v := range slf.data {
key, value := k, v
if !handle(key, value) {
return true
}
}
return false
}
func (slf *Map[Key, Value]) RangeFree(handle func(key Key, value Value, skip func(), breakout func())) {
slf.rangeFree(handle)
}
func (slf *Map[Key, Value]) rangeFree(handle func(key Key, value Value, skip func(), breakout func())) bool {
var skipExec, breakoutExec bool
var skip = func() {
skipExec = true
}
var breakout = func() {
breakoutExec = true
}
if !slf.atom {
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
}
}
return breakoutExec
}
func (slf *Map[Key, Value]) Keys() []Key {
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
var s = make([]Key, 0, len(slf.data))
for k := range slf.data {
s = append(s, k)
}
return s
}
func (slf *Map[Key, Value]) Slice() []Value {
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
var s = make([]Value, 0, len(slf.data))
for _, v := range slf.data {
s = append(s, v)
}
return s
}
func (slf *Map[Key, Value]) Map() map[Key]Value {
var m = make(map[Key]Value)
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
for k, v := range slf.data {
m[k] = v
}
return m
}
func (slf *Map[Key, Value]) Size() int {
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
return len(slf.data)
}
// GetOne 获取一个
func (slf *Map[Key, Value]) GetOne() (value Value) {
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
for _, v := range slf.data {
return v
}
return value
}
func (slf *Map[Key, Value]) MarshalJSON() ([]byte, error) {
m := slf.Map()
return json.Marshal(m)
}
func (slf *Map[Key, Value]) UnmarshalJSON(bytes []byte) error {
var m = make(map[Key]Value)
if err := json.Unmarshal(bytes, &m); err != nil {
return err
}
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
slf.data = m
return nil
}

View File

@ -1,10 +0,0 @@
package synchronization
type MapOption[Key comparable, Value any] func(m *Map[Key, Value])
// WithMapSource 通过传入的 map 初始化
func WithMapSource[Key comparable, Value any](source map[Key]Value) MapOption[Key, Value] {
return func(m *Map[Key, Value]) {
m.data = source
}
}

View File

@ -1,259 +0,0 @@
package synchronization
import (
"encoding/json"
"github.com/kercylan98/minotaur/utils/hash"
"sync"
)
func NewMapSegment[Key comparable, value any](segmentCount int) *MapSegment[Key, value] {
ms := &MapSegment[Key, value]{
segments: map[int]*Map[Key, value]{},
cache: map[Key]int{},
consistency: hash.NewConsistency(segmentCount),
}
for i := 0; i < segmentCount; i++ {
ms.consistency.AddNode(i)
ms.segments[i] = NewMap[Key, value]()
}
return ms
}
// MapSegment 基于分段锁实现的并发安全的字典数据结构map
type MapSegment[Key comparable, Value any] struct {
segments map[int]*Map[Key, Value]
cache map[Key]int
consistency *hash.Consistency
lock sync.RWMutex
}
func (slf *MapSegment[Key, Value]) Atom(handle func(m hash.Map[Key, Value])) {
panic("this function is currently not supported")
}
func (slf *MapSegment[Key, Value]) Set(key Key, value Value) {
slf.lock.RLock()
s, exist := slf.cache[key]
slf.lock.RUnlock()
if !exist {
s = slf.consistency.PickNode(key)
slf.lock.Lock()
slf.cache[key] = s
slf.lock.Unlock()
}
slf.segments[s].Set(key, value)
}
func (slf *MapSegment[Key, Value]) Get(key Key) (value Value) {
slf.lock.RLock()
s, exist := slf.cache[key]
slf.lock.RUnlock()
if !exist {
return value
}
return slf.segments[s].Get(key)
}
// AtomGetSet 原子方式获取一个值并在之后进行赋值
func (slf *MapSegment[Key, Value]) AtomGetSet(key Key, handle func(value Value, exist bool) (newValue Value, isSet bool)) {
var value Value
slf.lock.RLock()
s, exist := slf.cache[key]
slf.lock.RUnlock()
if !exist {
if newValue, isSet := handle(value, exist); isSet {
slf.Set(key, newValue)
}
return
}
slf.segments[s].AtomGetSet(key, handle)
}
func (slf *MapSegment[Key, Value]) Exist(key Key) bool {
slf.lock.RLock()
_, exist := slf.cache[key]
slf.lock.RUnlock()
return exist
}
func (slf *MapSegment[Key, Value]) GetExist(key Key) (value Value, exist bool) {
slf.lock.RLock()
s, exist := slf.cache[key]
slf.lock.RUnlock()
if !exist {
return value, false
}
return slf.segments[s].GetExist(key)
}
func (slf *MapSegment[Key, Value]) Delete(key Key) {
slf.lock.Lock()
s, exist := slf.cache[key]
delete(slf.cache, key)
slf.lock.Unlock()
if exist {
slf.segments[s].Delete(key)
}
}
func (slf *MapSegment[Key, Value]) DeleteGet(key Key) (value Value) {
slf.lock.Lock()
s, exist := slf.cache[key]
delete(slf.cache, key)
slf.lock.Unlock()
if exist {
return slf.segments[s].DeleteGet(key)
}
return
}
func (slf *MapSegment[Key, Value]) DeleteGetExist(key Key) (value Value, exist bool) {
slf.lock.Lock()
s, exist := slf.cache[key]
delete(slf.cache, key)
slf.lock.Unlock()
if exist {
return slf.segments[s].DeleteGetExist(key)
}
return value, exist
}
func (slf *MapSegment[Key, Value]) DeleteExist(key Key) bool {
slf.lock.Lock()
s, exist := slf.cache[key]
delete(slf.cache, key)
slf.lock.Unlock()
if exist {
return slf.segments[s].DeleteExist(key)
}
return false
}
func (slf *MapSegment[Key, Value]) Clear() {
slf.lock.Lock()
for k := range slf.cache {
delete(slf.cache, k)
}
for _, m := range slf.segments {
m.Clear()
}
slf.lock.Unlock()
}
func (slf *MapSegment[Key, Value]) ClearHandle(handle func(key Key, value Value)) {
slf.lock.Lock()
for k := range slf.cache {
delete(slf.cache, k)
}
for _, m := range slf.segments {
m.ClearHandle(handle)
}
slf.lock.Unlock()
}
func (slf *MapSegment[Key, Value]) Range(handle func(key Key, value Value)) {
for _, m := range slf.segments {
m.Range(handle)
}
}
func (slf *MapSegment[Key, Value]) RangeSkip(handle func(key Key, value Value) bool) {
for _, m := range slf.segments {
m.RangeSkip(handle)
}
}
func (slf *MapSegment[Key, Value]) RangeBreakout(handle func(key Key, value Value) bool) {
for _, m := range slf.segments {
if m.rangeBreakout(handle) {
break
}
}
}
func (slf *MapSegment[Key, Value]) RangeFree(handle func(key Key, value Value, skip func(), breakout func())) {
for _, m := range slf.segments {
if m.rangeFree(handle) {
break
}
}
}
func (slf *MapSegment[Key, Value]) Keys() []Key {
var s = make([]Key, 0, len(slf.cache))
slf.lock.RLock()
for k := range slf.cache {
s = append(s, k)
}
defer slf.lock.RUnlock()
return s
}
func (slf *MapSegment[Key, Value]) Slice() []Value {
slf.lock.RLock()
var s = make([]Value, 0, len(slf.cache))
slf.lock.RUnlock()
for _, m := range slf.segments {
s = append(s, m.Slice()...)
}
return s
}
func (slf *MapSegment[Key, Value]) Map() map[Key]Value {
slf.lock.RLock()
var s = map[Key]Value{}
slf.lock.RUnlock()
for _, m := range slf.segments {
for k, v := range m.Map() {
s[k] = v
}
}
return s
}
func (slf *MapSegment[Key, Value]) Size() int {
slf.lock.RLock()
defer slf.lock.RUnlock()
return len(slf.cache)
}
// GetOne 获取一个
func (slf *MapSegment[Key, Value]) GetOne() (value Value) {
for k, s := range slf.cache {
return slf.segments[s].Get(k)
}
return value
}
func (slf *MapSegment[Key, Value]) MarshalJSON() ([]byte, error) {
var ms struct {
Segments map[int]*Map[Key, Value]
Cache map[Key]int
SegmentCount int
}
ms.Segments = slf.segments
ms.Cache = slf.cache
ms.SegmentCount = len(slf.segments)
return json.Marshal(ms)
}
func (slf *MapSegment[Key, Value]) UnmarshalJSON(bytes []byte) error {
var ms struct {
Segments map[int]*Map[Key, Value]
Cache map[Key]int
SegmentCount int
}
if err := json.Unmarshal(bytes, &ms); err != nil {
return err
}
slf.lock.Lock()
slf.consistency = hash.NewConsistency(ms.SegmentCount)
for i := 0; i < ms.SegmentCount; i++ {
slf.consistency.AddNode(i)
}
slf.segments = ms.Segments
slf.cache = ms.Cache
slf.lock.Unlock()
return nil
}