feat(space): 替换并发安全map实现以提升性能

将原来基于`utils.ConcurrentMap`的玩家存储结构替换为
`github.com/mhmtszr/concurrent-swiss-map`提供的`CsMap`,
以获得更高效的并发读写能力。

同时修改了相关API调用方式:
- `Set` 改为 `Store`
- `Remove` 改为 `Delete`
- `IterCb` 改为 `Range`,并支持提前终止迭代
- `Items()` 不再使用

此外,调整了部分业务逻辑中对玩家列表遍历的方式,
确保在发送网络包后及时跳出循环,避免不必要的操作。

新增战斗类型处理函数`PET_King`用于处理宠物王相关的
战斗请求,并修复了`PET_MELEE`方法中的逻辑问题。

更新了go.mod和go.sum引入新的依赖库。
```
This commit is contained in:
2025-11-15 15:22:58 +08:00
parent f919047ff6
commit 6979b7018d
19 changed files with 117 additions and 48 deletions

View File

@@ -281,7 +281,7 @@ func (m ConcurrentMap[K, V]) Items() map[K]V {
// maps. RLock is held for all calls for a given shard // maps. RLock is held for all calls for a given shard
// therefore callback sess consistent view of a shard, // therefore callback sess consistent view of a shard,
// but not across the shards // but not across the shards
type IterCb[K comparable, V any] func(key K, v V) type IterCb[K comparable, V any] func(key K, v V) bool
// Callback based iterator, cheapest way to read // Callback based iterator, cheapest way to read
// all elements in a map. // all elements in a map.
@@ -290,7 +290,10 @@ func (m ConcurrentMap[K, V]) IterCb(fn IterCb[K, V]) {
shard := (m.shards)[idx] shard := (m.shards)[idx]
shard.RLock() shard.RLock()
for key, value := range shard.items { for key, value := range shard.items {
fn(key, value)
if !fn(key, value) {
break
}
} }
shard.RUnlock() shard.RUnlock()
} }

View File

@@ -30,8 +30,9 @@ func (h *Controller) ChangePlayerName(data *user.ChangePlayerNameInboundInfo, c
UserID: c.Info.UserID, UserID: c.Info.UserID,
} }
space.GetSpace(c.Info.MapID).User.IterCb(func(playerID uint32, player common.PlayerI) { space.GetSpace(c.Info.MapID).User.Range(func(playerID uint32, player common.PlayerI) bool {
player.SendPack(data.Head.Pack(&result)) player.SendPack(data.Head.Pack(&result))
return false
}) })
return result, 0 return result, 0

View File

@@ -17,9 +17,37 @@ func (h Controller) PET_MELEE(data *fight.StartPetWarInboundInfo, c *player.Play
c.PVPinfo = &info.PVPinfo{ c.PVPinfo = &info.PVPinfo{
Mode: info.BattleMode.PET_MELEE, Mode: info.BattleMode.PET_MELEE,
Status: info.BattleStatus.FIGHT_WITH_PLAYER} Status: info.BattleStatus.FIGHT_WITH_PLAYER}
g := c.PET_MELEE() g := c.Pet_joinFight()
if g != nil { if g != nil {
fight.NewFight(info.BattleMode.PET_MELEE, info.BattleStatus.FIGHT_WITH_PLAYER, c.PET_MELEE(), c) ///开始对战,房主方以及被邀请方 fight.NewFight(info.BattleMode.PET_MELEE, info.BattleStatus.FIGHT_WITH_PLAYER, g, c) ///开始对战,房主方以及被邀请方
}
return
}
func (h Controller) PET_King(data *fight.PetKingJoinInboundInfo, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
if !c.CanFight() {
return nil, errorcode.ErrorCodes.ErrPokemonNotEligible
}
c.PVPinfo = &info.PVPinfo{
Status: info.BattleStatus.FIGHT_WITH_PLAYER}
switch data.Type {
case 5:
c.PVPinfo.Mode = info.BattleMode.SINGLE_MODE
case 6:
c.PVPinfo.Mode = info.BattleMode.MULTI_MODE
}
g := c.Pet_joinFight()
if g != nil {
switch data.Type {
case 5:
fight.NewFight(info.BattleMode.SINGLE_MODE, info.BattleStatus.FIGHT_WITH_PLAYER, g, c) ///开始对战,房主方以及被邀请方
case 6:
fight.NewFight(info.BattleMode.MULTI_MODE, info.BattleStatus.FIGHT_WITH_PLAYER, g, c) ///开始对战,房主方以及被邀请方
}
} }
return return

View File

@@ -76,10 +76,11 @@ func (h Controller) ChangePlayerCloth(data *item.ChangePlayerClothInboundInfo, c
result.ClothList = append(result.ClothList, model.PeopleItemInfo{ID: v, Level: 1}) result.ClothList = append(result.ClothList, model.PeopleItemInfo{ID: v, Level: 1})
} }
c.Info.Clothes = result.ClothList c.Info.Clothes = result.ClothList
space.GetSpace(c.Info.MapID).User.IterCb(func(playerID uint32, player common.PlayerI) { space.GetSpace(c.Info.MapID).User.Range(func(playerID uint32, player common.PlayerI) bool {
// fmt.Println("ChangePlayerCloth", playerID, data.Head.Pack(result)) // fmt.Println("ChangePlayerCloth", playerID, data.Head.Pack(result))
data.Head.Result = 0 data.Head.Result = 0
player.SendPack(data.Head.Pack(result)) player.SendPack(data.Head.Pack(result))
return false
}) })
return nil, -1 return nil, -1

View File

@@ -106,11 +106,11 @@ func (h *Controller) Login(data *user.MAIN_LOGIN_IN, c gnet.Conn) (result *user.
//copier.Copy(t.Info, tt) //copier.Copy(t.Info, tt)
t1 := player.NewTomeeHeader(2001, t.Info.UserID) t1 := player.NewTomeeHeader(2001, t.Info.UserID)
space.GetSpace(t.Info.MapID).User.IterCb(func(playerID uint32, player common.PlayerI) { space.GetSpace(t.Info.MapID).User.Range(func(playerID uint32, player common.PlayerI) bool {
player.SendPack(t1.Pack(tt)) player.SendPack(t1.Pack(tt))
return false
}) })
space.GetSpace(t.Info.MapID).User.Set(t.Info.UserID, t) space.GetSpace(t.Info.MapID).User.Store(t.Info.UserID, t)
}() }()
return result, 0 return result, 0

View File

@@ -14,8 +14,8 @@ import (
func (h *Controller) MapEnter(data *maps.InInfo, c *player.Player) (result *maps.OutInfo, err errorcode.ErrorCode) { //这个时候player应该是空的 func (h *Controller) MapEnter(data *maps.InInfo, c *player.Player) (result *maps.OutInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
c.Info.MapID = data.MapId //登录地图 c.Info.MapID = data.MapId //登录地图
space.GetSpace(c.Info.MapID).User.Set(c.Info.UserID, c) //添加玩家 space.GetSpace(c.Info.MapID).User.Store(c.Info.UserID, c) //添加玩家
result = maps.NewOutInfo() result = maps.NewOutInfo()
c.Info.Pos = data.Point c.Info.Pos = data.Point
@@ -39,7 +39,7 @@ func (h *Controller) MapLeave(data *maps.LeaveMapInboundInfo, c *player.Player)
c.Canmon = false c.Canmon = false
c.Changemap = true //可以刷怪 c.Changemap = true //可以刷怪
data.Broadcast(c.Info.MapID, space.LeaveMapOutboundInfo{UserID: c.Info.UserID}) //同步广播 data.Broadcast(c.Info.MapID, space.LeaveMapOutboundInfo{UserID: c.Info.UserID}) //同步广播
space.GetSpace(c.Info.MapID).User.Remove(c.Info.UserID) space.GetSpace(c.Info.MapID).User.Delete(c.Info.UserID)
// 如果有正在运行的刷怪协程,发送停止信号 // 如果有正在运行的刷怪协程,发送停止信号
c.Info.MapID = 0 // 重置当前地图 c.Info.MapID = 0 // 重置当前地图
@@ -49,11 +49,12 @@ func (h *Controller) MapList(data *maps.ListMapPlayerInboundInfo, c *player.Play
result = &maps.ListMapPlayerOutboundInfo{} result = &maps.ListMapPlayerOutboundInfo{}
result.Player = make([]maps.OutInfo, 0) result.Player = make([]maps.OutInfo, 0)
space.GetSpace(c.Info.MapID).User.IterCb(func(playerID uint32, player common.PlayerI) { space.GetSpace(c.Info.MapID).User.Range(func(playerID uint32, player common.PlayerI) bool {
result1 := maps.NewOutInfo() result1 := maps.NewOutInfo()
copier.CopyWithOption(result1, player.GetInfo(), copier.Option{DeepCopy: true}) copier.CopyWithOption(result1, player.GetInfo(), copier.Option{DeepCopy: true})
result.Player = append(result.Player, *result1) result.Player = append(result.Player, *result1)
result.Player = LastFourElements(result.Player) result.Player = LastFourElements(result.Player)
return false
}) })
c.Canmon = true //可以刷怪 c.Canmon = true //可以刷怪

View File

@@ -37,7 +37,7 @@ func (h Controller) UserMoreInfo(data *user.MoreUserInfoInboundInfo, c *player.P
func (h Controller) Aimat(data *user.AimatInboundInfo, c *player.Player) (result *user.AimatOutboundInfo, err errorcode.ErrorCode) { func (h Controller) Aimat(data *user.AimatInboundInfo, c *player.Player) (result *user.AimatOutboundInfo, err errorcode.ErrorCode) {
defer space.GetSpace(c.Info.MapID).User.IterCb(func(playerID uint32, player common.PlayerI) { defer space.GetSpace(c.Info.MapID).User.Range(func(playerID uint32, player common.PlayerI) bool {
ret := &user.AimatOutboundInfo{ ret := &user.AimatOutboundInfo{
ItemId: data.ItemId, ItemId: data.ItemId,
@@ -47,14 +47,14 @@ func (h Controller) Aimat(data *user.AimatInboundInfo, c *player.Player) (result
} }
data.Head.Result = 0 data.Head.Result = 0
player.SendPack(data.Head.Pack(ret)) player.SendPack(data.Head.Pack(ret))
return false
}) })
return nil, -1 return nil, -1
} }
func (h Controller) Chat(data *user.ChatInboundInfo, c *player.Player) (result *user.ChatOutboundInfo, err errorcode.ErrorCode) { func (h Controller) Chat(data *user.ChatInboundInfo, c *player.Player) (result *user.ChatOutboundInfo, err errorcode.ErrorCode) {
defer space.GetSpace(c.Info.MapID).User.IterCb(func(playerID uint32, v common.PlayerI) { defer space.GetSpace(c.Info.MapID).User.Range(func(playerID uint32, v common.PlayerI) bool {
result = &user.ChatOutboundInfo{ result = &user.ChatOutboundInfo{
Message: string([]byte(data.Message)[:data.MessageLen-1]), Message: string([]byte(data.Message)[:data.MessageLen-1]),
@@ -64,6 +64,7 @@ func (h Controller) Chat(data *user.ChatInboundInfo, c *player.Player) (result *
result.Message = cool.Filter.Replace(result.Message, '*') result.Message = cool.Filter.Replace(result.Message, '*')
data.Head.Result = 0 data.Head.Result = 0
v.SendPack(data.Head.Pack(result)) v.SendPack(data.Head.Pack(result))
return false
}) })
return nil, -1 return nil, -1
@@ -76,7 +77,7 @@ func (h Controller) ChangePlayerColor(data *user.ChangeColorInboundInfo, c *play
c.Info.Color = data.Color c.Info.Color = data.Color
c.Info.Texture = 0 c.Info.Texture = 0
defer space.GetSpace(c.Info.MapID).User.IterCb(func(playerID uint32, v common.PlayerI) { defer space.GetSpace(c.Info.MapID).User.Range(func(playerID uint32, v common.PlayerI) bool {
data.Head.Result = 0 data.Head.Result = 0
result = &user.ChangeColorOutboundInfo{ result = &user.ChangeColorOutboundInfo{
UserId: c.Info.UserID, UserId: c.Info.UserID,
@@ -85,6 +86,7 @@ func (h Controller) ChangePlayerColor(data *user.ChangeColorInboundInfo, c *play
Texture: c.Info.Texture, Texture: c.Info.Texture,
} }
v.SendPack(data.Head.Pack(result)) v.SendPack(data.Head.Pack(result))
return false
}) })
return nil, -1 return nil, -1
@@ -95,7 +97,7 @@ func (h Controller) ChangePlayerDoodle(data *user.ChangeDoodleInboundInfo, c *pl
} }
c.Info.Texture = data.Id c.Info.Texture = data.Id
c.Info.Color = data.Color c.Info.Color = data.Color
defer space.GetSpace(c.Info.MapID).User.IterCb(func(playerID uint32, v common.PlayerI) { defer space.GetSpace(c.Info.MapID).User.Range(func(playerID uint32, v common.PlayerI) bool {
data.Head.Result = 0 data.Head.Result = 0
result = &user.ChangeDoodleOutboundInfo{ result = &user.ChangeDoodleOutboundInfo{
UserId: c.Info.UserID, UserId: c.Info.UserID,
@@ -104,6 +106,7 @@ func (h Controller) ChangePlayerDoodle(data *user.ChangeDoodleInboundInfo, c *pl
Texture: c.Info.Texture, Texture: c.Info.Texture,
} }
v.SendPack(data.Head.Pack(result)) v.SendPack(data.Head.Pack(result))
return false
}) })
return nil, -1 return nil, -1

View File

@@ -39,6 +39,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mhmtszr/concurrent-swiss-map v1.0.9 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/redis/go-redis/v9 v9.5.1 // indirect github.com/redis/go-redis/v9 v9.5.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect

View File

@@ -22,6 +22,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
@@ -69,6 +70,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mhmtszr/concurrent-swiss-map v1.0.9 h1:ijAlVG/QHC4A4FRdfdtq0oRJ03Zx9dsF8RSiBQB/gKk=
github.com/mhmtszr/concurrent-swiss-map v1.0.9/go.mod h1:F6QETL48Qn7jEJ3ZPt7EqRZjAAZu7lRQeQGIzXuUIDc=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg= github.com/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg=
@@ -77,6 +80,7 @@ github.com/panjf2000/gnet/v2 v2.9.1 h1:bKewICy/0xnQ9PMzNaswpe/Ah14w1TrRk91LHTcbI
github.com/panjf2000/gnet/v2 v2.9.1/go.mod h1:WQTxDWYuQ/hz3eccH0FN32IVuvZ19HewEWx0l62fx7E= github.com/panjf2000/gnet/v2 v2.9.1/go.mod h1:WQTxDWYuQ/hz3eccH0FN32IVuvZ19HewEWx0l62fx7E=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pointernil/bitset32 v0.0.1 h1:GL7o6ehG4W5ztXic75ncAmKzSxYQRSKxyaz2wOdz2aM= github.com/pointernil/bitset32 v0.0.1 h1:GL7o6ehG4W5ztXic75ncAmKzSxYQRSKxyaz2wOdz2aM=
github.com/pointernil/bitset32 v0.0.1/go.mod h1:naghmAYTkvJhKSCVPbBwLAXkHMF2WBIJ1bJpnFpmQwI= github.com/pointernil/bitset32 v0.0.1/go.mod h1:naghmAYTkvJhKSCVPbBwLAXkHMF2WBIJ1bJpnFpmQwI=
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=

View File

@@ -35,6 +35,13 @@ type StartPetWarInboundInfo struct {
Head player.TomeeHeader `cmd:"2431" struc:"[0]pad"` Head player.TomeeHeader `cmd:"2431" struc:"[0]pad"`
} }
// 表示"宠物王加入"的入站消息数据
type PetKingJoinInboundInfo struct {
Head player.TomeeHeader `cmd:"2413" struc:"[0]pad"`
Type uint32 // 战斗类型5=单精灵6=多精灵11=精灵大师赛 (对应Java的@UInt long type)
FightType uint32 // 仅当Type为11时有效 (对应Java的@UInt long fightType)
}
// HandleFightInviteInboundInfo 处理战斗邀请的入站消息 // HandleFightInviteInboundInfo 处理战斗邀请的入站消息
type HandleFightInviteInboundInfo struct { type HandleFightInviteInboundInfo struct {

View File

@@ -67,5 +67,5 @@ func (e *EffectAttackMiss) Skill_Hit_ex() bool {
// 设置参数:复用父类逻辑,设置持续回合 // 设置参数:复用父类逻辑,设置持续回合
func (e *EffectAttackMiss) SetArgs(t *input.Input, a ...int) { func (e *EffectAttackMiss) SetArgs(t *input.Input, a ...int) {
e.EffectNode.SetArgs(t, a...) e.EffectNode.SetArgs(t, a...)
e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0]) // 持续回合由SideEffectArgs[0]指定 e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0] - 1) // 持续回合由SideEffectArgs[0]指定
} }

View File

@@ -88,10 +88,7 @@ func (e *EffectPhysicalAttackAddStatus) SetArgs(t *input.Input, a ...int) {
// 计算持续回合(封装为独立方法,增强可读性) // 计算持续回合(封装为独立方法,增强可读性)
func (e *EffectPhysicalAttackAddStatus) getDuration() int { func (e *EffectPhysicalAttackAddStatus) getDuration() int {
// 优先使用配置的回合数SideEffectArgs[1]
if len(e.EffectNode.SideEffectArgs) > 1 {
return e.EffectNode.SideEffectArgs[1]
}
// 默认随机2~3回合Int31n(2)返回0/1+2后为2/3 // 默认随机2~3回合Int31n(2)返回0/1+2后为2/3
return int(e.Input.FightC.GetRand().Int31n(2)) + 2 return int(e.Input.FightC.GetRand().Int31n(2)) + 2
} }

View File

@@ -1,6 +1,7 @@
package maps package maps
import ( import (
"blazing/logic/service/common"
"blazing/logic/service/player" "blazing/logic/service/player"
"blazing/logic/service/space" "blazing/logic/service/space"
@@ -24,11 +25,14 @@ type InInfo struct {
func (t *InInfo) Broadcast(mapid uint32, o OutInfo) { func (t *InInfo) Broadcast(mapid uint32, o OutInfo) {
for _, v := range space.GetSpace(mapid).User.Items() { space.GetSpace(mapid).User.Range(func(k uint32, v common.PlayerI) (stop bool) {
t.Head.Result = 0 t.Head.Result = 0
v.SendPack(t.Head.Pack(&o)) v.SendPack(t.Head.Pack(&o))
} return false
})
} }
// 这里存储星球的map // 这里存储星球的map

View File

@@ -11,10 +11,11 @@ type LeaveMapInboundInfo struct {
} }
func (t *LeaveMapInboundInfo) Broadcast(mapid uint32, o space.LeaveMapOutboundInfo) { func (t *LeaveMapInboundInfo) Broadcast(mapid uint32, o space.LeaveMapOutboundInfo) {
space.GetSpace(mapid).User.IterCb(func(playerID uint32, player common.PlayerI) { space.GetSpace(mapid).User.Range(func(playerID uint32, player common.PlayerI) bool {
t.Head.Result = 0 t.Head.Result = 0
player.SendPack(t.Head.Pack(&o)) player.SendPack(t.Head.Pack(&o))
return false
}) })
} }

View File

@@ -30,10 +30,11 @@ func (t *WalkInInfo) Broadcast(mapid uint32, o WalkOutInfo) {
if !limiter.Allow() { if !limiter.Allow() {
return return
} }
space.GetSpace(mapid).User.IterCb(func(playerID uint32, player common.PlayerI) { space.GetSpace(mapid).User.Range(func(playerID uint32, player common.PlayerI) bool {
t.Head.Result = 0 t.Head.Result = 0
tt := t.Head.Pack(&o) tt := t.Head.Pack(&o)
player.SendPack(tt) player.SendPack(tt)
return false
}) })
} }

View File

@@ -1,6 +1,7 @@
package pet package pet
import ( import (
"blazing/logic/service/common"
"blazing/logic/service/player" "blazing/logic/service/player"
"blazing/logic/service/space" "blazing/logic/service/space"
"blazing/modules/blazing/model" "blazing/modules/blazing/model"
@@ -39,12 +40,13 @@ type PetShowInboundInfo struct {
} }
func (t *PetShowInboundInfo) Broadcast(mapid uint32, o PetShowOutboundInfo) { func (t *PetShowInboundInfo) Broadcast(mapid uint32, o PetShowOutboundInfo) {
space.GetSpace(mapid).User.Range(func(key uint32, v common.PlayerI) (stop bool) {
for _, v := range space.GetSpace(mapid).User.Items() {
t.Head.Result = 0 t.Head.Result = 0
v.SendPack(t.Head.Pack(&o)) v.SendPack(t.Head.Pack(&o))
} return false
})
} }
type PetShowOutboundInfo struct { type PetShowOutboundInfo struct {

View File

@@ -11,7 +11,7 @@ import (
func (lw *Player) InvitePlayerToBattle() { func (lw *Player) InvitePlayerToBattle() {
pinfo := lw.PVPinfo pinfo := lw.PVPinfo
space.GetSpace(lw.Info.MapID).User.IterCb(func(key uint32, v common.PlayerI) { space.GetSpace(lw.Info.MapID).User.Range(func(key uint32, v common.PlayerI) bool {
value := v.(*Player) value := v.(*Player)
if key == uint32(pinfo.PlayerID) { //说明这里是针对玩家邀请的 if key == uint32(pinfo.PlayerID) { //说明这里是针对玩家邀请的
@@ -24,30 +24,31 @@ func (lw *Player) InvitePlayerToBattle() {
Mode: pinfo.Mode, Mode: pinfo.Mode,
} }
value.SendPack(t1.Pack(&t)) value.SendPack(t1.Pack(&t))
return return true
} }
return false
}) })
} }
func (p *Player) PET_MELEE() *Player { func (p *Player) Pet_joinFight() *Player {
var lw *Player var lw *Player
//pinfo := lw.PVPinfo //pinfo := lw.PVPinfo
space.GetSpace(p.Info.MapID).User.IterCb(func(key uint32, v common.PlayerI) { space.GetSpace(p.Info.MapID).User.Range(func(key uint32, v common.PlayerI) bool {
value := v.(*Player) value := v.(*Player)
if value.PVPinfo != nil && value != p { if value.PVPinfo != nil && value != p {
//确认是乱斗模式 //确认是乱斗模式
if value.PVPinfo.Mode == info.BattleMode.PET_MELEE { if *value.PVPinfo == *p.PVPinfo {
lw.PVPinfo = nil //先将自身的准备信息置空 p.PVPinfo = nil //先将自身的准备信息置空
value.PVPinfo = nil
lw = value lw = value
return return true
} }
} }
return false
}) })
return lw return lw
} }

View File

@@ -311,11 +311,13 @@ func LeaveMap(c common.PlayerI) {
} }
t := NewTomeeHeader(2002, c.GetInfo().UserID) t := NewTomeeHeader(2002, c.GetInfo().UserID)
for k, v := range space.GetSpace(c.GetInfo().MapID).User.Items() { space.GetSpace(c.GetInfo().MapID).User.Range(func(k uint32, v common.PlayerI) (stop bool) {
if k != c.GetInfo().UserID { if k != c.GetInfo().UserID {
v.SendPack(t.Pack(&space.LeaveMapOutboundInfo{UserID: c.GetInfo().UserID})) v.SendPack(t.Pack(&space.LeaveMapOutboundInfo{UserID: c.GetInfo().UserID}))
} }
return false
})
} space.GetSpace(c.GetInfo().MapID).User.Delete(c.GetInfo().UserID)
space.GetSpace(c.GetInfo().MapID).User.Remove(c.GetInfo().UserID)
} }

View File

@@ -3,14 +3,15 @@ package space
import ( import (
"blazing/common/data/xmlres" "blazing/common/data/xmlres"
"blazing/common/utils" "blazing/common/utils"
"blazing/logic/service/common" "blazing/logic/service/common"
csmap "github.com/mhmtszr/concurrent-swiss-map"
) )
// Space 针对Player的并发安全map键为uint32类型 // Space 针对Player的并发安全map键为uint32类型
type Space struct { type Space struct {
User utils.ConcurrentMap[uint32, common.PlayerI] // 存储玩家数据的map键为玩家ID User *csmap.CsMap[uint32, common.PlayerI] // 存储玩家数据的map键为玩家ID
CanRefresh bool //是否能够刷怪 CanRefresh bool //是否能够刷怪
//ID uint32 // 地图ID //ID uint32 // 地图ID
Name string //地图名称 Name string //地图名称
// DefaultPos model.Pos //默认位置DefaultPos // DefaultPos model.Pos //默认位置DefaultPos
@@ -21,9 +22,20 @@ type Space struct {
// NewSyncMap 创建一个新的玩家同步map // NewSyncMap 创建一个新的玩家同步map
func NewSpace() *Space { func NewSpace() *Space {
return &Space{ return &Space{
User: utils.NewWithCustomShardingFunction[uint32, common.PlayerI](func(key uint32) uint32 { User: csmap.New[uint32, common.PlayerI](
return key // set the number of map shards. the default value is 32.
}), csmap.WithShardCount[uint32, common.PlayerI](32),
// // if don't set custom hasher, use the built-in maphash.
// csmap.WithCustomHasher[string, int](func(key string) uint64 {
// hash := fnv.New64a()
// hash.Write([]byte(key))
// return hash.Sum64()
// }),
// set the total capacity, every shard map has total capacity/shard count capacity. the default value is 0.
// csmap.WithSize[string, int](1000),
),
} }
} }