feat(player): 重构怪物闪光效果生成逻辑并优化颜色矩阵随机算法
- 移除了 Monster.go 中对 `model.GlowFilter` 的直接构造逻辑,改用统一的 `RandSHiny()` 方法处理异色光晕配置 - 新增 `RandomMatrixNoSingleColorBright` 和 `RandomMatrixNoSingleColorBrightDefault` 函数,增强颜色矩阵生成的灵活性和亮度控制能力 - 修复可能因全拷贝模式导致图像单一色彩的问题,确保至少有一行使用偏移量 - 增加 redBias 和 brightnessScale 参数支持,提升颜色多样性和视觉表现力 - 使用
This commit is contained in:
@@ -3,7 +3,6 @@ package player
|
|||||||
import (
|
import (
|
||||||
"blazing/common/data/xmlres"
|
"blazing/common/data/xmlres"
|
||||||
"blazing/logic/service/common"
|
"blazing/logic/service/common"
|
||||||
"blazing/modules/blazing/model"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@@ -49,32 +48,11 @@ func (p *Player) genMonster() {
|
|||||||
ttt.Id = gconv.Uint32(RandomStringFromSlice(id))
|
ttt.Id = gconv.Uint32(RandomStringFromSlice(id))
|
||||||
|
|
||||||
if ttt.Id != 0 {
|
if ttt.Id != 0 {
|
||||||
ttt.ShinyInfo = make([]model.GlowFilter, 1)
|
|
||||||
// 假设 t 是包含 ShinyInfo 字段的结构体,ShinyInfo 是 GlowFilter 类型的切片
|
|
||||||
ttt.ShinyInfo[0] = model.GlowFilter{
|
|
||||||
// 光晕颜色:白色(十六进制 0xFFFFFF),符合 uint32 类型
|
|
||||||
Color: 65535,
|
|
||||||
// 透明度:0.8(0.0~1.0 范围内的合理值,float64 类型)
|
|
||||||
Alpha: 0.8,
|
|
||||||
// 水平模糊量:10(0~255 范围内,uint8 类型,略高于默认值6)
|
|
||||||
BlurX: 20,
|
|
||||||
// 垂直模糊量:10(与 BlurX 对称,uint8 类型)
|
|
||||||
BlurY: 20,
|
|
||||||
// 发光强度:8(0~255 范围内,uint8 类型,略高于默认值2)
|
|
||||||
Strength: 1,
|
|
||||||
// 滤镜应用次数:2(1~3 范围内,int 类型,非默认值1)
|
|
||||||
Quality: 2,
|
|
||||||
// 内侧发光:true(bool 类型,模拟开启内侧发光)
|
|
||||||
Inner: true,
|
|
||||||
// 挖空:false(bool 类型,保持默认逻辑)
|
|
||||||
Knockout: false,
|
|
||||||
// 颜色矩阵:标准 RGBA 矩阵(20个uint8,符合 [20]uint8 数组类型)
|
|
||||||
// 矩阵含义:R=100%、G=100%、B=100%、A=100%,无颜色偏移
|
|
||||||
ColorMatrixFilter: RandomMatrixDefaultAlphaNoSingleColor(int64(grand.Intn(5000)), 0.8),
|
|
||||||
}
|
|
||||||
// g.Dump(ttt.ShinyInfo)
|
|
||||||
// ttt.Shiny = 0 //待确认是否刷新异色
|
|
||||||
ttt.Lv = gconv.Uint32(RandomStringFromSlice(lv))
|
ttt.Lv = gconv.Uint32(RandomStringFromSlice(lv))
|
||||||
|
if grand.Meet(1, 100) {
|
||||||
|
ttt.RandSHiny()
|
||||||
|
}
|
||||||
|
|
||||||
if len(id) == 1 { //说明这里只固定刷一个,概率变尼尔尼奥
|
if len(id) == 1 { //说明这里只固定刷一个,概率变尼尔尼奥
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,48 @@
|
|||||||
package player
|
package player
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
grand "math/rand"
|
grand "math/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RandomMatrixNoSingleColor 生成不会导致“只有一种颜色”的 4x5 uint8 矩阵(共20字节)。
|
// RandomMatrixNoSingleColorBright 是在 RandomMatrixNoSingleColorBias 基础上的变体,
|
||||||
// 约束与策略:
|
// 增加 brightnessScale 参数用于控制允许的 offset(亮度提升)的比例。
|
||||||
// - 仍然适用“直接把 uint8 原样当系数使用”的前提,保证不会白化或溢出:
|
// 参数说明:
|
||||||
// - 当某行有 multiplier != 0 时,offset 强制为 0;且 multipliers 最大为 1 且每行最多一个 1(L1<=1)
|
// - seed: 随机种子
|
||||||
// - 当某行使用常量偏移模式(ModeB)时,四个 multiplier 全为 0,offset 为常量(在安全中间区)
|
// - alphaRow: 最后 5 字节(alpha 行)固定值
|
||||||
//
|
// - brightChance: 原有决定 ModeB 的概率(常量行概率)
|
||||||
// - 为避免“只有一种颜色”的情况,我们强制:
|
// - redBias: 保持原有 R 偏好(0..1)
|
||||||
// - 至少有一个行使用 ModeA(依赖输入通道)。若有多个 ModeA,则这些 ModeA 的“pick”尽量互不相同(从 0..3 中抽样不重复),
|
// - brightnessScale: 0..1,0 表示不额外提升亮度(行为近似原函数),1 表示允许最大可用 offset(受每行 sum 限制)
|
||||||
// 这样 destR/destG/destB 至少有不同的输入来源,从而随输入像素变化产生多色彩输出。
|
// 返回:20 字节矩阵(直接 uint8 原样使用)
|
||||||
// - 如果有多个 ModeB(常量行),它们的 offset 不相同,且不同时全为相同值,避免所有通道都变为同一常量。
|
func RandomMatrixNoSingleColorBright(seed int64, alphaRow [5]uint8, brightChance, redBias, brightnessScale float64) [20]uint8 {
|
||||||
//
|
|
||||||
// - alphaRow (最后5字节) 由调用方指定并固定不变(默认 [0,0,0,1,0])。
|
|
||||||
//
|
|
||||||
// brightChance: 0..1,用于决定每行是否优先使用常量偏移 (ModeB);函数内部会修正以保证多样性。
|
|
||||||
func RandomMatrixNoSingleColor(seed int64, alphaRow [5]uint8, brightChance float64) [20]uint8 {
|
|
||||||
var out [20]uint8
|
var out [20]uint8
|
||||||
|
|
||||||
// clamp brightChance
|
// clamp params
|
||||||
if brightChance < 0 {
|
if brightChance < 0 {
|
||||||
brightChance = 0
|
brightChance = 0
|
||||||
}
|
}
|
||||||
if brightChance > 1 {
|
if brightChance > 1 {
|
||||||
brightChance = 1
|
brightChance = 1
|
||||||
}
|
}
|
||||||
|
if redBias < 0 {
|
||||||
|
redBias = 0
|
||||||
|
}
|
||||||
|
if redBias > 1 {
|
||||||
|
redBias = 1
|
||||||
|
}
|
||||||
|
if brightnessScale < 0 {
|
||||||
|
brightnessScale = 0
|
||||||
|
}
|
||||||
|
if brightnessScale > 1 {
|
||||||
|
brightnessScale = 1
|
||||||
|
}
|
||||||
|
|
||||||
// offset 范围(中间区,避免接近 0 或 255)
|
const minOffsetBase = 24 // 最小偏移基线(你可以调大以更亮)
|
||||||
const minOffset = 40
|
const maxOffsetBase = 180 // 最大偏移基线(经验上不宜太接近 255)
|
||||||
const maxOffset = 215
|
|
||||||
|
|
||||||
grand.Seed(seed)
|
grand.Seed(seed)
|
||||||
|
|
||||||
// 初步决定每行的模式(true=ModeB(常量偏移),false=ModeA(拷贝/置换))
|
// 1) 先按原逻辑决定 ModeA / ModeB 与 picks(保留 redBias 偏好)
|
||||||
modeB := make([]bool, 3)
|
modeB := make([]bool, 3)
|
||||||
modeBCount := 0
|
modeBCount := 0
|
||||||
for r := 0; r < 3; r++ {
|
for r := 0; r < 3; r++ {
|
||||||
@@ -46,99 +53,171 @@ func RandomMatrixNoSingleColor(seed int64, alphaRow [5]uint8, brightChance float
|
|||||||
modeB[r] = false
|
modeB[r] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 强制至少一个 ModeA(依赖输入),以避免全常量图像
|
|
||||||
if modeBCount == 3 {
|
if modeBCount == 3 {
|
||||||
// 随机把一行改为 ModeA
|
|
||||||
which := int(grand.Intn(3))
|
which := int(grand.Intn(3))
|
||||||
modeB[which] = false
|
modeB[which] = false
|
||||||
modeBCount--
|
modeBCount--
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为 ModeA 行分配不重复的 pick(0..3),尽量保证不同输入来源
|
// assign ModeA picks with red bias (similar于之前实现)
|
||||||
// 先收集需要 pick 的行索引
|
available := []int{0, 1, 2, 3}
|
||||||
pickRows := make([]int, 0, 3)
|
picks := make(map[int]int)
|
||||||
|
used := make(map[int]bool)
|
||||||
|
modeARows := []int{}
|
||||||
for r := 0; r < 3; r++ {
|
for r := 0; r < 3; r++ {
|
||||||
if !modeB[r] {
|
if !modeB[r] {
|
||||||
pickRows = append(pickRows, r)
|
modeARows = append(modeARows, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// available inputs 0..3
|
weightedPick := func(avoidUsed bool) int {
|
||||||
available := []int{0, 1, 2, 3}
|
cands := []int{}
|
||||||
// 随机打乱 available
|
ws := []float64{}
|
||||||
for i := len(available) - 1; i > 0; i-- {
|
for _, c := range available {
|
||||||
j := int(grand.Intn(i + 1))
|
if avoidUsed && used[c] {
|
||||||
available[i], available[j] = available[j], available[i]
|
continue
|
||||||
}
|
}
|
||||||
// 给每个需要 pick 的行分配不同输入(如果 pickRows 数量 <= 4 则肯定可不重复)
|
w := 1.0
|
||||||
picks := make(map[int]int) // row -> pick
|
if c == 0 {
|
||||||
for i, row := range pickRows {
|
w += redBias * 3.0
|
||||||
picks[row] = available[i%len(available)]
|
} // 红色加权
|
||||||
|
cands = append(cands, c)
|
||||||
|
ws = append(ws, w)
|
||||||
|
}
|
||||||
|
if len(cands) == 0 {
|
||||||
|
for _, c := range available {
|
||||||
|
w := 1.0
|
||||||
|
if c == 0 {
|
||||||
|
w += redBias * 3.0
|
||||||
|
}
|
||||||
|
cands = append(cands, c)
|
||||||
|
ws = append(ws, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum := 0.0
|
||||||
|
for _, w := range ws {
|
||||||
|
sum += w
|
||||||
|
}
|
||||||
|
rv := grand.Float64() * sum
|
||||||
|
acc := 0.0
|
||||||
|
for i, w := range ws {
|
||||||
|
acc += w
|
||||||
|
if rv <= acc {
|
||||||
|
return cands[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cands[len(cands)-1]
|
||||||
|
}
|
||||||
|
for _, row := range modeARows {
|
||||||
|
p := weightedPick(true)
|
||||||
|
picks[row] = p
|
||||||
|
used[p] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为 ModeB 行分配 offsets,保证多个 ModeB 行的 offset 不相同且不等于边界值
|
// 2) 先把 multipliers 写入 out(ModeA one-hot,ModeB 暂置 multipliers=0)
|
||||||
usedOffsets := map[int]bool{}
|
|
||||||
for r := 0; r < 3; r++ {
|
for r := 0; r < 3; r++ {
|
||||||
if modeB[r] {
|
base := r * 5
|
||||||
// 随机取一个不同的 offset
|
if !modeB[r] {
|
||||||
var off int
|
p := picks[r]
|
||||||
for tries := 0; tries < 10; tries++ {
|
for c := 0; c < 4; c++ {
|
||||||
off = minOffset + int(grand.Intn(maxOffset-minOffset+1))
|
if c == p {
|
||||||
if !usedOffsets[off] {
|
out[base+c] = 1
|
||||||
|
} else {
|
||||||
|
out[base+c] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out[base+4] = 0 // 暂时保持 0,后面可能根据 sum 注入 offset
|
||||||
|
} else {
|
||||||
|
for c := 0; c < 4; c++ {
|
||||||
|
out[base+c] = 0
|
||||||
|
}
|
||||||
|
out[base+4] = 0 // 将在下一步设置合法 offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) 基于每行 multipliers 的 sum 计算允许的 offset 上限,并在允许范围内按 brightnessScale 随机选择 offset
|
||||||
|
// allowedMaxOffset = min(maxOffsetBase, floor(255*(1 - sum))) 然后乘以 brightnessScale(线性缩放)
|
||||||
|
hadOffsetRow := false
|
||||||
|
for r := 0; r < 3; r++ {
|
||||||
|
base := r * 5
|
||||||
|
// 计算 multipliers sum(目前为 0/1)
|
||||||
|
sum := 0
|
||||||
|
for c := 0; c < 4; c++ {
|
||||||
|
sum += int(out[base+c])
|
||||||
|
}
|
||||||
|
// 允许的最大 offset(绝对上限由 sum 决定)
|
||||||
|
allowedBySum := int(math.Floor(255.0 * (1.0 - float64(sum))))
|
||||||
|
if allowedBySum < 0 {
|
||||||
|
allowedBySum = 0
|
||||||
|
}
|
||||||
|
// 想要的最大基线由 maxOffsetBase 决定,再乘以 brightnessScale
|
||||||
|
desiredMax := int(float64(maxOffsetBase) * brightnessScale)
|
||||||
|
// 最终可用最大 offset(至少要大于等于 minOffsetBase 才能选择)
|
||||||
|
allowedMax := allowedBySum
|
||||||
|
if desiredMax < allowedMax {
|
||||||
|
allowedMax = desiredMax
|
||||||
|
}
|
||||||
|
if allowedMax < minOffsetBase {
|
||||||
|
// 不足最小基线,设为 0(不可用 offset)
|
||||||
|
out[base+4] = 0
|
||||||
|
} else {
|
||||||
|
// 在 [minOffsetBase, allowedMax] 随机取一个 offset
|
||||||
|
off := minOffsetBase + int(grand.Intn(allowedMax-minOffsetBase+1))
|
||||||
|
out[base+4] = uint8(off)
|
||||||
|
hadOffsetRow = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) 保证至少有一行有 offset(避免整个图都只靠拷贝导致暗)
|
||||||
|
if !hadOffsetRow {
|
||||||
|
// 尝试找到 sum < 1 的行 preferentially Rout (row 0) then others
|
||||||
|
rowChosen := -1
|
||||||
|
for r := 0; r < 3; r++ {
|
||||||
|
base := r * 5
|
||||||
|
sum := 0
|
||||||
|
for c := 0; c < 4; c++ {
|
||||||
|
sum += int(out[base+c])
|
||||||
|
}
|
||||||
|
if sum < 1 {
|
||||||
|
rowChosen = r
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 若多次冲突仍然存在,仍使用最后一个 off(collision 可能性低)
|
if rowChosen == -1 {
|
||||||
usedOffsets[off] = true
|
// 所有行 sum==1(很可能全部 one-hot),我们需要强制把其中一行的 multiplier 置为 0 并赋 offset
|
||||||
// 写入 multipliers 全 0,offset = off
|
rowChosen = int(grand.Intn(3))
|
||||||
|
base := rowChosen * 5
|
||||||
for c := 0; c < 4; c++ {
|
for c := 0; c < 4; c++ {
|
||||||
out[r*5+c] = 0
|
out[base+c] = 0
|
||||||
}
|
}
|
||||||
out[r*5+4] = uint8(off)
|
}
|
||||||
} else {
|
// 计算 allowedBySum 并设置 offset 为 min(allowedBySum, desired),至少设为 minOffsetBase
|
||||||
// ModeA:拷贝/置换,只有一个 multiplier=1(pick),其他为0,offset=0
|
base := rowChosen * 5
|
||||||
pick := picks[r]
|
sum := 0
|
||||||
for c := 0; c < 4; c++ {
|
for c := 0; c < 4; c++ {
|
||||||
if c == pick {
|
sum += int(out[base+c])
|
||||||
out[r*5+c] = 1
|
}
|
||||||
|
allowedBySum := int(math.Floor(255.0 * (1.0 - float64(sum))))
|
||||||
|
if allowedBySum < minOffsetBase {
|
||||||
|
// 若仍不足则设为 min(minOffsetBase, allowedBySum) 或强制给一个较小的值
|
||||||
|
if allowedBySum > 0 {
|
||||||
|
out[base+4] = uint8(allowedBySum)
|
||||||
} else {
|
} else {
|
||||||
out[r*5+c] = 0
|
out[base+4] = uint8(minOffsetBase) // 最后手段(风险低,因为我们可能已把 sum=0)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
desiredMax := int(float64(maxOffsetBase) * brightnessScale)
|
||||||
|
if desiredMax > allowedBySum {
|
||||||
|
desiredMax = allowedBySum
|
||||||
}
|
}
|
||||||
out[r*5+4] = 0
|
if desiredMax < minOffsetBase {
|
||||||
|
desiredMax = minOffsetBase
|
||||||
|
}
|
||||||
|
off := minOffsetBase + int(grand.Intn(desiredMax-minOffsetBase+1))
|
||||||
|
out[base+4] = uint8(off)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 额外保护:防止三行输出仍然完全相同的常量或同一输入通道(极小概率)
|
// 5) 最后 5 个字节(alpha 行)由 caller 指定并固定
|
||||||
// 情况1:如果三行都是 ModeA 且 picks 全相同 -> 强制将其中一行改为拷贝另一个不同的输入(或改为 ModeB)
|
|
||||||
if !modeB[0] && !modeB[1] && !modeB[2] {
|
|
||||||
if picks[0] == picks[1] && picks[1] == picks[2] {
|
|
||||||
// 将第三行改为 pick = (picks[2]+1)%4 保证不同
|
|
||||||
newPick := (picks[2] + 1) % 4
|
|
||||||
for c := 0; c < 4; c++ {
|
|
||||||
if c == newPick {
|
|
||||||
out[2*5+c] = 1
|
|
||||||
} else {
|
|
||||||
out[2*5+c] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out[2*5+4] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 情况2:如果三行都是 ModeB 且 offsets 有重复(理论上不可能因为我们尽量保证唯一),强制调整
|
|
||||||
if modeB[0] && modeB[1] && modeB[2] {
|
|
||||||
if out[4] == out[9] && out[9] == out[14] {
|
|
||||||
// adjust third offset by +17 (并截断到范围)
|
|
||||||
newOff := int(out[14]) + 17
|
|
||||||
if newOff > maxOffset {
|
|
||||||
newOff = minOffset + ((newOff - minOffset) % (maxOffset - minOffset + 1))
|
|
||||||
}
|
|
||||||
out[14] = uint8(newOff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最后 5 个字节由 caller 指定(alpha 行)并固定
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
out[15+i] = alphaRow[i]
|
out[15+i] = alphaRow[i]
|
||||||
}
|
}
|
||||||
@@ -146,6 +225,7 @@ func RandomMatrixNoSingleColor(seed int64, alphaRow [5]uint8, brightChance float
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandomMatrixDefaultAlphaNoSingleColor(seed int64, brightChance float64) [20]uint8 {
|
// 便利 wrapper:默认 redBias=0.6,brightnessScale=0.5
|
||||||
return RandomMatrixNoSingleColor(seed, [5]uint8{0, 0, 0, 1, 0}, brightChance)
|
func RandomMatrixNoSingleColorBrightDefault(seed int64, alphaRow [5]uint8, brightChance float64) [20]uint8 {
|
||||||
|
return RandomMatrixNoSingleColorBright(seed, alphaRow, brightChance, 0.6, 0.5)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"blazing/logic/service/fight/info"
|
"blazing/logic/service/fight/info"
|
||||||
"blazing/logic/service/space"
|
"blazing/logic/service/space"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"blazing/modules/base/service"
|
"blazing/modules/base/service"
|
||||||
|
|
||||||
@@ -46,6 +47,36 @@ type OgrePetInfo struct {
|
|||||||
Ext uint32 `struc:"skip"` //是否变尼尔尼奥
|
Ext uint32 `struc:"skip"` //是否变尼尔尼奥
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *OgrePetInfo) RandSHiny() {
|
||||||
|
o.ShinyInfo = make([]model.GlowFilter, 1)
|
||||||
|
// 假设 t 是包含 ShinyInfo 字段的结构体,ShinyInfo 是 GlowFilter 类型的切片
|
||||||
|
o.ShinyInfo[0] = model.GlowFilter{
|
||||||
|
// 光晕颜色:白色(十六进制 0xFFFFFF),符合 uint32 类型
|
||||||
|
Color: 65535,
|
||||||
|
// 透明度:0.8(0.0~1.0 范围内的合理值,float64 类型)
|
||||||
|
Alpha: 0.3,
|
||||||
|
// 水平模糊量:10(0~255 范围内,uint8 类型,略高于默认值6)
|
||||||
|
BlurX: 20,
|
||||||
|
// 垂直模糊量:10(与 BlurX 对称,uint8 类型)
|
||||||
|
BlurY: 20,
|
||||||
|
// 发光强度:8(0~255 范围内,uint8 类型,略高于默认值2)
|
||||||
|
Strength: 1,
|
||||||
|
// 滤镜应用次数:2(1~3 范围内,int 类型,非默认值1)
|
||||||
|
Quality: 2,
|
||||||
|
// 内侧发光:true(bool 类型,模拟开启内侧发光)
|
||||||
|
Inner: true,
|
||||||
|
// 挖空:false(bool 类型,保持默认逻辑)
|
||||||
|
Knockout: false,
|
||||||
|
// 颜色矩阵:标准 RGBA 矩阵(20个uint8,符合 [20]uint8 数组类型)
|
||||||
|
// 矩阵含义:R=100%、G=100%、B=100%、A=100%,无颜色偏移
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
o.ShinyInfo[0].ColorMatrixFilter = RandomMatrixNoSingleColorBrightDefault(time.Now().Unix(), [5]uint8{0, 0, 0, 1, 0}, 0.6)
|
||||||
|
//g.Dump(ttt.ShinyInfo)
|
||||||
|
// ttt.Shiny = 0 //待确认是否刷新异色
|
||||||
|
}
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
MainConn gnet.Conn
|
MainConn gnet.Conn
|
||||||
baseplayer
|
baseplayer
|
||||||
|
|||||||
Reference in New Issue
Block a user