This commit is contained in:
昔念
2026-04-07 17:26:52 +08:00
91 changed files with 3417 additions and 811 deletions

View File

@@ -22,8 +22,14 @@ var ctx = context.TODO()
type Cmd struct {
Func reflect.Value //方法函数
Req reflect.Type //请求体
// HeaderFieldIndex 是请求结构体中 TomeeHeader 字段的索引路径。
HeaderFieldIndex []int
// UseConn 标记第二个参数是否为 gnet.Conn。
UseConn bool
// 新增预缓存的req创建函数返回结构体指针
NewReqFunc func() interface{}
// NewReqValue 返回请求结构体指针的 reflect.Value避免重复构造类型信息。
NewReqValue func() reflect.Value
//Res reflect.Value //返回体
}

View File

@@ -6,6 +6,16 @@ func AddClient(id uint32, client *ClientHandler) {
Clientmap.Store(id, client) // sync.Map存值
}
// 清理指定clientuid=100000*onlineID+port
func DeleteClientOnly(uid uint32) {
Clientmap.Delete(uid)
}
// 清理指定clientonlineID+port
func DeleteClient(id, port uint32) {
Clientmap.Delete(100000*id + port)
}
// 取值示例
func GetClient(id, port uint32) (*ClientHandler, bool) {
// 普通mapclient, ok := Clientmap[id]

View File

@@ -42,15 +42,15 @@ const (
maxMatrixSize = 227 // 矩阵维度覆盖最大属性ID 226
)
// 合法单属性ID集合快速校验
var validSingleElementIDs = map[int]bool{
// 合法单属性ID集合按ID直接索引避免运行时 map 查找
var validSingleElementIDs = [maxMatrixSize]bool{
1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true,
11: true, 12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true, 20: true,
221: true, 222: true, 223: true, 224: true, 225: true, 226: true,
}
// 元素名称映射(全属性对应,便于日志输出)
var elementNameMap = map[ElementType]string{
// 元素名称映射(按ID直接索引,便于日志输出)
var elementNameMap = [maxMatrixSize]string{
ElementTypeGrass: "GRASS",
ElementTypeWater: "WATER",
ElementTypeFire: "FIRE",
@@ -198,46 +198,55 @@ type ElementCombination struct {
ID int // 组合唯一ID
}
// 全局预加载资源(程序启动时init初始化,运行时直接使用
// 全局预加载资源(程序启动时初始化,运行时只读
var (
// 元素组合池key=组合IDvalue=组合实例(预加载所有合法组合)
elementCombinationPool = make(map[int]*ElementCombination, 150) // 128双+26单=154预分配足够容量
// 单属性克制矩阵预初始化所有特殊克制关系默认1.0
matrix [maxMatrixSize][maxMatrixSize]float64
validCombinationIDs [maxMatrixSize]bool
elementCombinationPool [maxMatrixSize]ElementCombination
dualElementSecondaryPool [maxMatrixSize]ElementType
matrix [maxMatrixSize][maxMatrixSize]float64
Calculator *ElementCalculator
)
// init 预加载所有资源(程序启动时执行一次,无并发问题)
func init() {
// 1. 初始化单属性克制矩阵
initFullTableMatrix()
initElementCombinationPool()
Calculator = NewElementCalculator()
}
// 2. 预加载所有单属性组合
for id := range validSingleElementIDs {
combo := &ElementCombination{
Primary: ElementType(id),
Secondary: nil,
ID: id,
func initElementCombinationPool() {
for id, valid := range validSingleElementIDs {
if !valid {
continue
}
validCombinationIDs[id] = true
elementCombinationPool[id] = ElementCombination{
Primary: ElementType(id),
ID: id,
}
elementCombinationPool[id] = combo
}
// 3. 预加载所有双属性组合
for dualID, atts := range dualElementMap {
primaryID, secondaryID := atts[0], atts[1]
// 按ID升序排序保证组合一致性
primary, secondary := ElementType(primaryID), ElementType(secondaryID)
if primary > secondary {
primary, secondary = secondary, primary
}
combo := &ElementCombination{
dualElementSecondaryPool[dualID] = secondary
validCombinationIDs[dualID] = true
elementCombinationPool[dualID] = ElementCombination{
Primary: primary,
Secondary: &secondary,
Secondary: &dualElementSecondaryPool[dualID],
ID: dualID,
}
elementCombinationPool[dualID] = combo
}
}
func isValidCombinationID(id int) bool {
return id > 0 && id < maxMatrixSize && validCombinationIDs[id]
}
// IsDual 判断是否为双属性
func (ec *ElementCombination) IsDual() bool {
return ec.Secondary != nil
@@ -245,84 +254,82 @@ func (ec *ElementCombination) IsDual() bool {
// Elements 获取所有属性列表
func (ec *ElementCombination) Elements() []ElementType {
if ec.IsDual() {
return []ElementType{ec.Primary, *ec.Secondary}
if secondary := ec.Secondary; secondary != nil {
return []ElementType{ec.Primary, *secondary}
}
return []ElementType{ec.Primary}
}
// String 友好格式化输出
func (ec *ElementCombination) String() string {
primaryName := elementNameMap[ec.Primary]
if !ec.IsDual() {
return fmt.Sprintf("(%s)", primaryName)
if secondary := ec.Secondary; secondary != nil {
return fmt.Sprintf("(%s, %s)", elementNameMap[ec.Primary], elementNameMap[*secondary])
}
return fmt.Sprintf("(%s, %s)", primaryName, elementNameMap[*ec.Secondary])
return fmt.Sprintf("(%s)", elementNameMap[ec.Primary])
}
// ElementCalculator 无锁元素克制计算器(依赖预加载资源
// ElementCalculator 无锁元素克制计算器(所有倍数在初始化阶段预计算
type ElementCalculator struct {
offensiveCache map[string]float64 // 攻击克制缓存(运行时填充,无并发写)
offensiveTable [maxMatrixSize][maxMatrixSize]float64
}
// NewElementCalculator 创建计算器实例(仅初始化缓存)
// NewElementCalculator 创建计算器实例(构建只读查表缓存)
func NewElementCalculator() *ElementCalculator {
return &ElementCalculator{
offensiveCache: make(map[string]float64, 4096), // 预分配大容量缓存
c := &ElementCalculator{}
c.initOffensiveTable()
return c
}
func (c *ElementCalculator) initOffensiveTable() {
for attackerID, valid := range validCombinationIDs {
if !valid {
continue
}
attacker := &elementCombinationPool[attackerID]
for defenderID, valid := range validCombinationIDs {
if !valid {
continue
}
defender := &elementCombinationPool[defenderID]
c.offensiveTable[attackerID][defenderID] = c.calculateMultiplier(attacker, defender)
}
}
}
// getMatrixValue 直接返回矩阵值修复核心问题不再将0转换为1
func (c *ElementCalculator) getMatrixValue(attacker, defender ElementType) float64 {
return matrix[attacker][defender] // 矩阵默认已初始化1.0,特殊值直接返回
return matrix[attacker][defender]
}
// GetCombination 获取元素组合(直接从预加载池读取
// GetCombination 获取元素组合(直接按ID索引
func (c *ElementCalculator) GetCombination(id int) (*ElementCombination, error) {
combo, exists := elementCombinationPool[id]
if !exists {
if !isValidCombinationID(id) {
return nil, fmt.Errorf("invalid element combination ID: %d", id)
}
return combo, nil
return &elementCombinationPool[id], nil
}
// GetOffensiveMultiplier 计算攻击方→防御方的克制倍数(缓存优先
// GetOffensiveMultiplier 计算攻击方→防御方的克制倍数(只读查表
func (c *ElementCalculator) GetOffensiveMultiplier(attackerID, defenderID int) (float64, error) {
// 1. 获取预加载的组合实例
attacker, err := c.GetCombination(attackerID)
if err != nil {
return 0, fmt.Errorf("attacker invalid: %w", err)
if !isValidCombinationID(attackerID) {
return 0, fmt.Errorf("attacker invalid: invalid element combination ID: %d", attackerID)
}
defender, err := c.GetCombination(defenderID)
if err != nil {
return 0, fmt.Errorf("defender invalid: %w", err)
if !isValidCombinationID(defenderID) {
return 0, fmt.Errorf("defender invalid: invalid element combination ID: %d", defenderID)
}
// 2. 缓存键(全局唯一)
cacheKey := fmt.Sprintf("a%d_d%d", attackerID, defenderID)
if val, exists := c.offensiveCache[cacheKey]; exists {
return val, nil
}
// 3. 核心计算+缓存
val := c.calculateMultiplier(attacker, defender)
c.offensiveCache[cacheKey] = val
return val, nil
return c.offensiveTable[attackerID][defenderID], nil
}
// calculateMultiplier 核心克制计算逻辑
func (c *ElementCalculator) calculateMultiplier(attacker, defender *ElementCombination) float64 {
// 场景1单→单
if !attacker.IsDual() && !defender.IsDual() {
return c.getMatrixValue(attacker.Primary, defender.Primary)
}
// 场景2单→双
if !attacker.IsDual() {
y1, y2 := defender.Primary, *defender.Secondary
m1 := c.getMatrixValue(attacker.Primary, y1)
m2 := c.getMatrixValue(attacker.Primary, y2)
switch {
case m1 == 2 && m2 == 2:
return 4.0
@@ -333,12 +340,10 @@ func (c *ElementCalculator) calculateMultiplier(attacker, defender *ElementCombi
}
}
// 场景3双→单
if !defender.IsDual() {
return c.calculateDualToSingle(attacker.Primary, *attacker.Secondary, defender.Primary)
}
// 场景4双→双
x1, x2 := attacker.Primary, *attacker.Secondary
y1, y2 := defender.Primary, *defender.Secondary
coeffY1 := c.calculateDualToSingle(x1, x2, y1)
@@ -350,7 +355,6 @@ func (c *ElementCalculator) calculateMultiplier(attacker, defender *ElementCombi
func (c *ElementCalculator) calculateDualToSingle(attacker1, attacker2, defender ElementType) float64 {
k1 := c.getMatrixValue(attacker1, defender)
k2 := c.getMatrixValue(attacker2, defender)
switch {
case k1 == 2 && k2 == 2:
return 4.0
@@ -361,60 +365,49 @@ func (c *ElementCalculator) calculateDualToSingle(attacker1, attacker2, defender
}
}
var Calculator = NewElementCalculator()
// TestAllScenarios 全场景测试(验证预加载和计算逻辑)
func TestAllScenarios() {
// 测试1单→单草→水
m1, _ := Calculator.GetOffensiveMultiplier(1, 2)
fmt.Println("草→水: %.2f预期2.0", m1)
if math.Abs(m1-2.0) > 0.001 {
fmt.Println("测试1失败实际%.2f", m1)
}
// 测试2特殊单→单混沌→虚空
m2, _ := Calculator.GetOffensiveMultiplier(222, 226)
fmt.Println("混沌→虚空: %.2f预期0.0", m2)
if math.Abs(m2-0.0) > 0.001 {
fmt.Println("测试2失败实际%.2f", m2)
}
// 测试3单→双火→冰龙43
m3, _ := Calculator.GetOffensiveMultiplier(3, 43)
fmt.Println("火→冰龙: %.2f预期1.5", m3)
if math.Abs(m3-1.5) > 0.001 {
fmt.Println("测试3失败实际%.2f", m3)
}
// 测试4双→特殊单混沌暗影92→神灵223
m4, _ := Calculator.GetOffensiveMultiplier(92, 223)
fmt.Println("混沌暗影→神灵: %.2f预期1.25", m4)
if math.Abs(m4-1.25) > 0.001 {
fmt.Println("测试4失败实际%.2f", m4)
}
// 测试5双→双虚空邪灵113→混沌远古98
m5, _ := Calculator.GetOffensiveMultiplier(113, 98)
fmt.Println("虚空邪灵→混沌远古: %.2f预期0.875", m5)
if math.Abs(m5-0.875) > 0.001 {
fmt.Println("测试5失败实际%.2f", m5)
}
// 测试6缓存命中
m6, _ := Calculator.GetOffensiveMultiplier(113, 98)
if math.Abs(m6-m5) > 0.001 {
fmt.Println("测试6失败缓存未命中")
}
// 测试7含无效组合电→地面
m7, _ := Calculator.GetOffensiveMultiplier(5, 7)
fmt.Println("电→地面: %.2f预期0.0", m7)
if math.Abs(m7-0.0) > 0.001 {
fmt.Println("测试7失败实际%.2f", m7)
}
// 测试8双属性含无效电战斗→地面
m8, _ := Calculator.GetOffensiveMultiplier(35, 7)
fmt.Println("电战斗→地面: %.2f预期0.25", m8)
if math.Abs(m8-0.25) > 0.001 {

View File

@@ -1,106 +1,149 @@
package rpc
import (
"blazing/common/data/share"
"blazing/cool"
"context"
"fmt"
"log"
config "blazing/modules/config/service"
"github.com/filecoin-project/go-jsonrpc"
"github.com/gogf/gf/v2/util/gconv"
)
// Define the server handler
type ServerHandler struct{}
// 实现踢人
func (*ServerHandler) Kick(_ context.Context, userid uint32) error {
useid1, _ := share.ShareManager.GetUserOnline(userid)
if useid1 == 0 {
return nil
}
cl, ok := cool.GetClientOnly(useid1)
if !ok {
return nil
}
cl.KickPerson(userid) //实现指定服务器踢人
return nil
}
// 注册logic服务器
func (*ServerHandler) RegisterLogic(ctx context.Context, id, port uint32) error {
fmt.Println("注册logic服务器", id, port)
//TODO 待修复滚动更新可能导致的玩家可以同时在旧服务器和新服务器同时在线的bug
revClient, ok := jsonrpc.ExtractReverseClient[cool.ClientHandler](ctx)
if !ok {
return fmt.Errorf("no reverse client")
}
t := config.NewServerService().GetServerID((id))
aa, ok := cool.GetClient(t.OnlineID, t.Port)
if ok && aa != nil { //如果已经存在且这个端口已经被存过
aa.QuitSelf(0)
}
cool.AddClient(100000*id+port, &revClient)
//Refurh()
return nil
}
func CServer() *jsonrpc.RPCServer {
// create a new server instance
rpcServer := jsonrpc.NewServer(jsonrpc.WithReverseClient[cool.ClientHandler](""))
rpcServer.Register("", &ServerHandler{})
return rpcServer
}
var closer jsonrpc.ClientCloser
func StartClient(id, port uint32, callback any) *struct {
Kick func(uint32) error
RegisterLogic func(uint32, uint32) error
} {
//cool.Config.File.Domain = "127.0.0.1"
var rpcaddr = "ws://" + cool.Config.File.Domain + gconv.String(cool.Config.Address) + "/rpc"
closer1, err := jsonrpc.NewMergeClient(context.Background(),
rpcaddr, "", []interface{}{
&RPCClient,
}, nil, jsonrpc.WithClientHandler("", callback),
jsonrpc.WithReconnFun(func() { RPCClient.RegisterLogic(id, port) }),
)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
//if port != 0 { //注册logic
defer RPCClient.RegisterLogic(id, port)
//}
closer = closer1
return &RPCClient
}
// Setup RPCClient with reverse call handler
var RPCClient struct {
Kick func(uint32) error //踢人
RegisterLogic func(uint32, uint32) error
// UserLogin func(int32, int32) error //用户登录事件
// UserLogout func(int32, int32) error //用户登出事件
}
package rpc
import (
"blazing/common/data/share"
"blazing/cool"
"context"
"fmt"
"log"
"time"
config "blazing/modules/config/service"
"github.com/filecoin-project/go-jsonrpc"
"github.com/gogf/gf/v2/util/gconv"
)
// Define the server handler
type ServerHandler struct{}
const kickForwardTimeout = 3 * time.Second
// 实现踢人
func (*ServerHandler) Kick(_ context.Context, userid uint32) error {
useid1, err := share.ShareManager.GetUserOnline(userid)
if err != nil || useid1 == 0 {
// 请求到达时用户已离线,直接视为成功
return nil
}
cl, ok := cool.GetClientOnly(useid1)
if !ok || cl == nil {
// 目标服务器不在线,清理僵尸在线标记并视为成功
_ = share.ShareManager.DeleteUserOnline(userid)
cool.DeleteClientOnly(useid1)
return nil
}
resultCh := make(chan error, 1)
go func() {
resultCh <- cl.KickPerson(userid) // 实现指定服务器踢人
}()
select {
case callErr := <-resultCh:
if callErr == nil {
return nil
}
// 调用失败后兜底:用户若已离线/切服/目标服不在线都算成功
useid2, err2 := share.ShareManager.GetUserOnline(userid)
if err2 != nil || useid2 == 0 || useid2 != useid1 {
return nil
}
if cl2, ok2 := cool.GetClientOnly(useid2); !ok2 || cl2 == nil {
_ = share.ShareManager.DeleteUserOnline(userid)
cool.DeleteClientOnly(useid2)
return nil
}
// 仍在线则返回失败,不按成功处理
return callErr
case <-time.After(kickForwardTimeout):
// 仅防止无限等待;超时不算成功
useid2, err2 := share.ShareManager.GetUserOnline(userid)
if err2 != nil || useid2 == 0 || useid2 != useid1 {
return nil
}
if cl2, ok2 := cool.GetClientOnly(useid2); !ok2 || cl2 == nil {
_ = share.ShareManager.DeleteUserOnline(userid)
cool.DeleteClientOnly(useid2)
return nil
}
return fmt.Errorf("kick timeout, user still online: uid=%d server=%d", userid, useid2)
}
}
// 注册logic服务器
func (*ServerHandler) RegisterLogic(ctx context.Context, id, port uint32) error {
fmt.Println("注册logic服务器", id, port)
//TODO 待修复滚动更新可能导致的玩家可以同时在旧服务器和新服务器同时在线的bug
revClient, ok := jsonrpc.ExtractReverseClient[cool.ClientHandler](ctx)
if !ok {
return fmt.Errorf("no reverse client")
}
t := config.NewServerService().GetServerID((id))
aa, ok := cool.GetClient(t.OnlineID, t.Port)
if ok && aa != nil { //如果已经存在且这个端口已经被存过
aa.QuitSelf(0)
}
cool.AddClient(100000*id+port, &revClient)
//Refurh()
return nil
}
func CServer() *jsonrpc.RPCServer {
// create a new server instance
rpcServer := jsonrpc.NewServer(jsonrpc.WithReverseClient[cool.ClientHandler](""))
rpcServer.Register("", &ServerHandler{})
return rpcServer
}
var closer jsonrpc.ClientCloser
func StartClient(id, port uint32, callback any) *struct {
Kick func(uint32) error
RegisterLogic func(uint32, uint32) error
} {
//cool.Config.File.Domain = "127.0.0.1"
var rpcaddr = "ws://" + cool.Config.File.Domain + gconv.String(cool.Config.Address) + "/rpc"
closer1, err := jsonrpc.NewMergeClient(context.Background(),
rpcaddr, "", []interface{}{
&RPCClient,
}, nil, jsonrpc.WithClientHandler("", callback),
jsonrpc.WithReconnFun(func() { RPCClient.RegisterLogic(id, port) }),
)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
//if port != 0 { //注册logic
defer RPCClient.RegisterLogic(id, port)
//}
closer = closer1
return &RPCClient
}
// Setup RPCClient with reverse call handler
var RPCClient struct {
Kick func(uint32) error //踢人
RegisterLogic func(uint32, uint32) error
// UserLogin func(int32, int32) error //用户登录事件
// UserLogout func(int32, int32) error //用户登出事件
}

View File

@@ -1,24 +1,29 @@
package socket
import (
"blazing/common/socket/codec"
"blazing/cool"
"blazing/logic/service/player"
"blazing/modules/config/service"
"bytes"
"context"
"encoding/binary"
"errors"
"io"
"log"
"os"
"sync/atomic"
"time"
"blazing/cool"
"blazing/logic/service/player"
"blazing/modules/config/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/panjf2000/gnet/v2"
)
const (
minPacketLen = 17
maxPacketLen = 10 * 1024
)
func (s *Server) Boot(serverid, port uint32) error {
// go s.bootws()
s.serverid = serverid
@@ -53,7 +58,6 @@ func (s *Server) Stop() error {
func (s *Server) OnClose(c gnet.Conn, err error) (action gnet.Action) {
defer func() {
if err := recover(); err != nil { // 恢复 panicerr 为 panic 错误值
// 1. 打印错误信息
if t, ok := c.Context().(*player.ClientData); ok {
if t.Player != nil {
if t.Player.Info != nil {
@@ -62,53 +66,35 @@ func (s *Server) OnClose(c gnet.Conn, err error) (action gnet.Action) {
}
}
} else {
cool.Logger.Error(context.TODO(), "OnClose 错误:", cool.Config.ServerInfo.OnlineID, err)
}
}
}()
// 识别 RST 导致的连接中断(错误信息含 "connection reset"
// if err != nil && (strings.Contains(err.Error(), "connection reset") || strings.Contains(err.Error(), "reset by peer")) {
// remoteIP := c.RemoteAddr().(*net.TCPAddr).IP.String()
// log.Printf("RST 攻击检测: 来源 %s, 累计攻击次数 %d", remoteIP)
// // 防护逻辑:临时封禁异常 IP可扩展为 IP 黑名单)
// // go s.tempBlockIP(remoteIP, 5*time.Minute)
// }
//fmt.Println(err, c.RemoteAddr().String(), "断开连接")
atomic.AddInt64(&cool.Connected, -1)
//logging.Infof("conn[%v] disconnected", c.RemoteAddr().String())
v, _ := c.Context().(*player.ClientData)
v.LF.Close()
// v.LF.Close()
//close(v.MsgChan)
if v.Player != nil {
go v.Player.SaveOnDisconnect() //保存玩家数据
if v != nil {
v.Close()
if v.Player != nil {
v.Player.Save() //保存玩家数据
}
}
//}
//关闭连接
return
}
func (s *Server) OnTick() (delay time.Duration, action gnet.Action) {
g.Log().Async().Info(context.Background(), gtime.Now().ISO8601(), "服务器ID", cool.Config.ServerInfo.OnlineID, "链接数", atomic.LoadInt64(&cool.Connected))
if s.quit && atomic.LoadInt64(&cool.Connected) == 0 {
//执行正常退出逻辑
os.Exit(0)
}
return 30 * time.Second, gnet.None
}
func (s *Server) OnBoot(eng gnet.Engine) gnet.Action {
s.eng = eng
service.NewServerService().SetServerID(s.serverid, s.port) //设置当前服务器端口
service.NewServerService().SetServerID(s.serverid, s.port)
return gnet.None
}
@@ -116,59 +102,52 @@ func (s *Server) OnOpen(conn gnet.Conn) (out []byte, action gnet.Action) {
if s.network != "tcp" {
return nil, gnet.Close
}
if conn.Context() == nil {
conn.SetContext(player.NewClientData(conn)) //注入data
conn.SetContext(player.NewClientData(conn))
}
atomic.AddInt64(&cool.Connected, 1)
return nil, gnet.None
}
func (s *Server) OnTraffic(c gnet.Conn) (action gnet.Action) {
defer func() {
if err := recover(); err != nil { // 恢复 panicerr 为 panic 错误值
// 1. 打印错误信息
if err := recover(); err != nil {
if t, ok := c.Context().(*player.ClientData); ok {
if t.Player != nil {
if t.Player.Info != nil {
cool.Logger.Error(context.TODO(), "OnTraffic 错误:", cool.Config.ServerInfo.OnlineID, t.Player.Info.UserID, err)
t.Player.Service.Info.Save(*t.Player.Info)
}
if t.Player != nil && t.Player.Info != nil {
cool.Logger.Error(context.TODO(), "OnTraffic 错误:", cool.Config.ServerInfo.OnlineID, t.Player.Info.UserID, err)
t.Player.Service.Info.Save(*t.Player.Info)
}
}
}
}()
ws := c.Context().(*player.ClientData).Wsmsg
if ws.Tcp { //升级失败时候防止缓冲区溢出
if ws.Tcp {
return s.handleTCP(c)
}
tt, len1 := ws.ReadBufferBytes(c)
if tt == gnet.Close {
readAction, inboundLen := ws.ReadBufferBytes(c)
if readAction == gnet.Close {
return gnet.Close
}
ok, action := ws.Upgrade(c)
if action != gnet.None { //连接断开
state, action := ws.Upgrade(c)
if action != gnet.None {
return action
}
if !ok { //升级失败,说明是tcp连接
ws.Tcp = true
return s.handleTCP(c)
if state == player.UpgradeNeedMoreData {
return gnet.None
}
if state == player.UpgradeUseTCP {
return s.handleTCP(c)
}
if inboundLen > 0 {
if _, err := c.Discard(inboundLen); err != nil {
return gnet.Close
}
ws.ResetInboundMirror()
}
// fmt.Println(ws.Buf.Bytes())
c.Discard(len1)
messages, err := ws.Decode(c)
if err != nil {
@@ -179,91 +158,93 @@ func (s *Server) OnTraffic(c gnet.Conn) (action gnet.Action) {
}
for _, msg := range messages {
s.onevent(c, msg.Payload)
//t.OnEvent(msg.Payload)
if !s.onevent(c, msg.Payload) {
return gnet.Close
}
}
return gnet.None
}
const maxBodyLen = 10 * 1024 // 业务最大包体长度,按需调整
func (s *Server) handleTCP(conn gnet.Conn) (action gnet.Action) {
client := conn.Context().(*player.ClientData)
if s.discorse && !client.IsCrossDomainChecked() {
handled, ready, action := handle(conn)
if action != gnet.None {
return action
}
if !ready {
return gnet.None
}
if handled {
client.MarkCrossDomainChecked()
return gnet.None
}
client.MarkCrossDomainChecked()
}
conn.Context().(*player.ClientData).IsCrossDomain.Do(func() { //跨域检测
handle(conn)
})
// handle(c)
// 先读取4字节的包长度
lenBuf, err := conn.Peek(4)
body, err := s.codec.Decode(conn)
if err != nil {
if errors.Is(err, io.ErrShortBuffer) {
return
if errors.Is(err, codec.ErrIncompletePacket) {
return gnet.None
}
return gnet.Close
}
bodyLen := binary.BigEndian.Uint32(lenBuf)
if bodyLen > maxBodyLen {
if !s.onevent(conn, body) {
return gnet.Close
}
if conn.InboundBuffered() < int(bodyLen) {
return
}
// 提取包体
body, err := conn.Next(int(bodyLen))
if err != nil {
if errors.Is(err, io.ErrShortBuffer) {
return
}
return gnet.Close
}
s.onevent(conn, body)
if conn.InboundBuffered() > 0 {
if err := conn.Wake(nil); err != nil { // wake up the connection manually to avoid missing the leftover data
if err := conn.Wake(nil); err != nil {
return gnet.Close
}
}
return action
}
// CROSS_DOMAIN 定义跨域策略文件内容
const CROSS_DOMAIN = "<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy><cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\x00"
// TEXT 定义跨域请求的文本格式
const TEXT = "<policy-file-request/>\x00"
func handle(c gnet.Conn) {
func handle(c gnet.Conn) (handled bool, ready bool, action gnet.Action) {
probeLen := c.InboundBuffered()
if probeLen == 0 {
return false, false, gnet.None
}
if probeLen > len(TEXT) {
probeLen = len(TEXT)
}
// 读取数据并检查是否为跨域请求
data, err := c.Peek(len(TEXT))
data, err := c.Peek(probeLen)
if err != nil {
log.Printf("Error reading cross-domain request: %v", err)
return
return false, false, gnet.Close
}
if string(data) == TEXT { //判断是否是跨域请求
//log.Printf("Received cross-domain request from %s", c.RemoteAddr())
// 处理跨域请求
c.Write([]byte(CROSS_DOMAIN))
c.Discard(len(TEXT))
return
if !bytes.Equal(data, []byte(TEXT[:probeLen])) {
return false, true, gnet.None
}
//return
if probeLen < len(TEXT) {
return false, false, gnet.None
}
if _, err := c.Write([]byte(CROSS_DOMAIN)); err != nil {
return false, true, gnet.Close
}
if _, err := c.Discard(len(TEXT)); err != nil {
return false, true, gnet.Close
}
return true, true, gnet.None
}
func (s *Server) onevent(c gnet.Conn, v []byte) {
func (s *Server) onevent(c gnet.Conn, v []byte) bool {
if !isValidPacket(v) {
return false
}
if t, ok := c.Context().(*player.ClientData); ok {
t.PushEvent(v, s.workerPool.Submit)
}
return true
}
func isValidPacket(v []byte) bool {
if len(v) < minPacketLen || len(v) > maxPacketLen {
return false
}
return binary.BigEndian.Uint32(v[0:4]) == uint32(len(v))
}