This commit is contained in:
2025-07-04 23:41:19 +08:00
parent 7c2c32a31a
commit f0fd00d706
27 changed files with 1206 additions and 163 deletions

View File

@@ -9,7 +9,7 @@ import (
"github.com/judwhite/go-svc"
"runtime/debug"
"scene/config"
"scene/grpc_server"
"scene/grpc_server/server"
"sync"
)
@@ -41,7 +41,7 @@ func (p *Program) Start() error {
}()
discover.Listen()
p.server = grpc_server.NewServer(config.Get().Serve.Grpc.TTL)
p.server = server.NewServer(config.Get().Serve.Grpc.TTL)
p.server.Init(config.Get().Serve.Grpc.Address, config.Get().Serve.Grpc.Port)
return nil

View File

@@ -1,19 +1,30 @@
package grpc_server
package server
import (
"common/log"
"common/proto/gen/common"
"common/proto/gen/grpc_pb"
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"scene/instance"
"sync"
)
func (s *Server) Enter(ctx context.Context, req *grpc_pb.EnterReq) (*grpc_pb.EnterResp, error) {
log.Infof("enter 触发 %v", req.SID)
return nil, status.Errorf(codes.Unimplemented, "")
var i *instance.Instance
if len(instance.Mgr.GetAll()) == 0 {
i = instance.NewScene(s.SID, int(req.InstanceID))
i.Start(s.EtcdTTL)
} else {
for _, v := range instance.Mgr.GetAll() {
i = v
break
}
}
i.EventIn <- req
return &grpc_pb.EnterResp{
SceneSID: s.SID,
UniqueNo: i.UniqueNo,
}, nil
}
func (s *Server) Action(server grpc_pb.Scene_ActionServer) error {

View File

@@ -1,4 +1,4 @@
package grpc_server
package server
import (
"common/discover/common"

View File

@@ -0,0 +1,65 @@
package stream_client
import (
"common/log"
"common/net/grpc/service"
"context"
"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
)
var gatewayServerM map[int64]map[GatewayFun]grpc.ClientStream // map[sid]map[方法名]流连接
type GatewayFun int
const (
FunToClient GatewayFun = iota
)
func init() {
gatewayServerM = make(map[int64]map[GatewayFun]grpc.ClientStream)
}
func FindGatewayBySID(sid int64, fun GatewayFun) (grpc.ClientStream, error) {
g := gatewayServerM[sid]
if g == nil {
g = make(map[GatewayFun]grpc.ClientStream)
gatewayServerM[sid] = g
}
gatewayLink := g[fun]
if gatewayLink == nil {
gatewayClient, err := service.GatewayNewClient(sid)
if err != nil {
log.Errorf("cannot find gatewayClient: %v", err)
return nil, err
}
var link grpc.ClientStream
switch fun {
case FunToClient:
link, err = gatewayClient.ToClient(context.Background())
}
if err != nil {
log.Errorf("FindGatewayBySID %v err: %v, sid: %v", fun, err, sid)
return nil, err
}
g[fun] = link
gatewayLink = link
}
return gatewayLink, nil
}
func SendMessageToGateway(sid int64, fun GatewayFun, msg proto.Message, re ...bool) error {
stream, err := FindGatewayBySID(sid, fun)
if err != nil {
return err
}
if err = stream.SendMsg(msg); err != nil {
if re == nil || !re[0] {
_ = stream.CloseSend()
delete(gatewayServerM[sid], fun)
return SendMessageToGateway(sid, fun, msg, true)
}
return err
}
return nil
}

View File

@@ -1,9 +0,0 @@
package instance
import "google.golang.org/protobuf/proto"
type Msg2Client struct {
SID string
UID int
Msg proto.Message
}

View File

@@ -3,11 +3,16 @@ package instance
import (
"common/discover"
"common/log"
"common/proto/gen/cs"
"common/proto/gen/grpc_pb"
"common/utils"
"context"
"fmt"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"runtime/debug"
"scene/grpc_server/stream_client"
"scene/npc"
"sync"
"time"
)
@@ -15,27 +20,27 @@ import (
// Instance 场景类
type Instance struct {
wg sync.WaitGroup
players map[int]*PlayerNode // 存储所有玩家节点 [uid]
ctx context.Context // 停止指令
cancel context.CancelFunc // 停止函数
players map[int]*npc.PlayerNode // 存储所有玩家节点 [uid]
ctx context.Context // 停止指令
cancel context.CancelFunc // 停止函数
logger *zap.SugaredLogger // 日志
SID int64 // 服务ID
InstanceID int // 副本ID
UniqueNo int64 // 唯一编号
EventIn chan proto.Message // 消息入口
EventOut chan *Msg2Client // 消息出口
}
// NewScene 初始化场景
func NewScene(sid int64, instanceID int) *Instance {
s := &Instance{
players: make(map[int]*PlayerNode),
players: make(map[int]*npc.PlayerNode),
SID: sid,
InstanceID: instanceID,
UniqueNo: utils.SnowflakeInstance().Generate().Int64(),
EventIn: make(chan proto.Message),
EventOut: make(chan *Msg2Client),
}
s.logger = log.GetLogger().Named(fmt.Sprintf("%v:%v", s.InstanceID, s.UniqueNo))
s.ctx, s.cancel = context.WithCancel(context.Background())
return s
}
@@ -46,20 +51,20 @@ func (i *Instance) Start(ttl int64) {
defer i.wg.Done()
defer func() {
if err := recover(); err != nil {
log.Infof("instance err: %v, uniqueNo: %v", err, i.UniqueNo)
i.logger.Infof("instance err: %v", err)
debug.PrintStack()
}
}()
Mgr.Add(i.UniqueNo, i)
defer func() {
log.Infof("instance destroy: %v, %v", i.InstanceID, i.UniqueNo)
i.logger.Infof("instance destroy")
discover.UnRegisterInstance(i.UniqueNo)
close(i.EventIn)
Mgr.Delete(i.UniqueNo)
}()
if err := discover.RegisterInstance(i.SID, i.InstanceID, i.UniqueNo, ttl); err != nil {
log.Errorf("register discover error")
i.logger.Errorf("register discover error")
return
}
// 场景心跳
@@ -75,23 +80,46 @@ func (i *Instance) Start(ttl int64) {
}
}
}()
log.Infof("new scene start: %v, %v", i.InstanceID, i.UniqueNo)
i.logger.Infof("new scene start")
}
// 网络帧
func (i *Instance) onEvent(e proto.Message) {
switch v := e.(type) {
case *grpc_pb.EnterReq:
i.players[int(v.UID)] = npc.NewPlayerNode(v.GatewaySID, int(v.UID))
case *grpc_pb.ActionReq:
if node, ok := i.players[int(v.UID)]; ok {
node.addAction(v)
node.AddAction(v)
}
}
}
// 逻辑帧
func (i *Instance) onLogic() {
positionUpdate := make([]*cs.PositionInfo, 0)
sid := int64(0)
// 优先处理移动指令
for _, node := range i.players {
node.logicMove()
if node.LogicMove() {
positionUpdate = append(positionUpdate, &cs.PositionInfo{
UID: int32(node.UID),
X: node.Position[0],
Y: node.Position[1],
})
sid = node.GatewaySID
}
}
if len(positionUpdate) > 0 {
payload, _ := proto.Marshal(&cs.S2C_Position{
Info: positionUpdate,
})
if err := stream_client.SendMessageToGateway(sid, stream_client.FunToClient, &grpc_pb.ToClientReq{
UID: -1,
MessageID: int32(cs.MessageID_MESSAGE_ID_POSITION),
Payload: payload,
}); err != nil {
i.logger.Errorf("send action err: %v", err)
}
}
}

76
Server/scene/npc/npc.go Normal file
View File

@@ -0,0 +1,76 @@
package npc
import (
"common/proto/gen/grpc_pb"
)
// NPCNode 定义NPC节点
type NPCNode struct {
UID int // 用户ID
GatewaySID int64 // 网关服务ID
Position [2]float64 // 二维坐标 [x, y]
MoveCross int8 // 移动十字1 上 2 下 4 左 8 右
Action []*grpc_pb.ActionReq // 其他操作
}
func NewNPCNode(gatewaySID int64, uid int) *NPCNode {
return &NPCNode{
UID: uid,
GatewaySID: gatewaySID,
Position: [2]float64{0, 0},
MoveCross: 0,
Action: make([]*grpc_pb.ActionReq, 0),
}
}
// 逻辑帧-移动
func (p *NPCNode) logicMove() bool {
if p.MoveCross&DirUp != 0 && p.MoveCross&DirDown != 0 {
p.MoveCross &^= DirUp | DirDown
}
if p.MoveCross&DirLeft != 0 && p.MoveCross&DirRight != 0 {
p.MoveCross &^= DirLeft | DirRight
}
var moveX, moveY float64
if p.MoveCross&DirUp != 0 {
moveY += 1
}
if p.MoveCross&DirDown != 0 {
moveY -= 1
}
if p.MoveCross&DirLeft != 0 {
moveX -= 1
}
if p.MoveCross&DirRight != 0 {
moveX += 1
}
// 没有移动
if moveX == 0 && moveY == 0 {
return false
}
if moveX != 0 && moveY != 0 {
const diagonalFactor = 0.7071
moveX *= diagonalFactor
moveY *= diagonalFactor
}
speed := 10.0
p.Position[0] += moveX * speed
p.Position[1] += moveY * speed
if p.Position[0] < 0 {
p.Position[0] = 0
}
if p.Position[0] > 400 {
p.Position[0] = 400
}
if p.Position[1] < 0 {
p.Position[1] = 0
}
if p.Position[1] > 400 {
p.Position[1] = 400
}
return true
}

View File

@@ -1,4 +1,4 @@
package instance
package npc
import (
"common/proto/gen/grpc_pb"
@@ -6,14 +6,26 @@ import (
// PlayerNode 定义玩家节点结构体
type PlayerNode struct {
ID int
UID int // 用户ID
GatewaySID int64 // 网关服务ID
Position [2]float64 // 二维坐标 [x, y]
MoveCross int8 // 移动十字1 上 2 下 4 左 8 右
Action []*grpc_pb.ActionReq // 其他操作
}
// 将指令存储到玩家身上
func (p *PlayerNode) addAction(e *grpc_pb.ActionReq) {
func NewPlayerNode(gatewaySID int64, uid int) *PlayerNode {
return &PlayerNode{
UID: uid,
GatewaySID: gatewaySID,
Position: [2]float64{0, 0},
MoveCross: 0,
Action: make([]*grpc_pb.ActionReq, 0),
}
}
// AddAction 将指令存储到玩家身上
func (p *PlayerNode) AddAction(e *grpc_pb.ActionReq) {
if e.Action < 16 {
p.MoveCross = int8(e.Action)
} else {
@@ -28,8 +40,8 @@ const (
DirRight // 8 (00001000)
)
// 逻辑帧-移动
func (p *PlayerNode) logicMove() {
// LogicMove 逻辑帧-移动
func (p *PlayerNode) LogicMove() bool {
if p.MoveCross&DirUp != 0 && p.MoveCross&DirDown != 0 {
p.MoveCross &^= DirUp | DirDown
}
@@ -50,6 +62,10 @@ func (p *PlayerNode) logicMove() {
if p.MoveCross&DirRight != 0 {
moveX += 1
}
// 没有移动
if moveX == 0 && moveY == 0 {
return false
}
if moveX != 0 && moveY != 0 {
const diagonalFactor = 0.7071
@@ -57,7 +73,20 @@ func (p *PlayerNode) logicMove() {
moveY *= diagonalFactor
}
speed := 100.0
speed := 10.0
p.Position[0] += moveX * speed
p.Position[1] += moveY * speed
if p.Position[0] < 0 {
p.Position[0] = 0
}
if p.Position[0] > 400 {
p.Position[0] = 400
}
if p.Position[1] < 0 {
p.Position[1] = 0
}
if p.Position[1] > 400 {
p.Position[1] = 400
}
return true
}