game_sync/gamesrv/clawdoll/scene_clawdoll.go

574 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package clawdoll
import (
"container/list"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"mongo.games.com/game/common"
rule "mongo.games.com/game/gamerule/clawdoll"
"mongo.games.com/game/gamesrv/base"
"mongo.games.com/game/proto"
"mongo.games.com/game/protocol/clawdoll"
"mongo.games.com/game/protocol/machine"
"mongo.games.com/game/protocol/webapi"
"mongo.games.com/goserver/core/basic"
"mongo.games.com/goserver/core/logger"
"mongo.games.com/goserver/core/netlib"
"mongo.games.com/goserver/core/task"
"mongo.games.com/goserver/core/timer"
"mongo.games.com/goserver/srvlib"
"net/http"
"net/url"
"time"
)
type PlayerData struct {
SnId int32
Head int32 //头像框
VIP int32 //VIP帐号 等级
Name string //名字
Sex int32 //性别
IsRob bool
Coin int64
gainCoin int64 //本局赢的金币
taxCoin int64 //本局税收
isBilled bool //是否结算
CurIsWin int64 //当局输赢 负数:输 正数:赢
InviterId int32 //邀请人Id
BeUnderAgentCode string //隶属经销商(推广人)
IsPlayerFirst bool
Platform string //平台
Channel string //渠道信息
PackageID string //推广包标识 对应客户端的packagetag
flag int
}
type SceneEx struct {
*base.Scene // 场景
logic *rule.Logic //
players map[int32]*PlayerEx // 玩家信息
PlayerBackup map[int32]*PlayerData // 本局离场玩家数据备份
seats []*PlayerEx // 本局游戏中的玩家状态数据
waitPlayers *list.List
playingSnid int32 // 正在玩的玩家snid
RoundId int // 局数,第几局
robotNum int // 参与游戏的机器人数量
logid string
machineId int //娃娃机ID
machineConn *netlib.Session //娃娃机链接
machineStatus int32 //娃娃机链接状态 0:离线 1:在线
payCoinTimeHandle timer.TimerHandle //上分投币托管handle
grabTimerHandle timer.TimerHandle //下抓托管handle
}
// 游戏是否能开始
func (this *SceneEx) CanStart() bool {
//人数>=1自动开始
if len(this.players) >= 1 && (this.GetRealPlayerNum() >= 1 || this.IsPreCreateScene() || this.IsHasPlaying()) {
return true
}
return false
}
// 从房间删除玩家
func (this *SceneEx) delPlayer(p *base.Player) {
if p, exist := this.players[p.SnId]; exist {
//this.seats[p.GetPos()] = nil
delete(this.players, p.SnId)
this.RemoveWaitPlayer(p.SnId)
}
}
// 广播玩家进入
func (this *SceneEx) BroadcastPlayerEnter(p *base.Player, reason int) {
pack := &clawdoll.SCCLAWDOLLPlayerEnter{}
pack.Data = &clawdoll.CLAWDOLLPlayerDigestInfo{}
pack.Data.SnId = p.GetSnId()
pack.Data.Head = p.Head
pack.Data.HeadUrl = p.HeadUrl
pack.Data.Name = p.Name
if playerEx, ok := p.ExtraData.(*PlayerEx); ok {
pack.Data.Stat = playerEx.clawDollState
}
proto.SetDefaults(pack)
this.Broadcast(int(clawdoll.CLAWDOLLPacketID_PACKET_SC_PlayerEnter), pack, p.GetSid())
}
// 广播玩家离开
func (this *SceneEx) BroadcastPlayerLeave(p *base.Player, reason int) {
scLeavePack := &clawdoll.SCCLAWDOLLPlayerLeave{
SnId: proto.Int32(p.SnId),
}
proto.SetDefaults(scLeavePack)
this.Broadcast(int(clawdoll.CLAWDOLLPacketID_PACKET_SC_PlayerLeave), scLeavePack, p.GetSid())
}
// 玩家进入事件
func (this *SceneEx) OnPlayerEnter(p *base.Player, reason int) {
this.BroadcastPlayerEnter(p, reason)
machineId := this.GetDBGameFree().GetId() % 6080000
machineInfo := this.GetMachineServerInfo(machineId, p.Platform)
if machineInfo == nil {
return
}
if this.GetPlayerNum() >= 1 {
logger.Logger.Trace("Clawdoll (*SceneEx) OnPlayerEnter, GetPlayerNum = ", this.GetPlayerNum())
// 发送http Get请求 恢复直播间流
//operateTask(this, 2, rule.Zego_ResumeRTCStream, p.Platform)
}
}
// 玩家离开事件
func (this *SceneEx) OnPlayerLeave(p *base.Player, reason int) {
this.delPlayer(p)
this.BroadcastPlayerLeave(p, reason)
machineId := this.GetDBGameFree().GetId() % 6080000
machineInfo := this.GetMachineServerInfo(machineId, p.Platform)
if machineInfo == nil {
return
}
if len(this.players) <= 0 {
logger.Logger.Trace("Clawdoll (*SceneEx) OnPlayerLeave, cur player num = ", len(this.players))
// 发送http Get请求 关闭直播间流
//operateTask(this, 2, rule.Zego_ForbidRTCStream, p.Platform)
}
}
func (this *SceneEx) SceneDestroy(force bool) {
//销毁房间
this.Scene.Destroy(force)
}
func (e *SceneEx) playerOpPack(snId int32, opcode int, opRetCode clawdoll.OpResultCode, params []int64) *clawdoll.SCCLAWDOLLOp {
pack := &clawdoll.SCCLAWDOLLOp{
SnId: proto.Int32(snId),
OpCode: proto.Int32(int32(opcode)),
Params: params,
OpRetCode: opRetCode,
}
proto.SetDefaults(pack)
return pack
}
// OnPlayerSCOp 发送玩家操作情况
func (e *SceneEx) OnPlayerSCOp(p *base.Player, opcode int, opRetCode clawdoll.OpResultCode, params []int64) {
pack := e.playerOpPack(p.SnId, opcode, opRetCode, params)
p.SendToClient(int(clawdoll.CLAWDOLLPacketID_PACKET_SC_PLAYEROP), pack)
logger.Logger.Tracef("OnPlayerSCOp %s", pack)
}
// 房间信息打包
func (this *SceneEx) ClawdollCreateRoomInfoPacket(s *base.Scene, p *base.Player) interface{} {
pack := &clawdoll.SCCLAWDOLLRoomInfo{
RoomId: proto.Int(s.GetSceneId()),
GameId: proto.Int(s.GetGameId()),
RoomMode: proto.Int(s.GetSceneMode()),
Params: common.CopySliceInt64ToInt32(s.Params),
State: proto.Int(s.GetSceneState().GetState()),
TimeOut: proto.Int(s.GetSceneState().GetTimeout(s)),
TotalPlayer: proto.Int(len(this.players)),
RoundId: proto.Int(this.RoundId),
ParamsEx: nil,
GameFreeId: 0,
BaseScore: proto.Int32(this.GetBaseScore()),
}
// 玩家信息
for _, playerEx := range this.players {
if p.SnId == playerEx.SnId {
pd := &clawdoll.CLAWDOLLPlayerData{
SnId: proto.Int32(playerEx.SnId),
Name: proto.String(playerEx.Name),
Head: proto.Int32(playerEx.Head),
Sex: proto.Int32(playerEx.Sex),
Coin: proto.Int64(playerEx.Coin),
Flag: proto.Int(playerEx.GetFlag()),
HeadOutLine: proto.Int32(playerEx.HeadOutLine),
VIP: proto.Int32(playerEx.VIP),
WinCoin: proto.Int64(playerEx.gainCoin),
ClawDollState: proto.Int32(playerEx.clawDollState),
}
pack.Players = append(pack.Players, pd)
}
}
proto.SetDefaults(pack)
if p != nil {
p.SyncFlag()
}
return pack
}
func NewClawdollSceneData(s *base.Scene) *SceneEx {
sceneEx := &SceneEx{
Scene: s,
logic: new(rule.Logic),
players: make(map[int32]*PlayerEx),
PlayerBackup: make(map[int32]*PlayerData),
waitPlayers: list.New(),
}
return sceneEx
}
func (this *SceneEx) init() bool {
this.Clear()
return true
}
// 检查上分投币是否合法
func (this *SceneEx) CheckPayCoinOp(p *PlayerEx) bool {
if p == nil {
return false
}
if p.SnId == this.playingSnid {
return true
}
return false
}
// 检查移动是否合法
func (this *SceneEx) CheckMoveOp(p *PlayerEx) bool {
if p == nil {
return false
}
if p.SnId == this.playingSnid {
return true
}
return false
}
// 下抓是否合法
func (this *SceneEx) CheckGrapOp(p *PlayerEx) bool {
if p == nil {
return false
}
if p.SnId == this.playingSnid {
return true
}
return false
}
func (this *SceneEx) Clear() {
this.robotNum = 0
this.PlayerBackup = make(map[int32]*PlayerData)
this.RoundId = 0
for e := this.waitPlayers.Front(); e != nil; e = e.Next() {
if e != nil {
p := e.Value.(*PlayerEx)
p.Clear(0)
}
}
}
// 是否有玩家正在玩
func (this *SceneEx) IsHasPlaying() bool {
if this.playingSnid == 0 {
return false
}
return true
}
// 等待下一个玩家
func (this *SceneEx) ReStartGame() {
logger.Logger.Trace("(*SceneEx) ReStartGame, playingSnid = ", this.playingSnid)
this.playingSnid = 0
}
func (this *SceneEx) BackupPlayer(p *PlayerEx, isBilled bool) {
this.PlayerBackup[p.SnId] = &PlayerData{
SnId: p.SnId,
gainCoin: p.gainCoin,
taxCoin: p.taxCoin,
isBilled: isBilled,
IsRob: p.IsRob,
Coin: p.Coin,
Head: p.Head,
flag: p.GetFlag(),
Platform: p.Platform,
Channel: p.Channel,
PackageID: p.PackageID,
CurIsWin: p.CurIsWin,
Name: p.Name,
Sex: p.Sex,
VIP: p.VIP,
InviterId: p.InviterId,
IsPlayerFirst: this.IsPlayerFirst(p.Player),
BeUnderAgentCode: p.BeUnderAgentCode,
}
}
func (this *SceneEx) GetPlayGrabType(player *PlayerEx) int32 {
if player == nil {
return rule.ClawWeak
}
if this.RoundId/2 == 0 && this.RoundId != 0 {
return rule.ClawStrong
}
return rule.ClawWeak
}
func (this *SceneEx) AddWaitPlayer(player *PlayerEx) {
if player == nil {
return
}
this.waitPlayers.PushBack(player)
}
func (this *SceneEx) RemoveWaitPlayer(SnId int32) bool {
l := this.waitPlayers
for e := l.Front(); e != nil; e = e.Next() {
if p := e.Value.(*PlayerEx); p.SnId == SnId {
this.waitPlayers.Remove(e)
return true
}
}
return false
}
func (this *SceneEx) GetPlayingEx() *PlayerEx {
if this.playingSnid == 0 {
return nil
}
return this.players[this.playingSnid]
}
func (this *SceneEx) SetPlayingState(state int32) {
if this.playingSnid == 0 {
return
}
playerEx := this.players[this.playingSnid]
if playerEx != nil {
oldState := playerEx.GetClawState()
if oldState != state {
if oldState == rule.ClawDollPlayerStateWait && (state >= rule.ClawDollPlayerStateStart && state <= rule.ClawDollPlayerWaitPayCoin) {
ClawdollBroadcastPlayingInfo(this.Scene)
}
if state == rule.ClawDollPlayerStateWait && (oldState >= rule.ClawDollPlayerStateStart && oldState <= rule.ClawDollPlayerWaitPayCoin) {
ClawdollBroadcastPlayingInfo(this.Scene)
}
}
playerEx.SetClawState(state)
}
}
// 时间到 系统开始下抓
func (this *SceneEx) TimeOutPlayGrab() bool {
playerEx := this.players[this.playingSnid]
if playerEx != nil {
grapType := this.GetPlayGrabType(playerEx)
this.OnPlayerSMGrabOp(this.playingSnid, int32(this.machineId), grapType)
}
return true
}
// OnPlayerSMGrabOp 下抓 //1-弱力抓 2 -强力抓 3-必出抓
func (this *SceneEx) OnPlayerSMGrabOp(SnId, Id, GrabType int32) {
pack := &machine.SMDollMachineGrab{
Snid: proto.Int32(SnId),
Id: proto.Int32(Id),
TypeId: proto.Int32(GrabType),
}
this.SendToMachine(int(machine.DollMachinePacketID_PACKET_SMDollMachineGrab), pack)
}
// OnPlayerSCOp 发送玩家操作情况 1-前 2-后 3-左 4-右 5-投币
func (this *SceneEx) OnPlayerSMPerateOp(SnId, Id, Perate int32) {
pack := &machine.SMDollMachineoPerate{
Snid: proto.Int32(SnId),
Id: proto.Int32(Id),
Perate: proto.Int32(Perate),
}
this.SendToMachine(int(machine.DollMachinePacketID_PACKET_SMDollMachinePerate), pack)
}
// 向娃娃机主机发送消息
func (this *SceneEx) SendToMachine(pid int, msg interface{}) {
this.machineConn = srvlib.ServerSessionMgrSington.GetSession(1, 10, 1001)
if this.machineConn != nil {
this.machineConn.Send(pid, msg)
} else {
logger.Logger.Error("MachineConn is nil !")
}
}
// 下抓托管handle
func (this *SceneEx) GrabTimerHandle() {
if this.grabTimerHandle != timer.TimerHandle(0) {
timer.StopTimer(this.grabTimerHandle)
this.grabTimerHandle = timer.TimerHandle(0)
}
this.grabTimerHandle, _ = timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
logger.Logger.Tracef("ClawDoll SceneEx GrabTimerHandle: TimeOut: %v", rule.ClawDollSceneGrapTimeOut)
this.TimeOutPlayGrab()
return true
}), nil, rule.ClawDollSceneGrapTimeOut, 1)
}
// 上分投币托管handle
func (this *SceneEx) PayCoinTimeHandle() {
if this.payCoinTimeHandle != timer.TimerHandle(0) {
timer.StopTimer(this.payCoinTimeHandle)
this.payCoinTimeHandle = timer.TimerHandle(0)
}
this.payCoinTimeHandle, _ = timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
logger.Logger.Tracef("ClawDoll SceneEx PayCoinTimeHandle: TimeOut: %v", rule.ClawDollScenePayCoinTimeOut)
return true
}), nil, rule.ClawDollScenePayCoinTimeOut, 1)
}
// actionType : 1 禁止推流 ForbidRTCStream
// actionType : 2 恢复推流 ResumeRTCStream
func operateTask(sceneEx *SceneEx, times int, actionType int, platform string) {
machineId := sceneEx.GetDBGameFree().GetId() % 6080000
machineInfo := sceneEx.GetMachineServerInfo(machineId, platform)
if machineInfo == nil {
logger.Logger.Errorf("ZegoRTCStreamAction machineId: %v, platform: %v", machineId, platform)
return
}
actionTypeStr := "NoneAction"
if actionType == rule.Zego_ForbidRTCStream {
actionTypeStr = "ForbidRTCStream"
} else {
actionTypeStr = "ResumeRTCStream"
}
logger.Logger.Tracef("ZegoRTCStreamAction: actionTypeStr: %v, machineId: %v, machineInfo: %v", actionTypeStr, machineId, machineInfo)
var resp rule.ZegoForbidRTCResp
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
resp = ZegoRTCStreamAction(actionTypeStr, machineInfo)
return nil
}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
if resp.Error == 0 && resp.Code == 0 && resp.Message == "ok" {
} else {
logger.Logger.Errorf("ZegoForbidRTCResp Code: %v, Error: %v, Message: %v", resp.Code, resp.Error, resp.Message)
if times > 0 {
timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
operateTask(sceneEx, times-1, actionType, platform)
return true
}), nil, time.Second*5, 1)
}
}
}), "ZegoRTCStream_Action").Start()
}
func ZegoRTCStreamAction(Action string, machineInfo *webapi.MachineInfo) rule.ZegoForbidRTCResp {
var zegoForbidRTCResp = rule.ZegoForbidRTCResp{
Error: 1,
}
timestamp := time.Now().Unix()
queryParams := url.Values{}
queryParams.Set("StreamId", "test")
queryParams.Set("Sequence", fmt.Sprintf("%d", timestamp))
// 生成16进制随机字符串(16位)
nonce := make([]byte, 8)
rand.Read(nonce)
hexNonce := hex.EncodeToString(nonce)
// 生成签名
signature := generateSignature(uint32(machineInfo.AppId), machineInfo.ServerSecret, hexNonce, timestamp)
authParams := url.Values{}
authParams.Set("AppId", fmt.Sprintf("%d", uint32(machineInfo.AppId)))
//公共参数中的随机数和生成签名的随机数要一致
authParams.Set("SignatureNonce", hexNonce)
authParams.Set("SignatureVersion", "2.0")
//公共参数中的时间戳和生成签名的时间戳要一致
authParams.Set("Timestamp", fmt.Sprintf("%d", timestamp))
authParams.Set("Signature", signature)
//authParams.Set("IsTest", "true")
// rtc-api.zego.im 表示使用的产品是云通讯产品包括了实时音视频Express Video、实时音频Express Audio、低延迟直播L3
addr := fmt.Sprintf("https://rtc-api.zego.im/?Action=%s&%s&%s", Action, authParams.Encode(), queryParams.Encode())
logger.Logger.Tracef("ZegoRTCStreamAction Get addr: %+v", addr)
rsp, err := http.Get(addr)
if err != nil {
logger.Logger.Errorf("ZegoRTCStreamAction Get err %v", err)
return zegoForbidRTCResp
}
defer rsp.Body.Close()
body, err := ioutil.ReadAll(rsp.Body)
if err != nil {
logger.Logger.Errorf("ZegoRTCStreamAction ioutil.ReadAll err %v", err)
return zegoForbidRTCResp
}
logger.Logger.Tracef("ZegoRTCStreamAction Action: %+v, body: %+v", Action, string(body))
err = json.Unmarshal(body, &zegoForbidRTCResp)
if err != nil {
zegoForbidRTCResp.Error = 5
logger.Logger.Errorf("ZegoRTCStreamAction %v %v", zegoForbidRTCResp, err.Error())
return zegoForbidRTCResp
}
zegoForbidRTCResp.Error = 0
return zegoForbidRTCResp
}
// 生成签名
// Signature=md5(AppId + SignatureNonce + ServerSecret + Timestamp)
func generateSignature(appId uint32, serverSecret, signatureNonce string, timeStamp int64) string {
data := fmt.Sprintf("%d%s%s%d", appId, signatureNonce, serverSecret, timeStamp)
h := md5.New()
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}