From 14fb8e94b29de24a28e1b647476d0484ab92175f Mon Sep 17 00:00:00 2001 From: genxium Date: Mon, 26 Sep 2022 23:09:18 +0800 Subject: [PATCH 01/13] Preparation of server-side collision calc. --- battle_srv/models/barrier.go | 1 - battle_srv/models/bullet.go | 19 - battle_srv/models/pb_type_convert.go | 111 - battle_srv/models/pumpkin.go | 14 - battle_srv/models/room.go | 537 +--- battle_srv/models/room_heap_manager.go | 7 +- battle_srv/models/speed_shoe.go | 17 - battle_srv/models/tiled_map.go | 71 +- battle_srv/models/trap.go | 39 - battle_srv/models/treasure.go | 18 - .../pb_output/room_downsync_frame.pb.go | 1061 +------- .../pbfiles/room_downsync_frame.proto | 62 +- ...om_downsync_frame_proto_bundle.forcemsg.js | 2289 +---------------- 13 files changed, 270 insertions(+), 3976 deletions(-) delete mode 100644 battle_srv/models/bullet.go delete mode 100644 battle_srv/models/pumpkin.go delete mode 100644 battle_srv/models/speed_shoe.go delete mode 100644 battle_srv/models/trap.go delete mode 100644 battle_srv/models/treasure.go diff --git a/battle_srv/models/barrier.go b/battle_srv/models/barrier.go index 9b3a73c..81b78d5 100644 --- a/battle_srv/models/barrier.go +++ b/battle_srv/models/barrier.go @@ -7,7 +7,6 @@ import ( type Barrier struct { X float64 Y float64 - Type uint32 Boundary *Polygon2D CollidableBody *box2d.B2Body } diff --git a/battle_srv/models/bullet.go b/battle_srv/models/bullet.go deleted file mode 100644 index 6936d5c..0000000 --- a/battle_srv/models/bullet.go +++ /dev/null @@ -1,19 +0,0 @@ -package models - -import ( - "github.com/ByteArena/box2d" -) - -type Bullet struct { - LocalIdInBattle int32 `json:"-"` - LinearSpeed float64 `json:"-"` - X float64 `json:"-"` - Y float64 `json:"-"` - Removed bool `json:"-"` - Dir *Direction `json:"-"` - StartAtPoint *Vec2D `json:"-"` - EndAtPoint *Vec2D `json:"-"` - DamageBoundary *Polygon2D `json:"-"` - CollidableBody *box2d.B2Body `json:"-"` - RemovedAtFrameId int32 `json:"-"` -} diff --git a/battle_srv/models/pb_type_convert.go b/battle_srv/models/pb_type_convert.go index 73360cb..2b64bfd 100644 --- a/battle_srv/models/pb_type_convert.go +++ b/battle_srv/models/pb_type_convert.go @@ -90,114 +90,3 @@ func toPbPlayers(modelInstances map[int32]*Player) map[int32]*pb.Player { return toRet } - -func toPbTreasures(modelInstances map[int32]*Treasure) map[int32]*pb.Treasure { - toRet := make(map[int32]*pb.Treasure, 0) - if nil == modelInstances { - return toRet - } - - for k, last := range modelInstances { - toRet[k] = &pb.Treasure{ - Id: last.Id, - LocalIdInBattle: last.LocalIdInBattle, - Score: last.Score, - X: last.X, - Y: last.Y, - Removed: last.Removed, - Type: last.Type, - } - } - - return toRet -} - -func toPbTraps(modelInstances map[int32]*Trap) map[int32]*pb.Trap { - toRet := make(map[int32]*pb.Trap, 0) - if nil == modelInstances { - return toRet - } - - for k, last := range modelInstances { - toRet[k] = &pb.Trap{ - Id: last.Id, - LocalIdInBattle: last.LocalIdInBattle, - X: last.X, - Y: last.Y, - Removed: last.Removed, - Type: last.Type, - } - } - - return toRet -} - -func toPbBullets(modelInstances map[int32]*Bullet) map[int32]*pb.Bullet { - toRet := make(map[int32]*pb.Bullet, 0) - if nil == modelInstances { - return toRet - } - - for k, last := range modelInstances { - if nil == last.StartAtPoint || nil == last.EndAtPoint { - continue - } - toRet[k] = &pb.Bullet{ - LocalIdInBattle: last.LocalIdInBattle, - LinearSpeed: last.LinearSpeed, - X: last.X, - Y: last.Y, - Removed: last.Removed, - StartAtPoint: &pb.Vec2D{ - X: last.StartAtPoint.X, - Y: last.StartAtPoint.Y, - }, - EndAtPoint: &pb.Vec2D{ - X: last.EndAtPoint.X, - Y: last.EndAtPoint.Y, - }, - } - } - - return toRet -} - -func toPbSpeedShoes(modelInstances map[int32]*SpeedShoe) map[int32]*pb.SpeedShoe { - toRet := make(map[int32]*pb.SpeedShoe, 0) - if nil == modelInstances { - return toRet - } - - for k, last := range modelInstances { - toRet[k] = &pb.SpeedShoe{ - Id: last.Id, - LocalIdInBattle: last.LocalIdInBattle, - X: last.X, - Y: last.Y, - Removed: last.Removed, - Type: last.Type, - } - } - - return toRet -} - -func toPbGuardTowers(modelInstances map[int32]*GuardTower) map[int32]*pb.GuardTower { - toRet := make(map[int32]*pb.GuardTower, 0) - if nil == modelInstances { - return toRet - } - - for k, last := range modelInstances { - toRet[k] = &pb.GuardTower{ - Id: last.Id, - LocalIdInBattle: last.LocalIdInBattle, - X: last.X, - Y: last.Y, - Removed: last.Removed, - Type: last.Type, - } - } - - return toRet -} diff --git a/battle_srv/models/pumpkin.go b/battle_srv/models/pumpkin.go deleted file mode 100644 index 7868387..0000000 --- a/battle_srv/models/pumpkin.go +++ /dev/null @@ -1,14 +0,0 @@ -package models - -import "github.com/ByteArena/box2d" - -type Pumpkin struct { - LocalIdInBattle int32 `json:"localIdInBattle,omitempty"` - LinearSpeed float64 `json:"linearSpeed,omitempty"` - X float64 `json:"x,omitempty"` - Y float64 `json:"y,omitempty"` - Removed bool `json:"removed,omitempty"` - Dir *Direction `json:"-"` - CollidableBody *box2d.B2Body `json:"-"` - RemovedAtFrameId int32 `json:"-"` -} diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index cb74cc6..2a100e1 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -43,20 +43,10 @@ const ( const ( // You can equivalently use the `GroupIndex` approach, but the more complicated and general purpose approach is used deliberately here. Reference http://www.aurelienribon.com/post/2011-07-box2d-tutorial-collision-filtering. COLLISION_CATEGORY_CONTROLLED_PLAYER = (1 << 1) - COLLISION_CATEGORY_TREASURE = (1 << 2) - COLLISION_CATEGORY_TRAP = (1 << 3) - COLLISION_CATEGORY_TRAP_BULLET = (1 << 4) - COLLISION_CATEGORY_BARRIER = (1 << 5) - COLLISION_CATEGORY_PUMPKIN = (1 << 6) - COLLISION_CATEGORY_SPEED_SHOES = (1 << 7) + COLLISION_CATEGORY_BARRIER = (1 << 2) - COLLISION_MASK_FOR_CONTROLLED_PLAYER = (COLLISION_CATEGORY_TREASURE | COLLISION_CATEGORY_TRAP | COLLISION_CATEGORY_TRAP_BULLET | COLLISION_CATEGORY_SPEED_SHOES) - COLLISION_MASK_FOR_TREASURE = (COLLISION_CATEGORY_CONTROLLED_PLAYER) - COLLISION_MASK_FOR_TRAP = (COLLISION_CATEGORY_CONTROLLED_PLAYER) - COLLISION_MASK_FOR_TRAP_BULLET = (COLLISION_CATEGORY_CONTROLLED_PLAYER) - COLLISION_MASK_FOR_BARRIER = (COLLISION_CATEGORY_PUMPKIN) - COLLISION_MASK_FOR_PUMPKIN = (COLLISION_CATEGORY_BARRIER) - COLLISION_MASK_FOR_SPEED_SHOES = (COLLISION_CATEGORY_CONTROLLED_PLAYER) + COLLISION_MASK_FOR_CONTROLLED_PLAYER = (COLLISION_CATEGORY_BARRIER) + COLLISION_MASK_FOR_BARRIER = (COLLISION_CATEGORY_CONTROLLED_PLAYER) ) var DIRECTION_DECODER = [][]int32{ @@ -75,6 +65,22 @@ var DIRECTION_DECODER = [][]int32{ {0, -1}, } +var DIRECTION_DECODER_INVERSE_LENGTH = []float32{ + 0.0, + 1.0, + 1.0, + 0.5, + 0.5, + 0.4472, + 0.4472, + 0.4472, + 0.4472, + 0.5, + 0.5, + 1.0, + 1.0, +} + type RoomBattleState struct { IDLE int32 WAITING int32 @@ -142,16 +148,11 @@ type Room struct { BattleDurationNanos int64 EffectivePlayerCount int32 DismissalWaitGroup sync.WaitGroup - Treasures map[int32]*Treasure - Traps map[int32]*Trap - GuardTowers map[int32]*GuardTower - Bullets map[int32]*Bullet - SpeedShoes map[int32]*SpeedShoe Barriers map[int32]*Barrier - Pumpkins map[int32]*Pumpkin AccumulatedLocalIdForBullets int32 CollidableWorld *box2d.B2World AllPlayerInputsBuffer *RingBuffer + RenderFrameBuffer *RingBuffer LastAllConfirmedInputFrameId int32 LastAllConfirmedInputFrameIdWithChange int32 LastAllConfirmedInputList []uint64 @@ -168,58 +169,11 @@ type Room struct { RawBattleStrToPolygon2DListMap StrToPolygon2DListMap } -func (pR *Room) onTreasurePickedUp(contactingPlayer *Player, contactingTreasure *Treasure) { - if _, existent := pR.Treasures[contactingTreasure.LocalIdInBattle]; existent { - Logger.Info("Player has picked up treasure:", zap.Any("roomId", pR.Id), zap.Any("contactingPlayer.Id", contactingPlayer.Id), zap.Any("contactingTreasure.LocalIdInBattle", contactingTreasure.LocalIdInBattle)) - pR.CollidableWorld.DestroyBody(contactingTreasure.CollidableBody) - pR.Treasures[contactingTreasure.LocalIdInBattle] = &Treasure{Removed: true} - pR.Players[contactingPlayer.Id].Score += contactingTreasure.Score - } -} - const ( PLAYER_DEFAULT_SPEED = 200 // Hardcoded ADD_SPEED = 100 // Hardcoded ) -func (pR *Room) onSpeedShoePickedUp(contactingPlayer *Player, contactingSpeedShoe *SpeedShoe, nowMillis int64) { - if _, existent := pR.SpeedShoes[contactingSpeedShoe.LocalIdInBattle]; existent && contactingPlayer.AddSpeedAtGmtMillis == -1 { - Logger.Info("Player has picked up a SpeedShoe:", zap.Any("roomId", pR.Id), zap.Any("contactingPlayer.Id", contactingPlayer.Id), zap.Any("contactingSpeedShoe.LocalIdInBattle", contactingSpeedShoe.LocalIdInBattle)) - pR.CollidableWorld.DestroyBody(contactingSpeedShoe.CollidableBody) - pR.SpeedShoes[contactingSpeedShoe.LocalIdInBattle] = &SpeedShoe{ - Removed: true, - RemovedAtFrameId: pR.Tick, - } - pR.Players[contactingPlayer.Id].Speed += ADD_SPEED - pR.Players[contactingPlayer.Id].AddSpeedAtGmtMillis = nowMillis - } -} - -func (pR *Room) onBulletCrashed(contactingPlayer *Player, contactingBullet *Bullet, nowMillis int64, maxMillisToFreezePerPlayer int64) { - if _, existent := pR.Bullets[contactingBullet.LocalIdInBattle]; existent { - pR.CollidableWorld.DestroyBody(contactingBullet.CollidableBody) - pR.Bullets[contactingBullet.LocalIdInBattle] = &Bullet{ - Removed: true, - RemovedAtFrameId: pR.Tick, - } - - if contactingPlayer != nil { - if maxMillisToFreezePerPlayer > (nowMillis - pR.Players[contactingPlayer.Id].FrozenAtGmtMillis) { - // Deliberately doing nothing. -- YFLu, 2019-09-04. - } else { - pR.Players[contactingPlayer.Id].Speed = 0 - pR.Players[contactingPlayer.Id].FrozenAtGmtMillis = nowMillis - pR.Players[contactingPlayer.Id].AddSpeedAtGmtMillis = -1 - //Logger.Info("Player has picked up bullet:", zap.Any("roomId", pR.Id), zap.Any("contactingPlayer.Id", contactingPlayer.Id), zap.Any("contactingBullet.LocalIdInBattle", contactingBullet.LocalIdInBattle), zap.Any("pR.Players[contactingPlayer.Id].Speed", pR.Players[contactingPlayer.Id].Speed)) - } - } - } -} - -func (pR *Room) onPumpkinEncounterPlayer(pumpkin *Pumpkin, player *Player) { - Logger.Info("pumpkin has caught the player: ", zap.Any("pumpkinId", pumpkin.LocalIdInBattle), zap.Any("playerId", player.Id)) -} - func (pR *Room) updateScore() { pR.Score = calRoomScore(pR.EffectivePlayerCount, pR.Capacity, pR.State) } @@ -280,251 +234,6 @@ func (pR *Room) ReAddPlayerIfPossible(pTmpPlayerInstance *Player, session *webso return true } -func (pR *Room) refreshColliders() { - /* - "BarrierCollider"s are NOT added to the "colliders in B2World of the current battle", thus NOT involved in server-side collision detection! - - -- YFLu, 2019-09-04 - */ - gravity := box2d.MakeB2Vec2(0.0, 0.0) - world := box2d.MakeB2World(gravity) - world.SetContactFilter(&box2d.B2ContactFilter{}) - pR.CollidableWorld = &world - - Logger.Info("Begins `refreshColliders` for players:", zap.Any("roomId", pR.Id)) - for _, player := range pR.Players { - var bdDef box2d.B2BodyDef - colliderOffset := box2d.MakeB2Vec2(0, 0) // Matching that of client-side setting. - bdDef = box2d.MakeB2BodyDef() - bdDef.Type = box2d.B2BodyType.B2_dynamicBody - bdDef.Position.Set(player.X+colliderOffset.X, player.Y+colliderOffset.Y) - - b2Body := pR.CollidableWorld.CreateBody(&bdDef) - - b2CircleShape := box2d.MakeB2CircleShape() - b2CircleShape.M_radius = 32 // Matching that of client-side setting. - - fd := box2d.MakeB2FixtureDef() - fd.Shape = &b2CircleShape - fd.Filter.CategoryBits = COLLISION_CATEGORY_CONTROLLED_PLAYER - fd.Filter.MaskBits = COLLISION_MASK_FOR_CONTROLLED_PLAYER - fd.Density = 0.0 - b2Body.CreateFixtureFromDef(&fd) - - player.CollidableBody = b2Body - b2Body.SetUserData(player) - } - Logger.Info("Ends `refreshColliders` for players:", zap.Any("roomId", pR.Id)) - - Logger.Info("Begins `refreshColliders` for treasures:", zap.Any("roomId", pR.Id)) - for _, treasure := range pR.Treasures { - var bdDef box2d.B2BodyDef - bdDef.Type = box2d.B2BodyType.B2_dynamicBody - bdDef = box2d.MakeB2BodyDef() - bdDef.Position.Set(treasure.PickupBoundary.Anchor.X, treasure.PickupBoundary.Anchor.Y) - - b2Body := pR.CollidableWorld.CreateBody(&bdDef) - - pointsCount := len(treasure.PickupBoundary.Points) - - b2Vertices := make([]box2d.B2Vec2, pointsCount) - for vIndex, v2 := range treasure.PickupBoundary.Points { - b2Vertices[vIndex] = v2.ToB2Vec2() - } - - b2PolygonShape := box2d.MakeB2PolygonShape() - b2PolygonShape.Set(b2Vertices, pointsCount) - - fd := box2d.MakeB2FixtureDef() - fd.Shape = &b2PolygonShape - fd.Filter.CategoryBits = COLLISION_CATEGORY_TREASURE - fd.Filter.MaskBits = COLLISION_MASK_FOR_TREASURE - fd.Density = 0.0 - b2Body.CreateFixtureFromDef(&fd) - - treasure.CollidableBody = b2Body - b2Body.SetUserData(treasure) - } - Logger.Info("Ends `refreshColliders` for treasures:", zap.Any("roomId", pR.Id)) - - Logger.Info("Begins `refreshColliders` for towers:", zap.Any("roomId", pR.Id)) - for _, tower := range pR.GuardTowers { - // Logger.Info("Begins `refreshColliders` for single tower:", zap.Any("k-th", k), zap.Any("tower.LocalIdInBattle", tower.LocalIdInBattle), zap.Any("tower.X", tower.X), zap.Any("tower.Y", tower.Y), zap.Any("tower.PickupBoundary", tower.PickupBoundary), zap.Any("tower.PickupBoundary.Points", tower.PickupBoundary.Points), zap.Any("tower.WidthInB2World", tower.WidthInB2World), zap.Any("tower.HeightInB2World", tower.HeightInB2World), zap.Any("roomId", pR.Id)) - var bdDef box2d.B2BodyDef - bdDef.Type = box2d.B2BodyType.B2_dynamicBody - bdDef = box2d.MakeB2BodyDef() - bdDef.Position.Set(tower.PickupBoundary.Anchor.X, tower.PickupBoundary.Anchor.Y) - - b2Body := pR.CollidableWorld.CreateBody(&bdDef) - // Logger.Info("Checks#1 `refreshColliders` for single tower:", zap.Any("k-th", k), zap.Any("tower", tower), zap.Any("roomId", pR.Id)) - - pointsCount := len(tower.PickupBoundary.Points) - - b2Vertices := make([]box2d.B2Vec2, pointsCount) - for vIndex, v2 := range tower.PickupBoundary.Points { - b2Vertices[vIndex] = v2.ToB2Vec2() - } - // Logger.Info("Checks#2 `refreshColliders` for single tower:", zap.Any("k-th", k), zap.Any("tower", tower), zap.Any("roomId", pR.Id)) - - b2PolygonShape := box2d.MakeB2PolygonShape() - // Logger.Info("Checks#3 `refreshColliders` for single tower:", zap.Any("k-th", k), zap.Any("tower", tower), zap.Any("roomId", pR.Id)) - b2PolygonShape.Set(b2Vertices, pointsCount) - // Logger.Info("Checks#4 `refreshColliders` for single tower:", zap.Any("k-th", k), zap.Any("tower", tower), zap.Any("roomId", pR.Id)) - - fd := box2d.MakeB2FixtureDef() - fd.Shape = &b2PolygonShape - fd.Filter.CategoryBits = COLLISION_CATEGORY_TRAP - fd.Filter.MaskBits = COLLISION_MASK_FOR_TRAP - fd.Density = 0.0 - b2Body.CreateFixtureFromDef(&fd) - // Logger.Info("Checks#5 `refreshColliders` for single tower:", zap.Any("k-th", k), zap.Any("tower", tower), zap.Any("roomId", pR.Id)) - - tower.CollidableBody = b2Body - b2Body.SetUserData(tower) - // Logger.Info("Ends `refreshColliders` for single tower:", zap.Any("k-th", k), zap.Any("tower", tower), zap.Any("roomId", pR.Id)) - } - Logger.Info("Ends `refreshColliders` for towers:", zap.Any("roomId", pR.Id)) - - listener := RoomBattleContactListener{ - name: "TreasureHunterX", - room: pR, - } - /* - * Setting a "ContactListener" for "pR.CollidableWorld" - * will only trigger corresponding callbacks in the - * SAME GOROUTINE of "pR.CollidableWorld.Step(...)" according - * to "https://github.com/ByteArena/box2d/blob/master/DynamicsB2World.go" and - * "https://github.com/ByteArena/box2d/blob/master/DynamicsB2Contact.go". - * - * The invocation-chain involves "Step -> SolveTOI -> B2ContactUpdate -> [BeginContact, EndContact, PreSolve]". - */ - pR.CollidableWorld.SetContactListener(listener) -} - -func calculateDiffFrame(currentFrame *pb.RoomDownsyncFrame, lastFrame *pb.RoomDownsyncFrame) *pb.RoomDownsyncFrame { - if lastFrame == nil { - return currentFrame - } - diffFrame := &pb.RoomDownsyncFrame{ - Id: currentFrame.Id, - RefFrameId: lastFrame.Id, - Players: currentFrame.Players, - SentAt: currentFrame.SentAt, - CountdownNanos: currentFrame.CountdownNanos, - Bullets: currentFrame.Bullets, - Treasures: make(map[int32]*pb.Treasure, 0), - Traps: make(map[int32]*pb.Trap, 0), - SpeedShoes: make(map[int32]*pb.SpeedShoe, 0), - GuardTowers: make(map[int32]*pb.GuardTower, 0), - } - - for k, last := range lastFrame.Treasures { - if last.Removed { - diffFrame.Treasures[k] = last - continue - } - curr, ok := currentFrame.Treasures[k] - if !ok { - diffFrame.Treasures[k] = &pb.Treasure{Removed: true} - Logger.Info("A treasure is removed.", zap.Any("diffFrame.id", diffFrame.Id), zap.Any("treasure.LocalIdInBattle", curr.LocalIdInBattle)) - continue - } - if ok, v := diffTreasure(last, curr); ok { - diffFrame.Treasures[k] = v - } - } - - for k, last := range lastFrame.Bullets { - curr, ok := currentFrame.Bullets[k] - /* - * The use of 'bullet.RemovedAtFrameId' implies that you SHOULDN'T create a record '&Bullet{Removed: true}' here after it's already deleted from 'room.Bullets'. Same applies for `Traps` and `SpeedShoes`. - * - * -- YFLu - */ - if false == ok { - diffFrame.Bullets[k] = &pb.Bullet{Removed: true} - // Logger.Info("A bullet is removed.", zap.Any("diffFrame.id", diffFrame.Id), zap.Any("bullet.LocalIdInBattle", lastFrame.Bullets[k].LocalIdInBattle)) - continue - } - if ok, v := diffBullet(last, curr); ok { - diffFrame.Bullets[k] = v - } - } - - for k, last := range lastFrame.Traps { - curr, ok := currentFrame.Traps[k] - if false == ok { - continue - } - if ok, v := diffTrap(last, curr); ok { - diffFrame.Traps[k] = v - } - } - - for k, last := range lastFrame.SpeedShoes { - curr, ok := currentFrame.SpeedShoes[k] - if false == ok { - continue - } - if ok, v := diffSpeedShoe(last, curr); ok { - diffFrame.SpeedShoes[k] = v - } - } - - return diffFrame -} - -func diffTreasure(last *pb.Treasure, curr *pb.Treasure) (bool, *pb.Treasure) { - treature := &pb.Treasure{} - t := false - if last.Score != curr.Score { - treature.Score = curr.Score - t = true - } - if last.X != curr.X { - treature.X = curr.X - t = true - } - if last.Y != curr.Y { - treature.Y = curr.Y - t = true - } - return t, treature -} - -func diffTrap(last *pb.Trap, curr *pb.Trap) (bool, *pb.Trap) { - trap := &pb.Trap{} - t := false - if last.X != curr.X { - trap.X = curr.X - t = true - } - if last.Y != curr.Y { - trap.Y = curr.Y - t = true - } - return t, trap -} - -func diffSpeedShoe(last *pb.SpeedShoe, curr *pb.SpeedShoe) (bool, *pb.SpeedShoe) { - speedShoe := &pb.SpeedShoe{} - t := false - if last.X != curr.X { - speedShoe.X = curr.X - t = true - } - if last.Y != curr.Y { - speedShoe.Y = curr.Y - t = true - } - return t, speedShoe -} - -func diffBullet(last *pb.Bullet, curr *pb.Bullet) (bool, *pb.Bullet) { - t := true - return t, curr -} - func (pR *Room) ChooseStage() error { /* * We use the verb "refresh" here to imply that upon invocation of this function, all colliders will be recovered if they were destroyed in the previous battle. @@ -592,81 +301,21 @@ func (pR *Room) ChooseStage() error { pR.RawBattleStrToVec2DListMap = toRetStrToVec2DListMap pR.RawBattleStrToPolygon2DListMap = toRetStrToPolygon2DListMap - // Refresh "Treasure" data for RoomDownsyncFrame. - lowScoreTreasurePolygon2DList := *(toRetStrToPolygon2DListMap["LowScoreTreasure"]) - highScoreTreasurePolygon2DList := *(toRetStrToPolygon2DListMap["HighScoreTreasure"]) + barrierPolygon2DList := *(toRetStrToPolygon2DListMap["Barrier"]) - var treasureLocalIdInBattle int32 = 0 - for _, polygon2D := range lowScoreTreasurePolygon2DList { + var barrierLocalIdInBattle int32 = 0 + for _, polygon2D := range barrierPolygon2DList { /* // For debug-printing only. - - Logger.Info("ChooseStage printing polygon2D for lowScoreTreasurePolygon2DList", zap.Any("treasureLocalIdInBattle", treasureLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points)) + Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points)) */ - - theTreasure := &Treasure{ - Id: 0, - LocalIdInBattle: treasureLocalIdInBattle, - Score: LOW_SCORE_TREASURE_SCORE, - Type: LOW_SCORE_TREASURE_TYPE, + pR.Barriers[barrierLocalIdInBattle] = &Barrier{ X: polygon2D.Anchor.X, Y: polygon2D.Anchor.Y, - PickupBoundary: polygon2D, + Boundary: polygon2D, } - pR.Treasures[theTreasure.LocalIdInBattle] = theTreasure - treasureLocalIdInBattle++ - } - - for _, polygon2D := range highScoreTreasurePolygon2DList { - /* - // For debug-printing only. - - Logger.Info("ChooseStage printing polygon2D for highScoreTreasurePolygon2DList", zap.Any("treasureLocalIdInBattle", treasureLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points)) - */ - theTreasure := &Treasure{ - Id: 0, - LocalIdInBattle: treasureLocalIdInBattle, - Score: HIGH_SCORE_TREASURE_SCORE, - Type: HIGH_SCORE_TREASURE_TYPE, - X: polygon2D.Anchor.X, - Y: polygon2D.Anchor.Y, - PickupBoundary: polygon2D, - } - - pR.Treasures[theTreasure.LocalIdInBattle] = theTreasure - - treasureLocalIdInBattle++ - } - - // Refresh "GuardTower" data for RoomDownsyncFrame. - guardTowerPolygon2DList := *(toRetStrToPolygon2DListMap["GuardTower"]) - var guardTowerLocalIdInBattle int32 = 0 - for _, polygon2D := range guardTowerPolygon2DList { - /* - // For debug-printing only. - - Logger.Info("ChooseStage printing polygon2D for guardTowerPolygon2DList", zap.Any("guardTowerLocalIdInBattle", guardTowerLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points), zap.Any("pR.GuardTowers", pR.GuardTowers)) - */ - - var inRangePlayers InRangePlayerCollection - pInRangePlayers := &inRangePlayers - pInRangePlayers = pInRangePlayers.Init(10) - theGuardTower := &GuardTower{ - Id: 0, - LocalIdInBattle: guardTowerLocalIdInBattle, - X: polygon2D.Anchor.X, - Y: polygon2D.Anchor.Y, - PickupBoundary: polygon2D, - InRangePlayers: pInRangePlayers, - LastAttackTick: utils.UnixtimeNano(), - WidthInB2World: float64(polygon2D.TmxObjectWidth), - HeightInB2World: float64(polygon2D.TmxObjectHeight), - } - - pR.GuardTowers[theGuardTower.LocalIdInBattle] = theGuardTower - - guardTowerLocalIdInBattle++ + barrierLocalIdInBattle++ } return nil @@ -754,11 +403,6 @@ func (pR *Room) StartBattle() { Id: pR.Tick, RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START, Players: toPbPlayers(pR.Players), - Treasures: toPbTreasures(pR.Treasures), - Traps: toPbTraps(pR.Traps), - Bullets: toPbBullets(pR.Bullets), - SpeedShoes: toPbSpeedShoes(pR.SpeedShoes), - GuardTowers: toPbGuardTowers(pR.GuardTowers), SentAt: utils.UnixtimeMilli(), CountdownNanos: (pR.BattleDurationNanos - totalElapsedNanos), } @@ -783,6 +427,8 @@ func (pR *Room) StartBattle() { } stCalculation := utils.UnixtimeNano() + // TODO: Force confirm some non-all-confirmed but outdated input frames, derive server-sider RenderFrameBuffer elements from them, and send to respective players with "RoomDownsyncFrame.refFrameId=MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED" + refInputFrameId := int32(999999999) // Hardcoded as a max reference. for playerId, _ := range pR.Players { thatId := atomic.LoadInt32(&(pR.Players[playerId].AckingInputFrameId)) @@ -981,8 +627,6 @@ func (pR *Room) StopBattleForSettlement() { Players: toPbPlayers(pR.Players), SentAt: utils.UnixtimeMilli(), CountdownNanos: -1, // TODO: Replace this magic constant! - Treasures: toPbTreasures(pR.Treasures), - Traps: toPbTraps(pR.Traps), } pR.sendSafely(assembledFrame, playerId) } @@ -1089,11 +733,6 @@ func (pR *Room) onDismissed() { // Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference. pR.Players = make(map[int32]*Player) - pR.Treasures = make(map[int32]*Treasure) - pR.Traps = make(map[int32]*Trap) - pR.GuardTowers = make(map[int32]*GuardTower) - pR.Bullets = make(map[int32]*Bullet) - pR.SpeedShoes = make(map[int32]*SpeedShoe) pR.PlayerDownsyncSessionDict = make(map[int32]*websocket.Conn) pR.PlayerSignalToCloseDict = make(map[int32]SignalToCloseConnCbType) @@ -1105,6 +744,7 @@ func (pR *Room) onDismissed() { pR.JoinIndexBooleanArr[indice] = false } pR.AllPlayerInputsBuffer = NewRingBuffer(1024) + pR.RenderFrameBuffer = NewRingBuffer(1024) pR.ChooseStage() pR.EffectivePlayerCount = 0 @@ -1116,14 +756,6 @@ func (pR *Room) onDismissed() { Logger.Info("The room is completely dismissed:", zap.Any("roomId", pR.Id)) } -func (pR *Room) Unicast(toPlayerId int32, msg interface{}) { - // TODO -} - -func (pR *Room) Broadcast(msg interface{}) { - // TODO -} - func (pR *Room) expelPlayerDuringGame(playerId int32) { defer pR.onPlayerExpelledDuringGame(playerId) } @@ -1368,6 +1000,84 @@ func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool { return 0 == (inputFrameId % 10) } +func (pR *Room) refreshColliders() { + gravity := box2d.MakeB2Vec2(0.0, 0.0) + world := box2d.MakeB2World(gravity) + world.SetContactFilter(&box2d.B2ContactFilter{}) + pR.CollidableWorld = &world + + Logger.Info("Begins players collider processing:", zap.Any("roomId", pR.Id)) + for _, player := range pR.Players { + var bdDef box2d.B2BodyDef + colliderOffset := box2d.MakeB2Vec2(0, 0) // Matching that of client-side setting. + bdDef = box2d.MakeB2BodyDef() + bdDef.Type = box2d.B2BodyType.B2_dynamicBody + bdDef.Position.Set(player.X+colliderOffset.X, player.Y+colliderOffset.Y) + + b2Body := pR.CollidableWorld.CreateBody(&bdDef) + + b2CircleShape := box2d.MakeB2CircleShape() + b2CircleShape.M_radius = 32 // Matching that of client-side setting. + + fd := box2d.MakeB2FixtureDef() + fd.Shape = &b2CircleShape + fd.Filter.CategoryBits = COLLISION_CATEGORY_CONTROLLED_PLAYER + fd.Filter.MaskBits = COLLISION_MASK_FOR_CONTROLLED_PLAYER + fd.Density = 0.0 + b2Body.CreateFixtureFromDef(&fd) + + player.CollidableBody = b2Body + b2Body.SetUserData(player) + } + Logger.Info("Ends players collider processing:", zap.Any("roomId", pR.Id)) + + Logger.Info("Begins barriers collider processing:", zap.Any("roomId", pR.Id)) + for _, barrier := range pR.Barriers { + var bdDef box2d.B2BodyDef + bdDef.Type = box2d.B2BodyType.B2_dynamicBody + bdDef = box2d.MakeB2BodyDef() + bdDef.Position.Set(barrier.Boundary.Anchor.X, barrier.Boundary.Anchor.Y) + + b2Body := pR.CollidableWorld.CreateBody(&bdDef) + + pointsCount := len(barrier.Boundary.Points) + + b2Vertices := make([]box2d.B2Vec2, pointsCount) + for vIndex, v2 := range barrier.Boundary.Points { + b2Vertices[vIndex] = v2.ToB2Vec2() + } + + b2PolygonShape := box2d.MakeB2PolygonShape() + b2PolygonShape.Set(b2Vertices, pointsCount) + + fd := box2d.MakeB2FixtureDef() + fd.Shape = &b2PolygonShape + fd.Filter.CategoryBits = COLLISION_CATEGORY_BARRIER + fd.Filter.MaskBits = COLLISION_MASK_FOR_BARRIER + fd.Density = 0.0 + b2Body.CreateFixtureFromDef(&fd) + + barrier.CollidableBody = b2Body + b2Body.SetUserData(barrier) + } + Logger.Info("Ends barriers collider processing:", zap.Any("roomId", pR.Id)) + + listener := RoomBattleContactListener{ + name: "DelayNoMore", + room: pR, + } + /* + * Setting a "ContactListener" for "pR.CollidableWorld" + * will only trigger corresponding callbacks in the + * SAME GOROUTINE of "pR.CollidableWorld.Step(...)" according + * to "https://github.com/ByteArena/box2d/blob/master/DynamicsB2World.go" and + * "https://github.com/ByteArena/box2d/blob/master/DynamicsB2Contact.go". + * + * The invocation-chain involves "Step -> SolveTOI -> B2ContactUpdate -> [BeginContact, EndContact, PreSolve]". + */ + pR.CollidableWorld.SetContactListener(listener) +} + type RoomBattleContactListener struct { name string room *Room @@ -1376,15 +1086,15 @@ type RoomBattleContactListener struct { // Implementing the GolangBox2d contact listeners [begins]. /** * Note that the execution of these listeners is within the SAME GOROUTINE as that of "`battleMainLoop` in the same room". - * See the comments in `Room.refreshContactListener()` for details. + * See the comments in `Room.refreshColliders()` for details. */ func (l RoomBattleContactListener) BeginContact(contact box2d.B2ContactInterface) { - var pTower *GuardTower + var pBarrier *Barrier var pPlayer *Player switch v := contact.GetNodeA().Other.GetUserData().(type) { - case *GuardTower: - pTower = v + case *Barrier: + pBarrier = v case *Player: pPlayer = v default: @@ -1392,40 +1102,41 @@ func (l RoomBattleContactListener) BeginContact(contact box2d.B2ContactInterface } switch v := contact.GetNodeB().Other.GetUserData().(type) { - case *GuardTower: - pTower = v + case *Barrier: + pBarrier = v case *Player: pPlayer = v default: } - if pTower != nil && pPlayer != nil { - pTower.InRangePlayers.AppendPlayer(pPlayer) + if pBarrier != nil && pPlayer != nil { + Logger.Info("player begins collision with barrier:", zap.Any("barrier", pBarrier), zap.Any("player", pPlayer)) + // TODO: Push back player } } func (l RoomBattleContactListener) EndContact(contact box2d.B2ContactInterface) { - var pTower *GuardTower + var pBarrier *Barrier var pPlayer *Player switch v := contact.GetNodeA().Other.GetUserData().(type) { - case *GuardTower: - pTower = v + case *Barrier: + pBarrier = v case *Player: pPlayer = v default: } switch v := contact.GetNodeB().Other.GetUserData().(type) { - case *GuardTower: - pTower = v + case *Barrier: + pBarrier = v case *Player: pPlayer = v default: } - if pTower != nil && pPlayer != nil { - pTower.InRangePlayers.RemovePlayerById(pPlayer.Id) + if pBarrier != nil && pPlayer != nil { + Logger.Info("player ends collision with barrier:", zap.Any("barrier", pBarrier), zap.Any("player", pPlayer)) } } diff --git a/battle_srv/models/room_heap_manager.go b/battle_srv/models/room_heap_manager.go index 70b58b7..fc207b5 100644 --- a/battle_srv/models/room_heap_manager.go +++ b/battle_srv/models/room_heap_manager.go @@ -111,15 +111,10 @@ func InitRoomHeapManager() { //BattleDurationNanos: int64(5 * 1000 * 1000 * 1000), BattleDurationNanos: int64(30 * 1000 * 1000 * 1000), ServerFPS: 60, - Treasures: make(map[int32]*Treasure), - Traps: make(map[int32]*Trap), - GuardTowers: make(map[int32]*GuardTower), - Bullets: make(map[int32]*Bullet), - SpeedShoes: make(map[int32]*SpeedShoe), Barriers: make(map[int32]*Barrier), - Pumpkins: make(map[int32]*Pumpkin), AccumulatedLocalIdForBullets: 0, AllPlayerInputsBuffer: NewRingBuffer(1024), + RenderFrameBuffer: NewRingBuffer(1024), LastAllConfirmedInputFrameId: -1, LastAllConfirmedInputFrameIdWithChange: -1, LastAllConfirmedInputList: make([]uint64, roomCapacity), diff --git a/battle_srv/models/speed_shoe.go b/battle_srv/models/speed_shoe.go deleted file mode 100644 index a2db919..0000000 --- a/battle_srv/models/speed_shoe.go +++ /dev/null @@ -1,17 +0,0 @@ -package models - -import ( - "github.com/ByteArena/box2d" -) - -type SpeedShoe struct { - Id int32 `json:"id,omitempty"` - LocalIdInBattle int32 `json:"localIdInBattle,omitempty"` - X float64 `json:"x,omitempty"` - Y float64 `json:"y,omitempty"` - Removed bool `json:"removed,omitempty"` - Type int32 `json:"type,omitempty"` - PickupBoundary *Polygon2D `json:"-"` - CollidableBody *box2d.B2Body `json:"-"` - RemovedAtFrameId int32 `json:"-"` -} diff --git a/battle_srv/models/tiled_map.go b/battle_srv/models/tiled_map.go index da46afe..c6943c4 100644 --- a/battle_srv/models/tiled_map.go +++ b/battle_srv/models/tiled_map.go @@ -19,7 +19,6 @@ import ( const ( LOW_SCORE_TREASURE_TYPE = 1 HIGH_SCORE_TREASURE_TYPE = 2 - SPEED_SHOES_TYPE = 3 LOW_SCORE_TREASURE_SCORE = 100 @@ -297,7 +296,7 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f for _, tile := range pTsxIns.Tiles { globalGid := (firstGid + int(tile.Id)) /** - Per tile xml str could be + A tile xml string could be ``` @@ -367,8 +366,6 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i - "Polygon2D"s of "toRetStrToPolygon2DListMap" are already transformed into the "coordinate of B2World". - - -- YFLu */ for _, objGroup := range pTmxMapIns.ObjectGroups { @@ -391,13 +388,8 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i thePosInWorld := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedPos) *pTheVec2DListToCache = append(*pTheVec2DListToCache, &thePosInWorld) } - case "Pumpkin", "SpeedShoe": case "Barrier": - /* - Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is located exactly in an overlapping with "Polygon2D.Points[0]" w.r.t. B2World. - - -- YFLu - */ + // Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]" w.r.t. B2World. var pThePolygon2DListToCache *Polygon2DList _, ok := toRetStrToPolygon2DListMap[objGroup.Name] if false == ok { @@ -422,65 +414,6 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i } *pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld) } - case "LowScoreTreasure", "GuardTower", "HighScoreTreasure": - /* - Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" ISN'T located exactly in an overlapping with "Polygon2D.Points[0]" w.r.t. B2World, refer to "https://shimo.im/docs/SmLJJhXm2C8XMzZT" for details. - - -- YFLu - */ - for _, singleObjInTmxFile := range objGroup.Objects { - if nil == singleObjInTmxFile.Gid { - continue - } - theGlobalGid := singleObjInTmxFile.Gid - theStrToPolygon2DListMap, ok := gidBoundariesMapInB2World[*theGlobalGid] - if false == ok { - continue - } - - pThePolygon2DList, ok := theStrToPolygon2DListMap[objGroup.Name] - if false == ok { - continue - } - - var pThePolygon2DListToCache *Polygon2DList - _, ok = toRetStrToPolygon2DListMap[objGroup.Name] - if false == ok { - thePolygon2DListToCache := make(Polygon2DList, 0) - toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache - pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name] - } else { - pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name] - } - - for _, thePolygon2D := range *pThePolygon2DList { - theUntransformedBottomCenterAsAnchor := &Vec2D{ - X: singleObjInTmxFile.X, - Y: singleObjInTmxFile.Y, - } - - theTransformedBottomCenterAsAnchor := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedBottomCenterAsAnchor) - - thePolygon2DInWorld := &Polygon2D{ - Anchor: &theTransformedBottomCenterAsAnchor, - Points: make([]*Vec2D, len(thePolygon2D.Points)), - TileWidth: thePolygon2D.TileWidth, - TileHeight: thePolygon2D.TileHeight, - } - if nil != singleObjInTmxFile.Width && nil != singleObjInTmxFile.Height { - thePolygon2DInWorld.TmxObjectWidth = *singleObjInTmxFile.Width - thePolygon2DInWorld.TmxObjectHeight = *singleObjInTmxFile.Height - } - for kk, p := range thePolygon2D.Points { - // [WARNING] It's intentionally recreating a copy of "Vec2D" here. - thePolygon2DInWorld.Points[kk] = &Vec2D{ - X: p.X, - Y: p.Y, - } - } - *pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld) - } - } default: } } diff --git a/battle_srv/models/trap.go b/battle_srv/models/trap.go deleted file mode 100644 index 0474a02..0000000 --- a/battle_srv/models/trap.go +++ /dev/null @@ -1,39 +0,0 @@ -package models - -import ( - "github.com/ByteArena/box2d" -) - -type Trap struct { - Id int32 `json:"id,omitempty"` - LocalIdInBattle int32 `json:"localIdInBattle,omitempty"` - Type int32 `json:"type,omitempty"` - X float64 `json:"x,omitempty"` - Y float64 `json:"y,omitempty"` - Removed bool `json:"removed,omitempty"` - PickupBoundary *Polygon2D `json:"-"` - TrapBullets []*Bullet `json:"-"` - CollidableBody *box2d.B2Body `json:"-"` - RemovedAtFrameId int32 `json:"-"` -} - -type GuardTower struct { - Id int32 `json:"id,omitempty"` - LocalIdInBattle int32 `json:"localIdInBattle,omitempty"` - Type int32 `json:"type,omitempty"` - X float64 `json:"x,omitempty"` - Y float64 `json:"y,omitempty"` - Removed bool `json:"removed,omitempty"` - PickupBoundary *Polygon2D `json:"-"` - TrapBullets []*Bullet `json:"-"` - CollidableBody *box2d.B2Body `json:"-"` - RemovedAtFrameId int32 `json:"-"` - - InRangePlayers *InRangePlayerCollection `json:"-"` - LastAttackTick int64 `json:"-"` - - TileWidth float64 `json:"-"` - TileHeight float64 `json:"-"` - WidthInB2World float64 `json:"-"` - HeightInB2World float64 `json:"-"` -} diff --git a/battle_srv/models/treasure.go b/battle_srv/models/treasure.go deleted file mode 100644 index 95f3704..0000000 --- a/battle_srv/models/treasure.go +++ /dev/null @@ -1,18 +0,0 @@ -package models - -import ( - "github.com/ByteArena/box2d" -) - -type Treasure struct { - Id int32 `json:"id,omitempty"` - LocalIdInBattle int32 `json:"localIdInBattle,omitempty"` - Score int32 `json:"score,omitempty"` - X float64 `json:"x,omitempty"` - Y float64 `json:"y,omitempty"` - Removed bool `json:"removed,omitempty"` - Type int32 `json:"type,omitempty"` - - PickupBoundary *Polygon2D `json:"-"` - CollidableBody *box2d.B2Body `json:"-"` -} diff --git a/battle_srv/pb_output/room_downsync_frame.pb.go b/battle_srv/pb_output/room_downsync_frame.pb.go index 7f19732..5693445 100644 --- a/battle_srv/pb_output/room_downsync_frame.pb.go +++ b/battle_srv/pb_output/room_downsync_frame.pb.go @@ -596,536 +596,6 @@ func (x *PlayerMeta) GetJoinIndex() int32 { return 0 } -type Treasure struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - LocalIdInBattle int32 `protobuf:"varint,2,opt,name=localIdInBattle,proto3" json:"localIdInBattle,omitempty"` - Score int32 `protobuf:"varint,3,opt,name=score,proto3" json:"score,omitempty"` - X float64 `protobuf:"fixed64,4,opt,name=x,proto3" json:"x,omitempty"` - Y float64 `protobuf:"fixed64,5,opt,name=y,proto3" json:"y,omitempty"` - Removed bool `protobuf:"varint,6,opt,name=removed,proto3" json:"removed,omitempty"` - Type int32 `protobuf:"varint,7,opt,name=type,proto3" json:"type,omitempty"` -} - -func (x *Treasure) Reset() { - *x = Treasure{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Treasure) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Treasure) ProtoMessage() {} - -func (x *Treasure) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Treasure.ProtoReflect.Descriptor instead. -func (*Treasure) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{8} -} - -func (x *Treasure) GetId() int32 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *Treasure) GetLocalIdInBattle() int32 { - if x != nil { - return x.LocalIdInBattle - } - return 0 -} - -func (x *Treasure) GetScore() int32 { - if x != nil { - return x.Score - } - return 0 -} - -func (x *Treasure) GetX() float64 { - if x != nil { - return x.X - } - return 0 -} - -func (x *Treasure) GetY() float64 { - if x != nil { - return x.Y - } - return 0 -} - -func (x *Treasure) GetRemoved() bool { - if x != nil { - return x.Removed - } - return false -} - -func (x *Treasure) GetType() int32 { - if x != nil { - return x.Type - } - return 0 -} - -type Bullet struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - LocalIdInBattle int32 `protobuf:"varint,1,opt,name=localIdInBattle,proto3" json:"localIdInBattle,omitempty"` - LinearSpeed float64 `protobuf:"fixed64,2,opt,name=linearSpeed,proto3" json:"linearSpeed,omitempty"` - X float64 `protobuf:"fixed64,3,opt,name=x,proto3" json:"x,omitempty"` - Y float64 `protobuf:"fixed64,4,opt,name=y,proto3" json:"y,omitempty"` - Removed bool `protobuf:"varint,5,opt,name=removed,proto3" json:"removed,omitempty"` - StartAtPoint *Vec2D `protobuf:"bytes,6,opt,name=startAtPoint,proto3" json:"startAtPoint,omitempty"` - EndAtPoint *Vec2D `protobuf:"bytes,7,opt,name=endAtPoint,proto3" json:"endAtPoint,omitempty"` -} - -func (x *Bullet) Reset() { - *x = Bullet{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Bullet) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Bullet) ProtoMessage() {} - -func (x *Bullet) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Bullet.ProtoReflect.Descriptor instead. -func (*Bullet) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{9} -} - -func (x *Bullet) GetLocalIdInBattle() int32 { - if x != nil { - return x.LocalIdInBattle - } - return 0 -} - -func (x *Bullet) GetLinearSpeed() float64 { - if x != nil { - return x.LinearSpeed - } - return 0 -} - -func (x *Bullet) GetX() float64 { - if x != nil { - return x.X - } - return 0 -} - -func (x *Bullet) GetY() float64 { - if x != nil { - return x.Y - } - return 0 -} - -func (x *Bullet) GetRemoved() bool { - if x != nil { - return x.Removed - } - return false -} - -func (x *Bullet) GetStartAtPoint() *Vec2D { - if x != nil { - return x.StartAtPoint - } - return nil -} - -func (x *Bullet) GetEndAtPoint() *Vec2D { - if x != nil { - return x.EndAtPoint - } - return nil -} - -type Trap struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - LocalIdInBattle int32 `protobuf:"varint,2,opt,name=localIdInBattle,proto3" json:"localIdInBattle,omitempty"` - Type int32 `protobuf:"varint,3,opt,name=type,proto3" json:"type,omitempty"` - X float64 `protobuf:"fixed64,4,opt,name=x,proto3" json:"x,omitempty"` - Y float64 `protobuf:"fixed64,5,opt,name=y,proto3" json:"y,omitempty"` - Removed bool `protobuf:"varint,6,opt,name=removed,proto3" json:"removed,omitempty"` -} - -func (x *Trap) Reset() { - *x = Trap{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Trap) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Trap) ProtoMessage() {} - -func (x *Trap) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Trap.ProtoReflect.Descriptor instead. -func (*Trap) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{10} -} - -func (x *Trap) GetId() int32 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *Trap) GetLocalIdInBattle() int32 { - if x != nil { - return x.LocalIdInBattle - } - return 0 -} - -func (x *Trap) GetType() int32 { - if x != nil { - return x.Type - } - return 0 -} - -func (x *Trap) GetX() float64 { - if x != nil { - return x.X - } - return 0 -} - -func (x *Trap) GetY() float64 { - if x != nil { - return x.Y - } - return 0 -} - -func (x *Trap) GetRemoved() bool { - if x != nil { - return x.Removed - } - return false -} - -type SpeedShoe struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - LocalIdInBattle int32 `protobuf:"varint,2,opt,name=localIdInBattle,proto3" json:"localIdInBattle,omitempty"` - X float64 `protobuf:"fixed64,3,opt,name=x,proto3" json:"x,omitempty"` - Y float64 `protobuf:"fixed64,4,opt,name=y,proto3" json:"y,omitempty"` - Removed bool `protobuf:"varint,5,opt,name=removed,proto3" json:"removed,omitempty"` - Type int32 `protobuf:"varint,6,opt,name=type,proto3" json:"type,omitempty"` -} - -func (x *SpeedShoe) Reset() { - *x = SpeedShoe{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SpeedShoe) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SpeedShoe) ProtoMessage() {} - -func (x *SpeedShoe) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SpeedShoe.ProtoReflect.Descriptor instead. -func (*SpeedShoe) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{11} -} - -func (x *SpeedShoe) GetId() int32 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *SpeedShoe) GetLocalIdInBattle() int32 { - if x != nil { - return x.LocalIdInBattle - } - return 0 -} - -func (x *SpeedShoe) GetX() float64 { - if x != nil { - return x.X - } - return 0 -} - -func (x *SpeedShoe) GetY() float64 { - if x != nil { - return x.Y - } - return 0 -} - -func (x *SpeedShoe) GetRemoved() bool { - if x != nil { - return x.Removed - } - return false -} - -func (x *SpeedShoe) GetType() int32 { - if x != nil { - return x.Type - } - return 0 -} - -type Pumpkin struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - LocalIdInBattle int32 `protobuf:"varint,1,opt,name=localIdInBattle,proto3" json:"localIdInBattle,omitempty"` - LinearSpeed float64 `protobuf:"fixed64,2,opt,name=linearSpeed,proto3" json:"linearSpeed,omitempty"` - X float64 `protobuf:"fixed64,3,opt,name=x,proto3" json:"x,omitempty"` - Y float64 `protobuf:"fixed64,4,opt,name=y,proto3" json:"y,omitempty"` - Removed bool `protobuf:"varint,5,opt,name=removed,proto3" json:"removed,omitempty"` -} - -func (x *Pumpkin) Reset() { - *x = Pumpkin{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Pumpkin) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Pumpkin) ProtoMessage() {} - -func (x *Pumpkin) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Pumpkin.ProtoReflect.Descriptor instead. -func (*Pumpkin) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{12} -} - -func (x *Pumpkin) GetLocalIdInBattle() int32 { - if x != nil { - return x.LocalIdInBattle - } - return 0 -} - -func (x *Pumpkin) GetLinearSpeed() float64 { - if x != nil { - return x.LinearSpeed - } - return 0 -} - -func (x *Pumpkin) GetX() float64 { - if x != nil { - return x.X - } - return 0 -} - -func (x *Pumpkin) GetY() float64 { - if x != nil { - return x.Y - } - return 0 -} - -func (x *Pumpkin) GetRemoved() bool { - if x != nil { - return x.Removed - } - return false -} - -type GuardTower struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - LocalIdInBattle int32 `protobuf:"varint,2,opt,name=localIdInBattle,proto3" json:"localIdInBattle,omitempty"` - Type int32 `protobuf:"varint,3,opt,name=type,proto3" json:"type,omitempty"` - X float64 `protobuf:"fixed64,4,opt,name=x,proto3" json:"x,omitempty"` - Y float64 `protobuf:"fixed64,5,opt,name=y,proto3" json:"y,omitempty"` - Removed bool `protobuf:"varint,6,opt,name=removed,proto3" json:"removed,omitempty"` -} - -func (x *GuardTower) Reset() { - *x = GuardTower{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GuardTower) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GuardTower) ProtoMessage() {} - -func (x *GuardTower) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GuardTower.ProtoReflect.Descriptor instead. -func (*GuardTower) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{13} -} - -func (x *GuardTower) GetId() int32 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *GuardTower) GetLocalIdInBattle() int32 { - if x != nil { - return x.LocalIdInBattle - } - return 0 -} - -func (x *GuardTower) GetType() int32 { - if x != nil { - return x.Type - } - return 0 -} - -func (x *GuardTower) GetX() float64 { - if x != nil { - return x.X - } - return 0 -} - -func (x *GuardTower) GetY() float64 { - if x != nil { - return x.Y - } - return 0 -} - -func (x *GuardTower) GetRemoved() bool { - if x != nil { - return x.Removed - } - return false -} - type RoomDownsyncFrame struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1136,18 +606,13 @@ type RoomDownsyncFrame struct { Players map[int32]*Player `protobuf:"bytes,3,rep,name=players,proto3" json:"players,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` SentAt int64 `protobuf:"varint,4,opt,name=sentAt,proto3" json:"sentAt,omitempty"` CountdownNanos int64 `protobuf:"varint,5,opt,name=countdownNanos,proto3" json:"countdownNanos,omitempty"` - Treasures map[int32]*Treasure `protobuf:"bytes,6,rep,name=treasures,proto3" json:"treasures,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Traps map[int32]*Trap `protobuf:"bytes,7,rep,name=traps,proto3" json:"traps,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Bullets map[int32]*Bullet `protobuf:"bytes,8,rep,name=bullets,proto3" json:"bullets,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - SpeedShoes map[int32]*SpeedShoe `protobuf:"bytes,9,rep,name=speedShoes,proto3" json:"speedShoes,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - GuardTowers map[int32]*GuardTower `protobuf:"bytes,10,rep,name=guardTowers,proto3" json:"guardTowers,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - PlayerMetas map[int32]*PlayerMeta `protobuf:"bytes,11,rep,name=playerMetas,proto3" json:"playerMetas,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + PlayerMetas map[int32]*PlayerMeta `protobuf:"bytes,6,rep,name=playerMetas,proto3" json:"playerMetas,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *RoomDownsyncFrame) Reset() { *x = RoomDownsyncFrame{} if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[14] + mi := &file_room_downsync_frame_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1160,7 +625,7 @@ func (x *RoomDownsyncFrame) String() string { func (*RoomDownsyncFrame) ProtoMessage() {} func (x *RoomDownsyncFrame) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[14] + mi := &file_room_downsync_frame_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1173,7 +638,7 @@ func (x *RoomDownsyncFrame) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomDownsyncFrame.ProtoReflect.Descriptor instead. func (*RoomDownsyncFrame) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{14} + return file_room_downsync_frame_proto_rawDescGZIP(), []int{8} } func (x *RoomDownsyncFrame) GetId() int32 { @@ -1211,41 +676,6 @@ func (x *RoomDownsyncFrame) GetCountdownNanos() int64 { return 0 } -func (x *RoomDownsyncFrame) GetTreasures() map[int32]*Treasure { - if x != nil { - return x.Treasures - } - return nil -} - -func (x *RoomDownsyncFrame) GetTraps() map[int32]*Trap { - if x != nil { - return x.Traps - } - return nil -} - -func (x *RoomDownsyncFrame) GetBullets() map[int32]*Bullet { - if x != nil { - return x.Bullets - } - return nil -} - -func (x *RoomDownsyncFrame) GetSpeedShoes() map[int32]*SpeedShoe { - if x != nil { - return x.SpeedShoes - } - return nil -} - -func (x *RoomDownsyncFrame) GetGuardTowers() map[int32]*GuardTower { - if x != nil { - return x.GuardTowers - } - return nil -} - func (x *RoomDownsyncFrame) GetPlayerMetas() map[int32]*PlayerMeta { if x != nil { return x.PlayerMetas @@ -1265,7 +695,7 @@ type InputFrameUpsync struct { func (x *InputFrameUpsync) Reset() { *x = InputFrameUpsync{} if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[15] + mi := &file_room_downsync_frame_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1278,7 +708,7 @@ func (x *InputFrameUpsync) String() string { func (*InputFrameUpsync) ProtoMessage() {} func (x *InputFrameUpsync) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[15] + mi := &file_room_downsync_frame_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1291,7 +721,7 @@ func (x *InputFrameUpsync) ProtoReflect() protoreflect.Message { // Deprecated: Use InputFrameUpsync.ProtoReflect.Descriptor instead. func (*InputFrameUpsync) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{15} + return file_room_downsync_frame_proto_rawDescGZIP(), []int{9} } func (x *InputFrameUpsync) GetInputFrameId() int32 { @@ -1321,7 +751,7 @@ type InputFrameDownsync struct { func (x *InputFrameDownsync) Reset() { *x = InputFrameDownsync{} if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[16] + mi := &file_room_downsync_frame_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1334,7 +764,7 @@ func (x *InputFrameDownsync) String() string { func (*InputFrameDownsync) ProtoMessage() {} func (x *InputFrameDownsync) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[16] + mi := &file_room_downsync_frame_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1347,7 +777,7 @@ func (x *InputFrameDownsync) ProtoReflect() protoreflect.Message { // Deprecated: Use InputFrameDownsync.ProtoReflect.Descriptor instead. func (*InputFrameDownsync) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{16} + return file_room_downsync_frame_proto_rawDescGZIP(), []int{10} } func (x *InputFrameDownsync) GetInputFrameId() int32 { @@ -1382,7 +812,7 @@ type HeartbeatUpsync struct { func (x *HeartbeatUpsync) Reset() { *x = HeartbeatUpsync{} if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[17] + mi := &file_room_downsync_frame_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1395,7 +825,7 @@ func (x *HeartbeatUpsync) String() string { func (*HeartbeatUpsync) ProtoMessage() {} func (x *HeartbeatUpsync) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[17] + mi := &file_room_downsync_frame_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1408,7 +838,7 @@ func (x *HeartbeatUpsync) ProtoReflect() protoreflect.Message { // Deprecated: Use HeartbeatUpsync.ProtoReflect.Descriptor instead. func (*HeartbeatUpsync) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{17} + return file_room_downsync_frame_proto_rawDescGZIP(), []int{11} } func (x *HeartbeatUpsync) GetClientTimestamp() int64 { @@ -1436,7 +866,7 @@ type WsReq struct { func (x *WsReq) Reset() { *x = WsReq{} if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[18] + mi := &file_room_downsync_frame_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1449,7 +879,7 @@ func (x *WsReq) String() string { func (*WsReq) ProtoMessage() {} func (x *WsReq) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[18] + mi := &file_room_downsync_frame_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1462,7 +892,7 @@ func (x *WsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use WsReq.ProtoReflect.Descriptor instead. func (*WsReq) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{18} + return file_room_downsync_frame_proto_rawDescGZIP(), []int{12} } func (x *WsReq) GetMsgId() int32 { @@ -1537,7 +967,7 @@ type WsResp struct { func (x *WsResp) Reset() { *x = WsResp{} if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[19] + mi := &file_room_downsync_frame_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1550,7 +980,7 @@ func (x *WsResp) String() string { func (*WsResp) ProtoMessage() {} func (x *WsResp) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[19] + mi := &file_room_downsync_frame_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1563,7 +993,7 @@ func (x *WsResp) ProtoReflect() protoreflect.Message { // Deprecated: Use WsResp.ProtoReflect.Descriptor instead. func (*WsResp) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{19} + return file_room_downsync_frame_proto_rawDescGZIP(), []int{13} } func (x *WsResp) GetRet() int32 { @@ -1708,208 +1138,94 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69, - 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xa4, 0x01, 0x0a, 0x08, 0x54, 0x72, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, - 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x63, - 0x6f, 0x72, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, - 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xfe, 0x01, - 0x0a, 0x06, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, - 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, 0x74, - 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x53, 0x70, 0x65, 0x65, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x53, - 0x70, 0x65, 0x65, 0x64, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79, - 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0c, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x41, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x78, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x41, - 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x41, 0x74, 0x50, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x65, - 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x56, 0x65, 0x63, - 0x32, 0x44, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x41, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x8a, - 0x01, 0x0a, 0x04, 0x54, 0x72, 0x61, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, - 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, - 0x79, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x8f, 0x01, 0x0a, 0x09, - 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68, 0x6f, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, - 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, - 0x74, 0x6c, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, - 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x8b, 0x01, - 0x0a, 0x07, 0x50, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, - 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, - 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x53, 0x70, 0x65, - 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, - 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, - 0x79, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x90, 0x01, 0x0a, 0x0a, - 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x49, 0x6e, 0x42, 0x61, - 0x74, 0x74, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x01, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x9a, - 0x0a, 0x0a, 0x11, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, - 0x72, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, - 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x49, 0x64, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, - 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, - 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, - 0x4f, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, - 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x54, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x73, - 0x12, 0x43, 0x0a, 0x05, 0x74, 0x72, 0x61, 0x70, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2d, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, - 0x74, 0x72, 0x61, 0x70, 0x73, 0x12, 0x49, 0x0a, 0x07, 0x62, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, - 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, - 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, - 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x42, 0x75, 0x6c, 0x6c, 0x65, - 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x62, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, - 0x12, 0x52, 0x0a, 0x0a, 0x73, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68, 0x6f, 0x65, 0x73, 0x18, 0x09, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, - 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68, - 0x6f, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x65, 0x64, 0x53, - 0x68, 0x6f, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x67, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, - 0x65, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, - 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, - 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x47, 0x75, - 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, - 0x67, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, - 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, - 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x0e, 0x54, 0x72, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x72, 0x65, - 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x54, 0x72, 0x65, - 0x61, 0x73, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x4f, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xd7, 0x03, 0x0a, 0x11, 0x52, 0x6f, 0x6f, 0x6d, 0x44, + 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, + 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x49, 0x0a, 0x07, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, + 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, + 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41, 0x74, 0x12, + 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, + 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, + 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, + 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, + 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x78, 0x2e, 0x54, 0x72, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x53, 0x0a, 0x0c, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x59, 0x0a, 0x0f, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, - 0x68, 0x6f, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, - 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x53, 0x70, 0x65, - 0x65, 0x64, 0x53, 0x68, 0x6f, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, - 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, - 0x77, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, - 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x10, 0x49, - 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, - 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, - 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, - 0x44, 0x69, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, - 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, - 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, - 0x74, 0x22, 0x3b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, - 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xca, - 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09, - 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, - 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, - 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, - 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x63, - 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, - 0x12, 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, - 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, - 0x6e, 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, - 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, - 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, - 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x06, - 0x57, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6f, - 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, - 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x03, - 0x72, 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x61, - 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, - 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x72, - 0x64, 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, - 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, - 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, - 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, - 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, + 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, + 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, + 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, + 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, + 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, + 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x22, 0xca, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, + 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, + 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, + 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, + 0x0a, 0x02, 0x68, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, + 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, + 0x22, 0xa4, 0x02, 0x0a, 0x06, 0x57, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, + 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, + 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, + 0x74, 0x12, 0x34, 0x0a, 0x03, 0x72, 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, + 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, + 0x6d, 0x65, 0x52, 0x03, 0x72, 0x64, 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, + 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, + 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, + 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, + 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1924,7 +1240,7 @@ func file_room_downsync_frame_proto_rawDescGZIP() []byte { return file_room_downsync_frame_proto_rawDescData } -var file_room_downsync_frame_proto_msgTypes = make([]protoimpl.MessageInfo, 29) +var file_room_downsync_frame_proto_msgTypes = make([]protoimpl.MessageInfo, 18) var file_room_downsync_frame_proto_goTypes = []interface{}{ (*Direction)(nil), // 0: treasurehunterx.Direction (*Vec2D)(nil), // 1: treasurehunterx.Vec2D @@ -1934,64 +1250,41 @@ var file_room_downsync_frame_proto_goTypes = []interface{}{ (*BattleColliderInfo)(nil), // 5: treasurehunterx.BattleColliderInfo (*Player)(nil), // 6: treasurehunterx.Player (*PlayerMeta)(nil), // 7: treasurehunterx.PlayerMeta - (*Treasure)(nil), // 8: treasurehunterx.Treasure - (*Bullet)(nil), // 9: treasurehunterx.Bullet - (*Trap)(nil), // 10: treasurehunterx.Trap - (*SpeedShoe)(nil), // 11: treasurehunterx.SpeedShoe - (*Pumpkin)(nil), // 12: treasurehunterx.Pumpkin - (*GuardTower)(nil), // 13: treasurehunterx.GuardTower - (*RoomDownsyncFrame)(nil), // 14: treasurehunterx.RoomDownsyncFrame - (*InputFrameUpsync)(nil), // 15: treasurehunterx.InputFrameUpsync - (*InputFrameDownsync)(nil), // 16: treasurehunterx.InputFrameDownsync - (*HeartbeatUpsync)(nil), // 17: treasurehunterx.HeartbeatUpsync - (*WsReq)(nil), // 18: treasurehunterx.WsReq - (*WsResp)(nil), // 19: treasurehunterx.WsResp - nil, // 20: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry - nil, // 21: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry - nil, // 22: treasurehunterx.RoomDownsyncFrame.PlayersEntry - nil, // 23: treasurehunterx.RoomDownsyncFrame.TreasuresEntry - nil, // 24: treasurehunterx.RoomDownsyncFrame.TrapsEntry - nil, // 25: treasurehunterx.RoomDownsyncFrame.BulletsEntry - nil, // 26: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry - nil, // 27: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry - nil, // 28: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry + (*RoomDownsyncFrame)(nil), // 8: treasurehunterx.RoomDownsyncFrame + (*InputFrameUpsync)(nil), // 9: treasurehunterx.InputFrameUpsync + (*InputFrameDownsync)(nil), // 10: treasurehunterx.InputFrameDownsync + (*HeartbeatUpsync)(nil), // 11: treasurehunterx.HeartbeatUpsync + (*WsReq)(nil), // 12: treasurehunterx.WsReq + (*WsResp)(nil), // 13: treasurehunterx.WsResp + nil, // 14: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry + nil, // 15: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry + nil, // 16: treasurehunterx.RoomDownsyncFrame.PlayersEntry + nil, // 17: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry } var file_room_downsync_frame_proto_depIdxs = []int32{ 1, // 0: treasurehunterx.Polygon2D.Anchor:type_name -> treasurehunterx.Vec2D 1, // 1: treasurehunterx.Polygon2D.Points:type_name -> treasurehunterx.Vec2D 1, // 2: treasurehunterx.Vec2DList.vec2DList:type_name -> treasurehunterx.Vec2D 2, // 3: treasurehunterx.Polygon2DList.polygon2DList:type_name -> treasurehunterx.Polygon2D - 20, // 4: treasurehunterx.BattleColliderInfo.strToVec2DListMap:type_name -> treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry - 21, // 5: treasurehunterx.BattleColliderInfo.strToPolygon2DListMap:type_name -> treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry + 14, // 4: treasurehunterx.BattleColliderInfo.strToVec2DListMap:type_name -> treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry + 15, // 5: treasurehunterx.BattleColliderInfo.strToPolygon2DListMap:type_name -> treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry 0, // 6: treasurehunterx.Player.dir:type_name -> treasurehunterx.Direction - 1, // 7: treasurehunterx.Bullet.startAtPoint:type_name -> treasurehunterx.Vec2D - 1, // 8: treasurehunterx.Bullet.endAtPoint:type_name -> treasurehunterx.Vec2D - 22, // 9: treasurehunterx.RoomDownsyncFrame.players:type_name -> treasurehunterx.RoomDownsyncFrame.PlayersEntry - 23, // 10: treasurehunterx.RoomDownsyncFrame.treasures:type_name -> treasurehunterx.RoomDownsyncFrame.TreasuresEntry - 24, // 11: treasurehunterx.RoomDownsyncFrame.traps:type_name -> treasurehunterx.RoomDownsyncFrame.TrapsEntry - 25, // 12: treasurehunterx.RoomDownsyncFrame.bullets:type_name -> treasurehunterx.RoomDownsyncFrame.BulletsEntry - 26, // 13: treasurehunterx.RoomDownsyncFrame.speedShoes:type_name -> treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry - 27, // 14: treasurehunterx.RoomDownsyncFrame.guardTowers:type_name -> treasurehunterx.RoomDownsyncFrame.GuardTowersEntry - 28, // 15: treasurehunterx.RoomDownsyncFrame.playerMetas:type_name -> treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry - 15, // 16: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync - 17, // 17: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync - 14, // 18: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame - 16, // 19: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync - 5, // 20: treasurehunterx.WsResp.bciFrame:type_name -> treasurehunterx.BattleColliderInfo - 3, // 21: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> treasurehunterx.Vec2DList - 4, // 22: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> treasurehunterx.Polygon2DList - 6, // 23: treasurehunterx.RoomDownsyncFrame.PlayersEntry.value:type_name -> treasurehunterx.Player - 8, // 24: treasurehunterx.RoomDownsyncFrame.TreasuresEntry.value:type_name -> treasurehunterx.Treasure - 10, // 25: treasurehunterx.RoomDownsyncFrame.TrapsEntry.value:type_name -> treasurehunterx.Trap - 9, // 26: treasurehunterx.RoomDownsyncFrame.BulletsEntry.value:type_name -> treasurehunterx.Bullet - 11, // 27: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry.value:type_name -> treasurehunterx.SpeedShoe - 13, // 28: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry.value:type_name -> treasurehunterx.GuardTower - 7, // 29: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry.value:type_name -> treasurehunterx.PlayerMeta - 30, // [30:30] is the sub-list for method output_type - 30, // [30:30] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 16, // 7: treasurehunterx.RoomDownsyncFrame.players:type_name -> treasurehunterx.RoomDownsyncFrame.PlayersEntry + 17, // 8: treasurehunterx.RoomDownsyncFrame.playerMetas:type_name -> treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry + 9, // 9: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync + 11, // 10: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync + 8, // 11: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame + 10, // 12: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync + 5, // 13: treasurehunterx.WsResp.bciFrame:type_name -> treasurehunterx.BattleColliderInfo + 3, // 14: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> treasurehunterx.Vec2DList + 4, // 15: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> treasurehunterx.Polygon2DList + 6, // 16: treasurehunterx.RoomDownsyncFrame.PlayersEntry.value:type_name -> treasurehunterx.Player + 7, // 17: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry.value:type_name -> treasurehunterx.PlayerMeta + 18, // [18:18] is the sub-list for method output_type + 18, // [18:18] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_room_downsync_frame_proto_init() } @@ -2097,78 +1390,6 @@ func file_room_downsync_frame_proto_init() { } } file_room_downsync_frame_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Treasure); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_room_downsync_frame_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bullet); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_room_downsync_frame_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Trap); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_room_downsync_frame_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SpeedShoe); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_room_downsync_frame_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Pumpkin); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_room_downsync_frame_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GuardTower); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_room_downsync_frame_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RoomDownsyncFrame); i { case 0: return &v.state @@ -2180,7 +1401,7 @@ func file_room_downsync_frame_proto_init() { return nil } } - file_room_downsync_frame_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_room_downsync_frame_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InputFrameUpsync); i { case 0: return &v.state @@ -2192,7 +1413,7 @@ func file_room_downsync_frame_proto_init() { return nil } } - file_room_downsync_frame_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_room_downsync_frame_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InputFrameDownsync); i { case 0: return &v.state @@ -2204,7 +1425,7 @@ func file_room_downsync_frame_proto_init() { return nil } } - file_room_downsync_frame_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_room_downsync_frame_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HeartbeatUpsync); i { case 0: return &v.state @@ -2216,7 +1437,7 @@ func file_room_downsync_frame_proto_init() { return nil } } - file_room_downsync_frame_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_room_downsync_frame_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WsReq); i { case 0: return &v.state @@ -2228,7 +1449,7 @@ func file_room_downsync_frame_proto_init() { return nil } } - file_room_downsync_frame_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_room_downsync_frame_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WsResp); i { case 0: return &v.state @@ -2247,7 +1468,7 @@ func file_room_downsync_frame_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_room_downsync_frame_proto_rawDesc, NumEnums: 0, - NumMessages: 29, + NumMessages: 18, NumExtensions: 0, NumServices: 0, }, diff --git a/frontend/assets/resources/pbfiles/room_downsync_frame.proto b/frontend/assets/resources/pbfiles/room_downsync_frame.proto index 9f414cd..a48aa0f 100644 --- a/frontend/assets/resources/pbfiles/room_downsync_frame.proto +++ b/frontend/assets/resources/pbfiles/room_downsync_frame.proto @@ -61,73 +61,13 @@ message PlayerMeta { int32 joinIndex = 5; } -message Treasure { - int32 id = 1; - int32 localIdInBattle = 2; - int32 score = 3; - double x = 4; - double y = 5; - bool removed = 6; - int32 type = 7; -} - -message Bullet { - int32 localIdInBattle = 1; - double linearSpeed = 2; - double x = 3; - double y = 4; - bool removed = 5; - Vec2D startAtPoint = 6; - Vec2D endAtPoint = 7; -} - -message Trap { - int32 id = 1; - int32 localIdInBattle = 2; - int32 type = 3; - double x = 4; - double y = 5; - bool removed = 6; -} - -message SpeedShoe { - int32 id = 1; - int32 localIdInBattle = 2; - double x = 3; - double y = 4; - bool removed = 5; - int32 type = 6; -} - -message Pumpkin { - int32 localIdInBattle = 1; - double linearSpeed = 2; - double x = 3; - double y = 4; - bool removed = 5; -} - -message GuardTower { - int32 id = 1; - int32 localIdInBattle = 2; - int32 type = 3; - double x = 4; - double y = 5; - bool removed = 6; -} - message RoomDownsyncFrame { int32 id = 1; int32 refFrameId = 2; map players = 3; int64 sentAt = 4; int64 countdownNanos = 5; - map treasures = 6; - map traps = 7; - map bullets = 8; - map speedShoes = 9; - map guardTowers = 10; - map playerMetas = 11; + map playerMetas = 6; } message InputFrameUpsync { diff --git a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js index bd97e12..3cb3729 100644 --- a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js +++ b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js @@ -2381,1953 +2381,6 @@ $root.treasurehunterx = (function() { return PlayerMeta; })(); - treasurehunterx.Treasure = (function() { - - /** - * Properties of a Treasure. - * @memberof treasurehunterx - * @interface ITreasure - * @property {number|null} [id] Treasure id - * @property {number|null} [localIdInBattle] Treasure localIdInBattle - * @property {number|null} [score] Treasure score - * @property {number|null} [x] Treasure x - * @property {number|null} [y] Treasure y - * @property {boolean|null} [removed] Treasure removed - * @property {number|null} [type] Treasure type - */ - - /** - * Constructs a new Treasure. - * @memberof treasurehunterx - * @classdesc Represents a Treasure. - * @implements ITreasure - * @constructor - * @param {treasurehunterx.ITreasure=} [properties] Properties to set - */ - function Treasure(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * Treasure id. - * @member {number} id - * @memberof treasurehunterx.Treasure - * @instance - */ - Treasure.prototype.id = 0; - - /** - * Treasure localIdInBattle. - * @member {number} localIdInBattle - * @memberof treasurehunterx.Treasure - * @instance - */ - Treasure.prototype.localIdInBattle = 0; - - /** - * Treasure score. - * @member {number} score - * @memberof treasurehunterx.Treasure - * @instance - */ - Treasure.prototype.score = 0; - - /** - * Treasure x. - * @member {number} x - * @memberof treasurehunterx.Treasure - * @instance - */ - Treasure.prototype.x = 0; - - /** - * Treasure y. - * @member {number} y - * @memberof treasurehunterx.Treasure - * @instance - */ - Treasure.prototype.y = 0; - - /** - * Treasure removed. - * @member {boolean} removed - * @memberof treasurehunterx.Treasure - * @instance - */ - Treasure.prototype.removed = false; - - /** - * Treasure type. - * @member {number} type - * @memberof treasurehunterx.Treasure - * @instance - */ - Treasure.prototype.type = 0; - - /** - * Creates a new Treasure instance using the specified properties. - * @function create - * @memberof treasurehunterx.Treasure - * @static - * @param {treasurehunterx.ITreasure=} [properties] Properties to set - * @returns {treasurehunterx.Treasure} Treasure instance - */ - Treasure.create = function create(properties) { - return new Treasure(properties); - }; - - /** - * Encodes the specified Treasure message. Does not implicitly {@link treasurehunterx.Treasure.verify|verify} messages. - * @function encode - * @memberof treasurehunterx.Treasure - * @static - * @param {treasurehunterx.Treasure} message Treasure message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Treasure.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id); - if (message.localIdInBattle != null && Object.hasOwnProperty.call(message, "localIdInBattle")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.localIdInBattle); - if (message.score != null && Object.hasOwnProperty.call(message, "score")) - writer.uint32(/* id 3, wireType 0 =*/24).int32(message.score); - if (message.x != null && Object.hasOwnProperty.call(message, "x")) - writer.uint32(/* id 4, wireType 1 =*/33).double(message.x); - if (message.y != null && Object.hasOwnProperty.call(message, "y")) - writer.uint32(/* id 5, wireType 1 =*/41).double(message.y); - if (message.removed != null && Object.hasOwnProperty.call(message, "removed")) - writer.uint32(/* id 6, wireType 0 =*/48).bool(message.removed); - if (message.type != null && Object.hasOwnProperty.call(message, "type")) - writer.uint32(/* id 7, wireType 0 =*/56).int32(message.type); - return writer; - }; - - /** - * Encodes the specified Treasure message, length delimited. Does not implicitly {@link treasurehunterx.Treasure.verify|verify} messages. - * @function encodeDelimited - * @memberof treasurehunterx.Treasure - * @static - * @param {treasurehunterx.Treasure} message Treasure message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Treasure.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a Treasure message from the specified reader or buffer. - * @function decode - * @memberof treasurehunterx.Treasure - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {treasurehunterx.Treasure} Treasure - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Treasure.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.Treasure(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.id = reader.int32(); - break; - } - case 2: { - message.localIdInBattle = reader.int32(); - break; - } - case 3: { - message.score = reader.int32(); - break; - } - case 4: { - message.x = reader.double(); - break; - } - case 5: { - message.y = reader.double(); - break; - } - case 6: { - message.removed = reader.bool(); - break; - } - case 7: { - message.type = reader.int32(); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a Treasure message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof treasurehunterx.Treasure - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {treasurehunterx.Treasure} Treasure - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Treasure.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a Treasure message. - * @function verify - * @memberof treasurehunterx.Treasure - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - Treasure.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isInteger(message.id)) - return "id: integer expected"; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - if (!$util.isInteger(message.localIdInBattle)) - return "localIdInBattle: integer expected"; - if (message.score != null && message.hasOwnProperty("score")) - if (!$util.isInteger(message.score)) - return "score: integer expected"; - if (message.x != null && message.hasOwnProperty("x")) - if (typeof message.x !== "number") - return "x: number expected"; - if (message.y != null && message.hasOwnProperty("y")) - if (typeof message.y !== "number") - return "y: number expected"; - if (message.removed != null && message.hasOwnProperty("removed")) - if (typeof message.removed !== "boolean") - return "removed: boolean expected"; - if (message.type != null && message.hasOwnProperty("type")) - if (!$util.isInteger(message.type)) - return "type: integer expected"; - return null; - }; - - /** - * Creates a Treasure message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof treasurehunterx.Treasure - * @static - * @param {Object.} object Plain object - * @returns {treasurehunterx.Treasure} Treasure - */ - Treasure.fromObject = function fromObject(object) { - if (object instanceof $root.treasurehunterx.Treasure) - return object; - var message = new $root.treasurehunterx.Treasure(); - if (object.id != null) - message.id = object.id | 0; - if (object.localIdInBattle != null) - message.localIdInBattle = object.localIdInBattle | 0; - if (object.score != null) - message.score = object.score | 0; - if (object.x != null) - message.x = Number(object.x); - if (object.y != null) - message.y = Number(object.y); - if (object.removed != null) - message.removed = Boolean(object.removed); - if (object.type != null) - message.type = object.type | 0; - return message; - }; - - /** - * Creates a plain object from a Treasure message. Also converts values to other types if specified. - * @function toObject - * @memberof treasurehunterx.Treasure - * @static - * @param {treasurehunterx.Treasure} message Treasure - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - Treasure.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.id = 0; - object.localIdInBattle = 0; - object.score = 0; - object.x = 0; - object.y = 0; - object.removed = false; - object.type = 0; - } - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - object.localIdInBattle = message.localIdInBattle; - if (message.score != null && message.hasOwnProperty("score")) - object.score = message.score; - if (message.x != null && message.hasOwnProperty("x")) - object.x = options.json && !isFinite(message.x) ? String(message.x) : message.x; - if (message.y != null && message.hasOwnProperty("y")) - object.y = options.json && !isFinite(message.y) ? String(message.y) : message.y; - if (message.removed != null && message.hasOwnProperty("removed")) - object.removed = message.removed; - if (message.type != null && message.hasOwnProperty("type")) - object.type = message.type; - return object; - }; - - /** - * Converts this Treasure to JSON. - * @function toJSON - * @memberof treasurehunterx.Treasure - * @instance - * @returns {Object.} JSON object - */ - Treasure.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for Treasure - * @function getTypeUrl - * @memberof treasurehunterx.Treasure - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - Treasure.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/treasurehunterx.Treasure"; - }; - - return Treasure; - })(); - - treasurehunterx.Bullet = (function() { - - /** - * Properties of a Bullet. - * @memberof treasurehunterx - * @interface IBullet - * @property {number|null} [localIdInBattle] Bullet localIdInBattle - * @property {number|null} [linearSpeed] Bullet linearSpeed - * @property {number|null} [x] Bullet x - * @property {number|null} [y] Bullet y - * @property {boolean|null} [removed] Bullet removed - * @property {treasurehunterx.Vec2D|null} [startAtPoint] Bullet startAtPoint - * @property {treasurehunterx.Vec2D|null} [endAtPoint] Bullet endAtPoint - */ - - /** - * Constructs a new Bullet. - * @memberof treasurehunterx - * @classdesc Represents a Bullet. - * @implements IBullet - * @constructor - * @param {treasurehunterx.IBullet=} [properties] Properties to set - */ - function Bullet(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * Bullet localIdInBattle. - * @member {number} localIdInBattle - * @memberof treasurehunterx.Bullet - * @instance - */ - Bullet.prototype.localIdInBattle = 0; - - /** - * Bullet linearSpeed. - * @member {number} linearSpeed - * @memberof treasurehunterx.Bullet - * @instance - */ - Bullet.prototype.linearSpeed = 0; - - /** - * Bullet x. - * @member {number} x - * @memberof treasurehunterx.Bullet - * @instance - */ - Bullet.prototype.x = 0; - - /** - * Bullet y. - * @member {number} y - * @memberof treasurehunterx.Bullet - * @instance - */ - Bullet.prototype.y = 0; - - /** - * Bullet removed. - * @member {boolean} removed - * @memberof treasurehunterx.Bullet - * @instance - */ - Bullet.prototype.removed = false; - - /** - * Bullet startAtPoint. - * @member {treasurehunterx.Vec2D|null|undefined} startAtPoint - * @memberof treasurehunterx.Bullet - * @instance - */ - Bullet.prototype.startAtPoint = null; - - /** - * Bullet endAtPoint. - * @member {treasurehunterx.Vec2D|null|undefined} endAtPoint - * @memberof treasurehunterx.Bullet - * @instance - */ - Bullet.prototype.endAtPoint = null; - - /** - * Creates a new Bullet instance using the specified properties. - * @function create - * @memberof treasurehunterx.Bullet - * @static - * @param {treasurehunterx.IBullet=} [properties] Properties to set - * @returns {treasurehunterx.Bullet} Bullet instance - */ - Bullet.create = function create(properties) { - return new Bullet(properties); - }; - - /** - * Encodes the specified Bullet message. Does not implicitly {@link treasurehunterx.Bullet.verify|verify} messages. - * @function encode - * @memberof treasurehunterx.Bullet - * @static - * @param {treasurehunterx.Bullet} message Bullet message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Bullet.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.localIdInBattle != null && Object.hasOwnProperty.call(message, "localIdInBattle")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.localIdInBattle); - if (message.linearSpeed != null && Object.hasOwnProperty.call(message, "linearSpeed")) - writer.uint32(/* id 2, wireType 1 =*/17).double(message.linearSpeed); - if (message.x != null && Object.hasOwnProperty.call(message, "x")) - writer.uint32(/* id 3, wireType 1 =*/25).double(message.x); - if (message.y != null && Object.hasOwnProperty.call(message, "y")) - writer.uint32(/* id 4, wireType 1 =*/33).double(message.y); - if (message.removed != null && Object.hasOwnProperty.call(message, "removed")) - writer.uint32(/* id 5, wireType 0 =*/40).bool(message.removed); - if (message.startAtPoint != null && Object.hasOwnProperty.call(message, "startAtPoint")) - $root.treasurehunterx.Vec2D.encode(message.startAtPoint, writer.uint32(/* id 6, wireType 2 =*/50).fork()).ldelim(); - if (message.endAtPoint != null && Object.hasOwnProperty.call(message, "endAtPoint")) - $root.treasurehunterx.Vec2D.encode(message.endAtPoint, writer.uint32(/* id 7, wireType 2 =*/58).fork()).ldelim(); - return writer; - }; - - /** - * Encodes the specified Bullet message, length delimited. Does not implicitly {@link treasurehunterx.Bullet.verify|verify} messages. - * @function encodeDelimited - * @memberof treasurehunterx.Bullet - * @static - * @param {treasurehunterx.Bullet} message Bullet message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Bullet.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a Bullet message from the specified reader or buffer. - * @function decode - * @memberof treasurehunterx.Bullet - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {treasurehunterx.Bullet} Bullet - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Bullet.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.Bullet(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.localIdInBattle = reader.int32(); - break; - } - case 2: { - message.linearSpeed = reader.double(); - break; - } - case 3: { - message.x = reader.double(); - break; - } - case 4: { - message.y = reader.double(); - break; - } - case 5: { - message.removed = reader.bool(); - break; - } - case 6: { - message.startAtPoint = $root.treasurehunterx.Vec2D.decode(reader, reader.uint32()); - break; - } - case 7: { - message.endAtPoint = $root.treasurehunterx.Vec2D.decode(reader, reader.uint32()); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a Bullet message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof treasurehunterx.Bullet - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {treasurehunterx.Bullet} Bullet - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Bullet.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a Bullet message. - * @function verify - * @memberof treasurehunterx.Bullet - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - Bullet.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - if (!$util.isInteger(message.localIdInBattle)) - return "localIdInBattle: integer expected"; - if (message.linearSpeed != null && message.hasOwnProperty("linearSpeed")) - if (typeof message.linearSpeed !== "number") - return "linearSpeed: number expected"; - if (message.x != null && message.hasOwnProperty("x")) - if (typeof message.x !== "number") - return "x: number expected"; - if (message.y != null && message.hasOwnProperty("y")) - if (typeof message.y !== "number") - return "y: number expected"; - if (message.removed != null && message.hasOwnProperty("removed")) - if (typeof message.removed !== "boolean") - return "removed: boolean expected"; - if (message.startAtPoint != null && message.hasOwnProperty("startAtPoint")) { - var error = $root.treasurehunterx.Vec2D.verify(message.startAtPoint); - if (error) - return "startAtPoint." + error; - } - if (message.endAtPoint != null && message.hasOwnProperty("endAtPoint")) { - var error = $root.treasurehunterx.Vec2D.verify(message.endAtPoint); - if (error) - return "endAtPoint." + error; - } - return null; - }; - - /** - * Creates a Bullet message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof treasurehunterx.Bullet - * @static - * @param {Object.} object Plain object - * @returns {treasurehunterx.Bullet} Bullet - */ - Bullet.fromObject = function fromObject(object) { - if (object instanceof $root.treasurehunterx.Bullet) - return object; - var message = new $root.treasurehunterx.Bullet(); - if (object.localIdInBattle != null) - message.localIdInBattle = object.localIdInBattle | 0; - if (object.linearSpeed != null) - message.linearSpeed = Number(object.linearSpeed); - if (object.x != null) - message.x = Number(object.x); - if (object.y != null) - message.y = Number(object.y); - if (object.removed != null) - message.removed = Boolean(object.removed); - if (object.startAtPoint != null) { - if (typeof object.startAtPoint !== "object") - throw TypeError(".treasurehunterx.Bullet.startAtPoint: object expected"); - message.startAtPoint = $root.treasurehunterx.Vec2D.fromObject(object.startAtPoint); - } - if (object.endAtPoint != null) { - if (typeof object.endAtPoint !== "object") - throw TypeError(".treasurehunterx.Bullet.endAtPoint: object expected"); - message.endAtPoint = $root.treasurehunterx.Vec2D.fromObject(object.endAtPoint); - } - return message; - }; - - /** - * Creates a plain object from a Bullet message. Also converts values to other types if specified. - * @function toObject - * @memberof treasurehunterx.Bullet - * @static - * @param {treasurehunterx.Bullet} message Bullet - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - Bullet.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.localIdInBattle = 0; - object.linearSpeed = 0; - object.x = 0; - object.y = 0; - object.removed = false; - object.startAtPoint = null; - object.endAtPoint = null; - } - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - object.localIdInBattle = message.localIdInBattle; - if (message.linearSpeed != null && message.hasOwnProperty("linearSpeed")) - object.linearSpeed = options.json && !isFinite(message.linearSpeed) ? String(message.linearSpeed) : message.linearSpeed; - if (message.x != null && message.hasOwnProperty("x")) - object.x = options.json && !isFinite(message.x) ? String(message.x) : message.x; - if (message.y != null && message.hasOwnProperty("y")) - object.y = options.json && !isFinite(message.y) ? String(message.y) : message.y; - if (message.removed != null && message.hasOwnProperty("removed")) - object.removed = message.removed; - if (message.startAtPoint != null && message.hasOwnProperty("startAtPoint")) - object.startAtPoint = $root.treasurehunterx.Vec2D.toObject(message.startAtPoint, options); - if (message.endAtPoint != null && message.hasOwnProperty("endAtPoint")) - object.endAtPoint = $root.treasurehunterx.Vec2D.toObject(message.endAtPoint, options); - return object; - }; - - /** - * Converts this Bullet to JSON. - * @function toJSON - * @memberof treasurehunterx.Bullet - * @instance - * @returns {Object.} JSON object - */ - Bullet.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for Bullet - * @function getTypeUrl - * @memberof treasurehunterx.Bullet - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - Bullet.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/treasurehunterx.Bullet"; - }; - - return Bullet; - })(); - - treasurehunterx.Trap = (function() { - - /** - * Properties of a Trap. - * @memberof treasurehunterx - * @interface ITrap - * @property {number|null} [id] Trap id - * @property {number|null} [localIdInBattle] Trap localIdInBattle - * @property {number|null} [type] Trap type - * @property {number|null} [x] Trap x - * @property {number|null} [y] Trap y - * @property {boolean|null} [removed] Trap removed - */ - - /** - * Constructs a new Trap. - * @memberof treasurehunterx - * @classdesc Represents a Trap. - * @implements ITrap - * @constructor - * @param {treasurehunterx.ITrap=} [properties] Properties to set - */ - function Trap(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * Trap id. - * @member {number} id - * @memberof treasurehunterx.Trap - * @instance - */ - Trap.prototype.id = 0; - - /** - * Trap localIdInBattle. - * @member {number} localIdInBattle - * @memberof treasurehunterx.Trap - * @instance - */ - Trap.prototype.localIdInBattle = 0; - - /** - * Trap type. - * @member {number} type - * @memberof treasurehunterx.Trap - * @instance - */ - Trap.prototype.type = 0; - - /** - * Trap x. - * @member {number} x - * @memberof treasurehunterx.Trap - * @instance - */ - Trap.prototype.x = 0; - - /** - * Trap y. - * @member {number} y - * @memberof treasurehunterx.Trap - * @instance - */ - Trap.prototype.y = 0; - - /** - * Trap removed. - * @member {boolean} removed - * @memberof treasurehunterx.Trap - * @instance - */ - Trap.prototype.removed = false; - - /** - * Creates a new Trap instance using the specified properties. - * @function create - * @memberof treasurehunterx.Trap - * @static - * @param {treasurehunterx.ITrap=} [properties] Properties to set - * @returns {treasurehunterx.Trap} Trap instance - */ - Trap.create = function create(properties) { - return new Trap(properties); - }; - - /** - * Encodes the specified Trap message. Does not implicitly {@link treasurehunterx.Trap.verify|verify} messages. - * @function encode - * @memberof treasurehunterx.Trap - * @static - * @param {treasurehunterx.Trap} message Trap message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Trap.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id); - if (message.localIdInBattle != null && Object.hasOwnProperty.call(message, "localIdInBattle")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.localIdInBattle); - if (message.type != null && Object.hasOwnProperty.call(message, "type")) - writer.uint32(/* id 3, wireType 0 =*/24).int32(message.type); - if (message.x != null && Object.hasOwnProperty.call(message, "x")) - writer.uint32(/* id 4, wireType 1 =*/33).double(message.x); - if (message.y != null && Object.hasOwnProperty.call(message, "y")) - writer.uint32(/* id 5, wireType 1 =*/41).double(message.y); - if (message.removed != null && Object.hasOwnProperty.call(message, "removed")) - writer.uint32(/* id 6, wireType 0 =*/48).bool(message.removed); - return writer; - }; - - /** - * Encodes the specified Trap message, length delimited. Does not implicitly {@link treasurehunterx.Trap.verify|verify} messages. - * @function encodeDelimited - * @memberof treasurehunterx.Trap - * @static - * @param {treasurehunterx.Trap} message Trap message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Trap.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a Trap message from the specified reader or buffer. - * @function decode - * @memberof treasurehunterx.Trap - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {treasurehunterx.Trap} Trap - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Trap.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.Trap(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.id = reader.int32(); - break; - } - case 2: { - message.localIdInBattle = reader.int32(); - break; - } - case 3: { - message.type = reader.int32(); - break; - } - case 4: { - message.x = reader.double(); - break; - } - case 5: { - message.y = reader.double(); - break; - } - case 6: { - message.removed = reader.bool(); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a Trap message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof treasurehunterx.Trap - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {treasurehunterx.Trap} Trap - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Trap.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a Trap message. - * @function verify - * @memberof treasurehunterx.Trap - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - Trap.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isInteger(message.id)) - return "id: integer expected"; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - if (!$util.isInteger(message.localIdInBattle)) - return "localIdInBattle: integer expected"; - if (message.type != null && message.hasOwnProperty("type")) - if (!$util.isInteger(message.type)) - return "type: integer expected"; - if (message.x != null && message.hasOwnProperty("x")) - if (typeof message.x !== "number") - return "x: number expected"; - if (message.y != null && message.hasOwnProperty("y")) - if (typeof message.y !== "number") - return "y: number expected"; - if (message.removed != null && message.hasOwnProperty("removed")) - if (typeof message.removed !== "boolean") - return "removed: boolean expected"; - return null; - }; - - /** - * Creates a Trap message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof treasurehunterx.Trap - * @static - * @param {Object.} object Plain object - * @returns {treasurehunterx.Trap} Trap - */ - Trap.fromObject = function fromObject(object) { - if (object instanceof $root.treasurehunterx.Trap) - return object; - var message = new $root.treasurehunterx.Trap(); - if (object.id != null) - message.id = object.id | 0; - if (object.localIdInBattle != null) - message.localIdInBattle = object.localIdInBattle | 0; - if (object.type != null) - message.type = object.type | 0; - if (object.x != null) - message.x = Number(object.x); - if (object.y != null) - message.y = Number(object.y); - if (object.removed != null) - message.removed = Boolean(object.removed); - return message; - }; - - /** - * Creates a plain object from a Trap message. Also converts values to other types if specified. - * @function toObject - * @memberof treasurehunterx.Trap - * @static - * @param {treasurehunterx.Trap} message Trap - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - Trap.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.id = 0; - object.localIdInBattle = 0; - object.type = 0; - object.x = 0; - object.y = 0; - object.removed = false; - } - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - object.localIdInBattle = message.localIdInBattle; - if (message.type != null && message.hasOwnProperty("type")) - object.type = message.type; - if (message.x != null && message.hasOwnProperty("x")) - object.x = options.json && !isFinite(message.x) ? String(message.x) : message.x; - if (message.y != null && message.hasOwnProperty("y")) - object.y = options.json && !isFinite(message.y) ? String(message.y) : message.y; - if (message.removed != null && message.hasOwnProperty("removed")) - object.removed = message.removed; - return object; - }; - - /** - * Converts this Trap to JSON. - * @function toJSON - * @memberof treasurehunterx.Trap - * @instance - * @returns {Object.} JSON object - */ - Trap.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for Trap - * @function getTypeUrl - * @memberof treasurehunterx.Trap - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - Trap.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/treasurehunterx.Trap"; - }; - - return Trap; - })(); - - treasurehunterx.SpeedShoe = (function() { - - /** - * Properties of a SpeedShoe. - * @memberof treasurehunterx - * @interface ISpeedShoe - * @property {number|null} [id] SpeedShoe id - * @property {number|null} [localIdInBattle] SpeedShoe localIdInBattle - * @property {number|null} [x] SpeedShoe x - * @property {number|null} [y] SpeedShoe y - * @property {boolean|null} [removed] SpeedShoe removed - * @property {number|null} [type] SpeedShoe type - */ - - /** - * Constructs a new SpeedShoe. - * @memberof treasurehunterx - * @classdesc Represents a SpeedShoe. - * @implements ISpeedShoe - * @constructor - * @param {treasurehunterx.ISpeedShoe=} [properties] Properties to set - */ - function SpeedShoe(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SpeedShoe id. - * @member {number} id - * @memberof treasurehunterx.SpeedShoe - * @instance - */ - SpeedShoe.prototype.id = 0; - - /** - * SpeedShoe localIdInBattle. - * @member {number} localIdInBattle - * @memberof treasurehunterx.SpeedShoe - * @instance - */ - SpeedShoe.prototype.localIdInBattle = 0; - - /** - * SpeedShoe x. - * @member {number} x - * @memberof treasurehunterx.SpeedShoe - * @instance - */ - SpeedShoe.prototype.x = 0; - - /** - * SpeedShoe y. - * @member {number} y - * @memberof treasurehunterx.SpeedShoe - * @instance - */ - SpeedShoe.prototype.y = 0; - - /** - * SpeedShoe removed. - * @member {boolean} removed - * @memberof treasurehunterx.SpeedShoe - * @instance - */ - SpeedShoe.prototype.removed = false; - - /** - * SpeedShoe type. - * @member {number} type - * @memberof treasurehunterx.SpeedShoe - * @instance - */ - SpeedShoe.prototype.type = 0; - - /** - * Creates a new SpeedShoe instance using the specified properties. - * @function create - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {treasurehunterx.ISpeedShoe=} [properties] Properties to set - * @returns {treasurehunterx.SpeedShoe} SpeedShoe instance - */ - SpeedShoe.create = function create(properties) { - return new SpeedShoe(properties); - }; - - /** - * Encodes the specified SpeedShoe message. Does not implicitly {@link treasurehunterx.SpeedShoe.verify|verify} messages. - * @function encode - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {treasurehunterx.SpeedShoe} message SpeedShoe message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SpeedShoe.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id); - if (message.localIdInBattle != null && Object.hasOwnProperty.call(message, "localIdInBattle")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.localIdInBattle); - if (message.x != null && Object.hasOwnProperty.call(message, "x")) - writer.uint32(/* id 3, wireType 1 =*/25).double(message.x); - if (message.y != null && Object.hasOwnProperty.call(message, "y")) - writer.uint32(/* id 4, wireType 1 =*/33).double(message.y); - if (message.removed != null && Object.hasOwnProperty.call(message, "removed")) - writer.uint32(/* id 5, wireType 0 =*/40).bool(message.removed); - if (message.type != null && Object.hasOwnProperty.call(message, "type")) - writer.uint32(/* id 6, wireType 0 =*/48).int32(message.type); - return writer; - }; - - /** - * Encodes the specified SpeedShoe message, length delimited. Does not implicitly {@link treasurehunterx.SpeedShoe.verify|verify} messages. - * @function encodeDelimited - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {treasurehunterx.SpeedShoe} message SpeedShoe message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SpeedShoe.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SpeedShoe message from the specified reader or buffer. - * @function decode - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {treasurehunterx.SpeedShoe} SpeedShoe - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SpeedShoe.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.SpeedShoe(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.id = reader.int32(); - break; - } - case 2: { - message.localIdInBattle = reader.int32(); - break; - } - case 3: { - message.x = reader.double(); - break; - } - case 4: { - message.y = reader.double(); - break; - } - case 5: { - message.removed = reader.bool(); - break; - } - case 6: { - message.type = reader.int32(); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SpeedShoe message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {treasurehunterx.SpeedShoe} SpeedShoe - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SpeedShoe.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SpeedShoe message. - * @function verify - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SpeedShoe.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isInteger(message.id)) - return "id: integer expected"; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - if (!$util.isInteger(message.localIdInBattle)) - return "localIdInBattle: integer expected"; - if (message.x != null && message.hasOwnProperty("x")) - if (typeof message.x !== "number") - return "x: number expected"; - if (message.y != null && message.hasOwnProperty("y")) - if (typeof message.y !== "number") - return "y: number expected"; - if (message.removed != null && message.hasOwnProperty("removed")) - if (typeof message.removed !== "boolean") - return "removed: boolean expected"; - if (message.type != null && message.hasOwnProperty("type")) - if (!$util.isInteger(message.type)) - return "type: integer expected"; - return null; - }; - - /** - * Creates a SpeedShoe message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {Object.} object Plain object - * @returns {treasurehunterx.SpeedShoe} SpeedShoe - */ - SpeedShoe.fromObject = function fromObject(object) { - if (object instanceof $root.treasurehunterx.SpeedShoe) - return object; - var message = new $root.treasurehunterx.SpeedShoe(); - if (object.id != null) - message.id = object.id | 0; - if (object.localIdInBattle != null) - message.localIdInBattle = object.localIdInBattle | 0; - if (object.x != null) - message.x = Number(object.x); - if (object.y != null) - message.y = Number(object.y); - if (object.removed != null) - message.removed = Boolean(object.removed); - if (object.type != null) - message.type = object.type | 0; - return message; - }; - - /** - * Creates a plain object from a SpeedShoe message. Also converts values to other types if specified. - * @function toObject - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {treasurehunterx.SpeedShoe} message SpeedShoe - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SpeedShoe.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.id = 0; - object.localIdInBattle = 0; - object.x = 0; - object.y = 0; - object.removed = false; - object.type = 0; - } - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - object.localIdInBattle = message.localIdInBattle; - if (message.x != null && message.hasOwnProperty("x")) - object.x = options.json && !isFinite(message.x) ? String(message.x) : message.x; - if (message.y != null && message.hasOwnProperty("y")) - object.y = options.json && !isFinite(message.y) ? String(message.y) : message.y; - if (message.removed != null && message.hasOwnProperty("removed")) - object.removed = message.removed; - if (message.type != null && message.hasOwnProperty("type")) - object.type = message.type; - return object; - }; - - /** - * Converts this SpeedShoe to JSON. - * @function toJSON - * @memberof treasurehunterx.SpeedShoe - * @instance - * @returns {Object.} JSON object - */ - SpeedShoe.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for SpeedShoe - * @function getTypeUrl - * @memberof treasurehunterx.SpeedShoe - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - SpeedShoe.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/treasurehunterx.SpeedShoe"; - }; - - return SpeedShoe; - })(); - - treasurehunterx.Pumpkin = (function() { - - /** - * Properties of a Pumpkin. - * @memberof treasurehunterx - * @interface IPumpkin - * @property {number|null} [localIdInBattle] Pumpkin localIdInBattle - * @property {number|null} [linearSpeed] Pumpkin linearSpeed - * @property {number|null} [x] Pumpkin x - * @property {number|null} [y] Pumpkin y - * @property {boolean|null} [removed] Pumpkin removed - */ - - /** - * Constructs a new Pumpkin. - * @memberof treasurehunterx - * @classdesc Represents a Pumpkin. - * @implements IPumpkin - * @constructor - * @param {treasurehunterx.IPumpkin=} [properties] Properties to set - */ - function Pumpkin(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * Pumpkin localIdInBattle. - * @member {number} localIdInBattle - * @memberof treasurehunterx.Pumpkin - * @instance - */ - Pumpkin.prototype.localIdInBattle = 0; - - /** - * Pumpkin linearSpeed. - * @member {number} linearSpeed - * @memberof treasurehunterx.Pumpkin - * @instance - */ - Pumpkin.prototype.linearSpeed = 0; - - /** - * Pumpkin x. - * @member {number} x - * @memberof treasurehunterx.Pumpkin - * @instance - */ - Pumpkin.prototype.x = 0; - - /** - * Pumpkin y. - * @member {number} y - * @memberof treasurehunterx.Pumpkin - * @instance - */ - Pumpkin.prototype.y = 0; - - /** - * Pumpkin removed. - * @member {boolean} removed - * @memberof treasurehunterx.Pumpkin - * @instance - */ - Pumpkin.prototype.removed = false; - - /** - * Creates a new Pumpkin instance using the specified properties. - * @function create - * @memberof treasurehunterx.Pumpkin - * @static - * @param {treasurehunterx.IPumpkin=} [properties] Properties to set - * @returns {treasurehunterx.Pumpkin} Pumpkin instance - */ - Pumpkin.create = function create(properties) { - return new Pumpkin(properties); - }; - - /** - * Encodes the specified Pumpkin message. Does not implicitly {@link treasurehunterx.Pumpkin.verify|verify} messages. - * @function encode - * @memberof treasurehunterx.Pumpkin - * @static - * @param {treasurehunterx.Pumpkin} message Pumpkin message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Pumpkin.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.localIdInBattle != null && Object.hasOwnProperty.call(message, "localIdInBattle")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.localIdInBattle); - if (message.linearSpeed != null && Object.hasOwnProperty.call(message, "linearSpeed")) - writer.uint32(/* id 2, wireType 1 =*/17).double(message.linearSpeed); - if (message.x != null && Object.hasOwnProperty.call(message, "x")) - writer.uint32(/* id 3, wireType 1 =*/25).double(message.x); - if (message.y != null && Object.hasOwnProperty.call(message, "y")) - writer.uint32(/* id 4, wireType 1 =*/33).double(message.y); - if (message.removed != null && Object.hasOwnProperty.call(message, "removed")) - writer.uint32(/* id 5, wireType 0 =*/40).bool(message.removed); - return writer; - }; - - /** - * Encodes the specified Pumpkin message, length delimited. Does not implicitly {@link treasurehunterx.Pumpkin.verify|verify} messages. - * @function encodeDelimited - * @memberof treasurehunterx.Pumpkin - * @static - * @param {treasurehunterx.Pumpkin} message Pumpkin message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Pumpkin.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a Pumpkin message from the specified reader or buffer. - * @function decode - * @memberof treasurehunterx.Pumpkin - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {treasurehunterx.Pumpkin} Pumpkin - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Pumpkin.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.Pumpkin(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.localIdInBattle = reader.int32(); - break; - } - case 2: { - message.linearSpeed = reader.double(); - break; - } - case 3: { - message.x = reader.double(); - break; - } - case 4: { - message.y = reader.double(); - break; - } - case 5: { - message.removed = reader.bool(); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a Pumpkin message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof treasurehunterx.Pumpkin - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {treasurehunterx.Pumpkin} Pumpkin - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Pumpkin.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a Pumpkin message. - * @function verify - * @memberof treasurehunterx.Pumpkin - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - Pumpkin.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - if (!$util.isInteger(message.localIdInBattle)) - return "localIdInBattle: integer expected"; - if (message.linearSpeed != null && message.hasOwnProperty("linearSpeed")) - if (typeof message.linearSpeed !== "number") - return "linearSpeed: number expected"; - if (message.x != null && message.hasOwnProperty("x")) - if (typeof message.x !== "number") - return "x: number expected"; - if (message.y != null && message.hasOwnProperty("y")) - if (typeof message.y !== "number") - return "y: number expected"; - if (message.removed != null && message.hasOwnProperty("removed")) - if (typeof message.removed !== "boolean") - return "removed: boolean expected"; - return null; - }; - - /** - * Creates a Pumpkin message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof treasurehunterx.Pumpkin - * @static - * @param {Object.} object Plain object - * @returns {treasurehunterx.Pumpkin} Pumpkin - */ - Pumpkin.fromObject = function fromObject(object) { - if (object instanceof $root.treasurehunterx.Pumpkin) - return object; - var message = new $root.treasurehunterx.Pumpkin(); - if (object.localIdInBattle != null) - message.localIdInBattle = object.localIdInBattle | 0; - if (object.linearSpeed != null) - message.linearSpeed = Number(object.linearSpeed); - if (object.x != null) - message.x = Number(object.x); - if (object.y != null) - message.y = Number(object.y); - if (object.removed != null) - message.removed = Boolean(object.removed); - return message; - }; - - /** - * Creates a plain object from a Pumpkin message. Also converts values to other types if specified. - * @function toObject - * @memberof treasurehunterx.Pumpkin - * @static - * @param {treasurehunterx.Pumpkin} message Pumpkin - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - Pumpkin.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.localIdInBattle = 0; - object.linearSpeed = 0; - object.x = 0; - object.y = 0; - object.removed = false; - } - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - object.localIdInBattle = message.localIdInBattle; - if (message.linearSpeed != null && message.hasOwnProperty("linearSpeed")) - object.linearSpeed = options.json && !isFinite(message.linearSpeed) ? String(message.linearSpeed) : message.linearSpeed; - if (message.x != null && message.hasOwnProperty("x")) - object.x = options.json && !isFinite(message.x) ? String(message.x) : message.x; - if (message.y != null && message.hasOwnProperty("y")) - object.y = options.json && !isFinite(message.y) ? String(message.y) : message.y; - if (message.removed != null && message.hasOwnProperty("removed")) - object.removed = message.removed; - return object; - }; - - /** - * Converts this Pumpkin to JSON. - * @function toJSON - * @memberof treasurehunterx.Pumpkin - * @instance - * @returns {Object.} JSON object - */ - Pumpkin.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for Pumpkin - * @function getTypeUrl - * @memberof treasurehunterx.Pumpkin - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - Pumpkin.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/treasurehunterx.Pumpkin"; - }; - - return Pumpkin; - })(); - - treasurehunterx.GuardTower = (function() { - - /** - * Properties of a GuardTower. - * @memberof treasurehunterx - * @interface IGuardTower - * @property {number|null} [id] GuardTower id - * @property {number|null} [localIdInBattle] GuardTower localIdInBattle - * @property {number|null} [type] GuardTower type - * @property {number|null} [x] GuardTower x - * @property {number|null} [y] GuardTower y - * @property {boolean|null} [removed] GuardTower removed - */ - - /** - * Constructs a new GuardTower. - * @memberof treasurehunterx - * @classdesc Represents a GuardTower. - * @implements IGuardTower - * @constructor - * @param {treasurehunterx.IGuardTower=} [properties] Properties to set - */ - function GuardTower(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * GuardTower id. - * @member {number} id - * @memberof treasurehunterx.GuardTower - * @instance - */ - GuardTower.prototype.id = 0; - - /** - * GuardTower localIdInBattle. - * @member {number} localIdInBattle - * @memberof treasurehunterx.GuardTower - * @instance - */ - GuardTower.prototype.localIdInBattle = 0; - - /** - * GuardTower type. - * @member {number} type - * @memberof treasurehunterx.GuardTower - * @instance - */ - GuardTower.prototype.type = 0; - - /** - * GuardTower x. - * @member {number} x - * @memberof treasurehunterx.GuardTower - * @instance - */ - GuardTower.prototype.x = 0; - - /** - * GuardTower y. - * @member {number} y - * @memberof treasurehunterx.GuardTower - * @instance - */ - GuardTower.prototype.y = 0; - - /** - * GuardTower removed. - * @member {boolean} removed - * @memberof treasurehunterx.GuardTower - * @instance - */ - GuardTower.prototype.removed = false; - - /** - * Creates a new GuardTower instance using the specified properties. - * @function create - * @memberof treasurehunterx.GuardTower - * @static - * @param {treasurehunterx.IGuardTower=} [properties] Properties to set - * @returns {treasurehunterx.GuardTower} GuardTower instance - */ - GuardTower.create = function create(properties) { - return new GuardTower(properties); - }; - - /** - * Encodes the specified GuardTower message. Does not implicitly {@link treasurehunterx.GuardTower.verify|verify} messages. - * @function encode - * @memberof treasurehunterx.GuardTower - * @static - * @param {treasurehunterx.GuardTower} message GuardTower message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - GuardTower.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id); - if (message.localIdInBattle != null && Object.hasOwnProperty.call(message, "localIdInBattle")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.localIdInBattle); - if (message.type != null && Object.hasOwnProperty.call(message, "type")) - writer.uint32(/* id 3, wireType 0 =*/24).int32(message.type); - if (message.x != null && Object.hasOwnProperty.call(message, "x")) - writer.uint32(/* id 4, wireType 1 =*/33).double(message.x); - if (message.y != null && Object.hasOwnProperty.call(message, "y")) - writer.uint32(/* id 5, wireType 1 =*/41).double(message.y); - if (message.removed != null && Object.hasOwnProperty.call(message, "removed")) - writer.uint32(/* id 6, wireType 0 =*/48).bool(message.removed); - return writer; - }; - - /** - * Encodes the specified GuardTower message, length delimited. Does not implicitly {@link treasurehunterx.GuardTower.verify|verify} messages. - * @function encodeDelimited - * @memberof treasurehunterx.GuardTower - * @static - * @param {treasurehunterx.GuardTower} message GuardTower message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - GuardTower.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a GuardTower message from the specified reader or buffer. - * @function decode - * @memberof treasurehunterx.GuardTower - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {treasurehunterx.GuardTower} GuardTower - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - GuardTower.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.GuardTower(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.id = reader.int32(); - break; - } - case 2: { - message.localIdInBattle = reader.int32(); - break; - } - case 3: { - message.type = reader.int32(); - break; - } - case 4: { - message.x = reader.double(); - break; - } - case 5: { - message.y = reader.double(); - break; - } - case 6: { - message.removed = reader.bool(); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a GuardTower message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof treasurehunterx.GuardTower - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {treasurehunterx.GuardTower} GuardTower - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - GuardTower.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a GuardTower message. - * @function verify - * @memberof treasurehunterx.GuardTower - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - GuardTower.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isInteger(message.id)) - return "id: integer expected"; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - if (!$util.isInteger(message.localIdInBattle)) - return "localIdInBattle: integer expected"; - if (message.type != null && message.hasOwnProperty("type")) - if (!$util.isInteger(message.type)) - return "type: integer expected"; - if (message.x != null && message.hasOwnProperty("x")) - if (typeof message.x !== "number") - return "x: number expected"; - if (message.y != null && message.hasOwnProperty("y")) - if (typeof message.y !== "number") - return "y: number expected"; - if (message.removed != null && message.hasOwnProperty("removed")) - if (typeof message.removed !== "boolean") - return "removed: boolean expected"; - return null; - }; - - /** - * Creates a GuardTower message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof treasurehunterx.GuardTower - * @static - * @param {Object.} object Plain object - * @returns {treasurehunterx.GuardTower} GuardTower - */ - GuardTower.fromObject = function fromObject(object) { - if (object instanceof $root.treasurehunterx.GuardTower) - return object; - var message = new $root.treasurehunterx.GuardTower(); - if (object.id != null) - message.id = object.id | 0; - if (object.localIdInBattle != null) - message.localIdInBattle = object.localIdInBattle | 0; - if (object.type != null) - message.type = object.type | 0; - if (object.x != null) - message.x = Number(object.x); - if (object.y != null) - message.y = Number(object.y); - if (object.removed != null) - message.removed = Boolean(object.removed); - return message; - }; - - /** - * Creates a plain object from a GuardTower message. Also converts values to other types if specified. - * @function toObject - * @memberof treasurehunterx.GuardTower - * @static - * @param {treasurehunterx.GuardTower} message GuardTower - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - GuardTower.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.id = 0; - object.localIdInBattle = 0; - object.type = 0; - object.x = 0; - object.y = 0; - object.removed = false; - } - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - if (message.localIdInBattle != null && message.hasOwnProperty("localIdInBattle")) - object.localIdInBattle = message.localIdInBattle; - if (message.type != null && message.hasOwnProperty("type")) - object.type = message.type; - if (message.x != null && message.hasOwnProperty("x")) - object.x = options.json && !isFinite(message.x) ? String(message.x) : message.x; - if (message.y != null && message.hasOwnProperty("y")) - object.y = options.json && !isFinite(message.y) ? String(message.y) : message.y; - if (message.removed != null && message.hasOwnProperty("removed")) - object.removed = message.removed; - return object; - }; - - /** - * Converts this GuardTower to JSON. - * @function toJSON - * @memberof treasurehunterx.GuardTower - * @instance - * @returns {Object.} JSON object - */ - GuardTower.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for GuardTower - * @function getTypeUrl - * @memberof treasurehunterx.GuardTower - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - GuardTower.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/treasurehunterx.GuardTower"; - }; - - return GuardTower; - })(); - treasurehunterx.RoomDownsyncFrame = (function() { /** @@ -4339,11 +2392,6 @@ $root.treasurehunterx = (function() { * @property {Object.|null} [players] RoomDownsyncFrame players * @property {number|Long|null} [sentAt] RoomDownsyncFrame sentAt * @property {number|Long|null} [countdownNanos] RoomDownsyncFrame countdownNanos - * @property {Object.|null} [treasures] RoomDownsyncFrame treasures - * @property {Object.|null} [traps] RoomDownsyncFrame traps - * @property {Object.|null} [bullets] RoomDownsyncFrame bullets - * @property {Object.|null} [speedShoes] RoomDownsyncFrame speedShoes - * @property {Object.|null} [guardTowers] RoomDownsyncFrame guardTowers * @property {Object.|null} [playerMetas] RoomDownsyncFrame playerMetas */ @@ -4357,11 +2405,6 @@ $root.treasurehunterx = (function() { */ function RoomDownsyncFrame(properties) { this.players = {}; - this.treasures = {}; - this.traps = {}; - this.bullets = {}; - this.speedShoes = {}; - this.guardTowers = {}; this.playerMetas = {}; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) @@ -4409,46 +2452,6 @@ $root.treasurehunterx = (function() { */ RoomDownsyncFrame.prototype.countdownNanos = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - /** - * RoomDownsyncFrame treasures. - * @member {Object.} treasures - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.treasures = $util.emptyObject; - - /** - * RoomDownsyncFrame traps. - * @member {Object.} traps - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.traps = $util.emptyObject; - - /** - * RoomDownsyncFrame bullets. - * @member {Object.} bullets - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.bullets = $util.emptyObject; - - /** - * RoomDownsyncFrame speedShoes. - * @member {Object.} speedShoes - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.speedShoes = $util.emptyObject; - - /** - * RoomDownsyncFrame guardTowers. - * @member {Object.} guardTowers - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.guardTowers = $util.emptyObject; - /** * RoomDownsyncFrame playerMetas. * @member {Object.} playerMetas @@ -4494,34 +2497,9 @@ $root.treasurehunterx = (function() { writer.uint32(/* id 4, wireType 0 =*/32).int64(message.sentAt); if (message.countdownNanos != null && Object.hasOwnProperty.call(message, "countdownNanos")) writer.uint32(/* id 5, wireType 0 =*/40).int64(message.countdownNanos); - if (message.treasures != null && Object.hasOwnProperty.call(message, "treasures")) - for (var keys = Object.keys(message.treasures), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 6, wireType 2 =*/50).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); - $root.treasurehunterx.Treasure.encode(message.treasures[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); - } - if (message.traps != null && Object.hasOwnProperty.call(message, "traps")) - for (var keys = Object.keys(message.traps), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 7, wireType 2 =*/58).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); - $root.treasurehunterx.Trap.encode(message.traps[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); - } - if (message.bullets != null && Object.hasOwnProperty.call(message, "bullets")) - for (var keys = Object.keys(message.bullets), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 8, wireType 2 =*/66).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); - $root.treasurehunterx.Bullet.encode(message.bullets[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); - } - if (message.speedShoes != null && Object.hasOwnProperty.call(message, "speedShoes")) - for (var keys = Object.keys(message.speedShoes), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 9, wireType 2 =*/74).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); - $root.treasurehunterx.SpeedShoe.encode(message.speedShoes[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); - } - if (message.guardTowers != null && Object.hasOwnProperty.call(message, "guardTowers")) - for (var keys = Object.keys(message.guardTowers), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 10, wireType 2 =*/82).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); - $root.treasurehunterx.GuardTower.encode(message.guardTowers[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); - } if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas")) for (var keys = Object.keys(message.playerMetas), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 11, wireType 2 =*/90).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); + writer.uint32(/* id 6, wireType 2 =*/50).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); $root.treasurehunterx.PlayerMeta.encode(message.playerMetas[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); } return writer; @@ -4598,121 +2576,6 @@ $root.treasurehunterx = (function() { break; } case 6: { - if (message.treasures === $util.emptyObject) - message.treasures = {}; - var end2 = reader.uint32() + reader.pos; - key = 0; - value = null; - while (reader.pos < end2) { - var tag2 = reader.uint32(); - switch (tag2 >>> 3) { - case 1: - key = reader.int32(); - break; - case 2: - value = $root.treasurehunterx.Treasure.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag2 & 7); - break; - } - } - message.treasures[key] = value; - break; - } - case 7: { - if (message.traps === $util.emptyObject) - message.traps = {}; - var end2 = reader.uint32() + reader.pos; - key = 0; - value = null; - while (reader.pos < end2) { - var tag2 = reader.uint32(); - switch (tag2 >>> 3) { - case 1: - key = reader.int32(); - break; - case 2: - value = $root.treasurehunterx.Trap.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag2 & 7); - break; - } - } - message.traps[key] = value; - break; - } - case 8: { - if (message.bullets === $util.emptyObject) - message.bullets = {}; - var end2 = reader.uint32() + reader.pos; - key = 0; - value = null; - while (reader.pos < end2) { - var tag2 = reader.uint32(); - switch (tag2 >>> 3) { - case 1: - key = reader.int32(); - break; - case 2: - value = $root.treasurehunterx.Bullet.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag2 & 7); - break; - } - } - message.bullets[key] = value; - break; - } - case 9: { - if (message.speedShoes === $util.emptyObject) - message.speedShoes = {}; - var end2 = reader.uint32() + reader.pos; - key = 0; - value = null; - while (reader.pos < end2) { - var tag2 = reader.uint32(); - switch (tag2 >>> 3) { - case 1: - key = reader.int32(); - break; - case 2: - value = $root.treasurehunterx.SpeedShoe.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag2 & 7); - break; - } - } - message.speedShoes[key] = value; - break; - } - case 10: { - if (message.guardTowers === $util.emptyObject) - message.guardTowers = {}; - var end2 = reader.uint32() + reader.pos; - key = 0; - value = null; - while (reader.pos < end2) { - var tag2 = reader.uint32(); - switch (tag2 >>> 3) { - case 1: - key = reader.int32(); - break; - case 2: - value = $root.treasurehunterx.GuardTower.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag2 & 7); - break; - } - } - message.guardTowers[key] = value; - break; - } - case 11: { if (message.playerMetas === $util.emptyObject) message.playerMetas = {}; var end2 = reader.uint32() + reader.pos; @@ -4796,76 +2659,6 @@ $root.treasurehunterx = (function() { if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos")) if (!$util.isInteger(message.countdownNanos) && !(message.countdownNanos && $util.isInteger(message.countdownNanos.low) && $util.isInteger(message.countdownNanos.high))) return "countdownNanos: integer|Long expected"; - if (message.treasures != null && message.hasOwnProperty("treasures")) { - if (!$util.isObject(message.treasures)) - return "treasures: object expected"; - var key = Object.keys(message.treasures); - for (var i = 0; i < key.length; ++i) { - if (!$util.key32Re.test(key[i])) - return "treasures: integer key{k:int32} expected"; - { - var error = $root.treasurehunterx.Treasure.verify(message.treasures[key[i]]); - if (error) - return "treasures." + error; - } - } - } - if (message.traps != null && message.hasOwnProperty("traps")) { - if (!$util.isObject(message.traps)) - return "traps: object expected"; - var key = Object.keys(message.traps); - for (var i = 0; i < key.length; ++i) { - if (!$util.key32Re.test(key[i])) - return "traps: integer key{k:int32} expected"; - { - var error = $root.treasurehunterx.Trap.verify(message.traps[key[i]]); - if (error) - return "traps." + error; - } - } - } - if (message.bullets != null && message.hasOwnProperty("bullets")) { - if (!$util.isObject(message.bullets)) - return "bullets: object expected"; - var key = Object.keys(message.bullets); - for (var i = 0; i < key.length; ++i) { - if (!$util.key32Re.test(key[i])) - return "bullets: integer key{k:int32} expected"; - { - var error = $root.treasurehunterx.Bullet.verify(message.bullets[key[i]]); - if (error) - return "bullets." + error; - } - } - } - if (message.speedShoes != null && message.hasOwnProperty("speedShoes")) { - if (!$util.isObject(message.speedShoes)) - return "speedShoes: object expected"; - var key = Object.keys(message.speedShoes); - for (var i = 0; i < key.length; ++i) { - if (!$util.key32Re.test(key[i])) - return "speedShoes: integer key{k:int32} expected"; - { - var error = $root.treasurehunterx.SpeedShoe.verify(message.speedShoes[key[i]]); - if (error) - return "speedShoes." + error; - } - } - } - if (message.guardTowers != null && message.hasOwnProperty("guardTowers")) { - if (!$util.isObject(message.guardTowers)) - return "guardTowers: object expected"; - var key = Object.keys(message.guardTowers); - for (var i = 0; i < key.length; ++i) { - if (!$util.key32Re.test(key[i])) - return "guardTowers: integer key{k:int32} expected"; - { - var error = $root.treasurehunterx.GuardTower.verify(message.guardTowers[key[i]]); - if (error) - return "guardTowers." + error; - } - } - } if (message.playerMetas != null && message.hasOwnProperty("playerMetas")) { if (!$util.isObject(message.playerMetas)) return "playerMetas: object expected"; @@ -4927,56 +2720,6 @@ $root.treasurehunterx = (function() { message.countdownNanos = object.countdownNanos; else if (typeof object.countdownNanos === "object") message.countdownNanos = new $util.LongBits(object.countdownNanos.low >>> 0, object.countdownNanos.high >>> 0).toNumber(); - if (object.treasures) { - if (typeof object.treasures !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.treasures: object expected"); - message.treasures = {}; - for (var keys = Object.keys(object.treasures), i = 0; i < keys.length; ++i) { - if (typeof object.treasures[keys[i]] !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.treasures: object expected"); - message.treasures[keys[i]] = $root.treasurehunterx.Treasure.fromObject(object.treasures[keys[i]]); - } - } - if (object.traps) { - if (typeof object.traps !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.traps: object expected"); - message.traps = {}; - for (var keys = Object.keys(object.traps), i = 0; i < keys.length; ++i) { - if (typeof object.traps[keys[i]] !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.traps: object expected"); - message.traps[keys[i]] = $root.treasurehunterx.Trap.fromObject(object.traps[keys[i]]); - } - } - if (object.bullets) { - if (typeof object.bullets !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.bullets: object expected"); - message.bullets = {}; - for (var keys = Object.keys(object.bullets), i = 0; i < keys.length; ++i) { - if (typeof object.bullets[keys[i]] !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.bullets: object expected"); - message.bullets[keys[i]] = $root.treasurehunterx.Bullet.fromObject(object.bullets[keys[i]]); - } - } - if (object.speedShoes) { - if (typeof object.speedShoes !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.speedShoes: object expected"); - message.speedShoes = {}; - for (var keys = Object.keys(object.speedShoes), i = 0; i < keys.length; ++i) { - if (typeof object.speedShoes[keys[i]] !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.speedShoes: object expected"); - message.speedShoes[keys[i]] = $root.treasurehunterx.SpeedShoe.fromObject(object.speedShoes[keys[i]]); - } - } - if (object.guardTowers) { - if (typeof object.guardTowers !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.guardTowers: object expected"); - message.guardTowers = {}; - for (var keys = Object.keys(object.guardTowers), i = 0; i < keys.length; ++i) { - if (typeof object.guardTowers[keys[i]] !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.guardTowers: object expected"); - message.guardTowers[keys[i]] = $root.treasurehunterx.GuardTower.fromObject(object.guardTowers[keys[i]]); - } - } if (object.playerMetas) { if (typeof object.playerMetas !== "object") throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected"); @@ -5005,11 +2748,6 @@ $root.treasurehunterx = (function() { var object = {}; if (options.objects || options.defaults) { object.players = {}; - object.treasures = {}; - object.traps = {}; - object.bullets = {}; - object.speedShoes = {}; - object.guardTowers = {}; object.playerMetas = {}; } if (options.defaults) { @@ -5046,31 +2784,6 @@ $root.treasurehunterx = (function() { object.countdownNanos = options.longs === String ? String(message.countdownNanos) : message.countdownNanos; else object.countdownNanos = options.longs === String ? $util.Long.prototype.toString.call(message.countdownNanos) : options.longs === Number ? new $util.LongBits(message.countdownNanos.low >>> 0, message.countdownNanos.high >>> 0).toNumber() : message.countdownNanos; - if (message.treasures && (keys2 = Object.keys(message.treasures)).length) { - object.treasures = {}; - for (var j = 0; j < keys2.length; ++j) - object.treasures[keys2[j]] = $root.treasurehunterx.Treasure.toObject(message.treasures[keys2[j]], options); - } - if (message.traps && (keys2 = Object.keys(message.traps)).length) { - object.traps = {}; - for (var j = 0; j < keys2.length; ++j) - object.traps[keys2[j]] = $root.treasurehunterx.Trap.toObject(message.traps[keys2[j]], options); - } - if (message.bullets && (keys2 = Object.keys(message.bullets)).length) { - object.bullets = {}; - for (var j = 0; j < keys2.length; ++j) - object.bullets[keys2[j]] = $root.treasurehunterx.Bullet.toObject(message.bullets[keys2[j]], options); - } - if (message.speedShoes && (keys2 = Object.keys(message.speedShoes)).length) { - object.speedShoes = {}; - for (var j = 0; j < keys2.length; ++j) - object.speedShoes[keys2[j]] = $root.treasurehunterx.SpeedShoe.toObject(message.speedShoes[keys2[j]], options); - } - if (message.guardTowers && (keys2 = Object.keys(message.guardTowers)).length) { - object.guardTowers = {}; - for (var j = 0; j < keys2.length; ++j) - object.guardTowers[keys2[j]] = $root.treasurehunterx.GuardTower.toObject(message.guardTowers[keys2[j]], options); - } if (message.playerMetas && (keys2 = Object.keys(message.playerMetas)).length) { object.playerMetas = {}; for (var j = 0; j < keys2.length; ++j) From 266335b7c6d2f197f59bb224b8197293fa32807a Mon Sep 17 00:00:00 2001 From: genxium Date: Thu, 29 Sep 2022 12:21:04 +0800 Subject: [PATCH 02/13] Refactoring backend for periodical force confirmation. --- ConcerningEdgeCases.md | 32 + battle_srv/go.mod | 1 + battle_srv/go.sum | 4 + battle_srv/models/player.go | 2 +- battle_srv/models/room.go | 647 ++++++------- battle_srv/models/room_heap_manager.go | 11 +- battle_srv/models/tiled_map.go | 2 +- .../pb_output/room_downsync_frame.pb.go | 478 +++++----- .../pbfiles/room_downsync_frame.proto | 20 +- frontend/assets/scripts/Map.js | 27 +- ...om_downsync_frame_proto_bundle.forcemsg.js | 892 +++++++++--------- 11 files changed, 1083 insertions(+), 1033 deletions(-) diff --git a/ConcerningEdgeCases.md b/ConcerningEdgeCases.md index f828f41..e1a849f 100644 --- a/ConcerningEdgeCases.md +++ b/ConcerningEdgeCases.md @@ -1,3 +1,4 @@ +# Potential avalanche from local lag Under the current "input delay" algorithm, the lag of a single player would cause all the other players to receive outdated commands, e.g. when at a certain moment - player#1: renderFrameId = 100, significantly lagged due to local CPU overheated - player#2: renderFrameId = 240 @@ -9,3 +10,34 @@ players #2, #3 #4 would receive "outdated(in their subjective feelings) but all- In a "no-server & p2p" setup, I couldn't think of a proper way to cope with such edge case. Solely on the frontend we could only mitigate the impact to players #2, #3, #4, e.g. a potential lag due to "large range of frame-chasing" is proactively avoided in `/frontend/assets/scripts/Map.js, function update(dt)`. However in a "server as authority" setup, the server could force confirming an inputFrame without player#1's upsync, and notify player#1 to apply a "roomDownsyncFrame" as well as drop all its outdated local inputFrames. + +# Start up frames +renderFrameId | generatedInputFrameId | toApplyInputFrameId +-------------------|----------------------------|---------------------- +0, 1, 2, 3 | 0, _EMP_, _EMP_, _EMP_ | 0 +4, 5, 6, 7 | 1, _EMP_, _EMP_, _EMP_ | 0 +8, 9, 10, 11 | 2, _EMP_, _EMP_, _EMP_ | 1 +12, 13, 14, 15 | 3, _EMP_, _EMP_, _EMP_ | 2 + +It should be reasonable to assume that inputFrameId=0 is always of all-empty content, because human has no chance of clicking at the very first render frame. + +# Alignment of the current setup +The following setup is chosen deliberately for some "%4" number coincidence. +- NstDelayFrames = 2 +- InputDelayFrames = 4 +- InputScaleFrames = 2 + +If "InputDelayFrames" is changed, the impact would be as follows, kindly note that "372%4 == 0". + +### pR.InputDelayFrames = 4 +toApplyInputFrameId | renderFrameId +--------------------------|----------------------------------------- + 92 | _EMP_, _EMP_, _EMP_, _EMP_, 372, 373, 374, 375 + 91 | 368, 369, 370, 371 + +### pR.InputDelayFrames = 5 +toApplyInputFrameId | renderFrameId +--------------------------|----------------------------------------- + 92 | _EMP_, _EMP_, _EMP_, _EMP_, 373, 374, 375 + 91 | _EMP_, 369, 370, 371, 372 + 90 | 368 diff --git a/battle_srv/go.mod b/battle_srv/go.mod index 5ab0e6d..555734c 100644 --- a/battle_srv/go.mod +++ b/battle_srv/go.mod @@ -26,6 +26,7 @@ require ( github.com/mattn/go-isatty v0.0.3 // indirect github.com/mattn/go-sqlite3 v1.9.0 github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 + github.com/solarlune/resolv v0.5.1 github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713 github.com/ugorji/go v1.1.1 // indirect go.uber.org/atomic v1.3.2 // indirect diff --git a/battle_srv/go.sum b/battle_srv/go.sum index 3990203..984ae1f 100644 --- a/battle_srv/go.sum +++ b/battle_srv/go.sum @@ -45,6 +45,8 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+uGHxWtRt4C0Wjc6kJKo5XYx8xE= github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= +github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 h1:v8lWpj5957KtDMKu+xQtlu6G3ZoZR6Tn9bsfZCRG5Xw= +github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0/go.mod h1:GAX7tMJqXx9fB1BrsTWPOXy6IBRX+J461BffVPAdpwo= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -61,6 +63,8 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0C github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE= github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/solarlune/resolv v0.5.1 h1:Ul6PAs/zaxiMUOEYz1Z6VeUj5k3CDcWMvSh+kivybDY= +github.com/solarlune/resolv v0.5.1/go.mod h1:HjM2f/0NoVjVdZsi26GtugX5aFbA62COEFEXkOhveRw= github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713 h1:knaxjm6QMbUMNvuaSnJZmw0gRX4V/79JVUQiziJGM84= github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713/go.mod h1:mlR+dHGb+4YgXkf13rkQTuzrneeHANxOm6+ZnEV9HsA= github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w= diff --git a/battle_srv/models/player.go b/battle_srv/models/player.go index 3c4e137..7dbe018 100644 --- a/battle_srv/models/player.go +++ b/battle_srv/models/player.go @@ -37,7 +37,7 @@ type Player struct { X float64 `json:"x,omitempty"` Y float64 `json:"y,omitempty"` Dir *Direction `json:"dir,omitempty"` - Speed int32 `json:"speed,omitempty"` + Speed float64 `json:"speed,omitempty"` BattleState int32 `json:"battleState,omitempty"` LastMoveGmtMillis int32 `json:"lastMoveGmtMillis,omitempty"` Score int32 `json:"score,omitempty"` diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 2a100e1..1d57b01 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -3,9 +3,9 @@ package models import ( "encoding/xml" "fmt" - "github.com/ByteArena/box2d" "github.com/golang/protobuf/proto" "github.com/gorilla/websocket" + "github.com/solarlune/resolv" "go.uber.org/zap" "io/ioutil" "math/rand" @@ -41,12 +41,14 @@ const ( ) const ( - // You can equivalently use the `GroupIndex` approach, but the more complicated and general purpose approach is used deliberately here. Reference http://www.aurelienribon.com/post/2011-07-box2d-tutorial-collision-filtering. COLLISION_CATEGORY_CONTROLLED_PLAYER = (1 << 1) COLLISION_CATEGORY_BARRIER = (1 << 2) COLLISION_MASK_FOR_CONTROLLED_PLAYER = (COLLISION_CATEGORY_BARRIER) COLLISION_MASK_FOR_BARRIER = (COLLISION_CATEGORY_CONTROLLED_PLAYER) + + COLLISION_PLAYER_INDEX_PREFIX = (1 << 17) + COLLISION_BARRIER_INDEX_PREFIX = (1 << 16) ) var DIRECTION_DECODER = [][]int32{ @@ -65,7 +67,7 @@ var DIRECTION_DECODER = [][]int32{ {0, -1}, } -var DIRECTION_DECODER_INVERSE_LENGTH = []float32{ +var DIRECTION_DECODER_INVERSE_LENGTH = []float64{ 0.0, 1.0, 1.0, @@ -117,9 +119,11 @@ func calRoomScore(inRoomPlayerCount int32, roomPlayerCnt int, currentRoomBattleS } type Room struct { - Id int32 - Capacity int - Players map[int32]*Player + Id int32 + Capacity int + Players map[int32]*Player + PlayersArr []*Player // ordered by joinIndex + CollisionSysMap map[int32]*resolv.Object /** * The following `PlayerDownsyncSessionDict` is NOT individually put * under `type Player struct` for a reason. @@ -143,22 +147,23 @@ type Room struct { Score float32 State int32 Index int - Tick int32 + RenderFrameId int32 + CurDynamicsRenderFrameId int32 // [WARNING] The dynamics of backend is ALWAYS MOVING FORWARD BY ALL-CONFIRMED INPUTFRAMES (either by upsync or forced), i.e. no rollback ServerFPS int32 BattleDurationNanos int64 EffectivePlayerCount int32 DismissalWaitGroup sync.WaitGroup Barriers map[int32]*Barrier - AccumulatedLocalIdForBullets int32 - CollidableWorld *box2d.B2World AllPlayerInputsBuffer *RingBuffer RenderFrameBuffer *RingBuffer LastAllConfirmedInputFrameId int32 LastAllConfirmedInputFrameIdWithChange int32 LastAllConfirmedInputList []uint64 - InputDelayFrames int32 + InputDelayFrames int32 // in the count of render frames + NstDelayFrames int32 // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames" InputScaleFrames uint32 // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames) JoinIndexBooleanArr []bool + RollbackEstimatedDt float64 StageName string StageDiscreteW int32 @@ -170,8 +175,8 @@ type Room struct { } const ( - PLAYER_DEFAULT_SPEED = 200 // Hardcoded - ADD_SPEED = 100 // Hardcoded + PLAYER_DEFAULT_SPEED = float64(200) // Hardcoded + ADD_SPEED = float64(100) // Hardcoded ) func (pR *Room) updateScore() { @@ -310,9 +315,9 @@ func (pR *Room) ChooseStage() error { Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points)) */ pR.Barriers[barrierLocalIdInBattle] = &Barrier{ - X: polygon2D.Anchor.X, - Y: polygon2D.Anchor.Y, - Boundary: polygon2D, + X: polygon2D.Anchor.X, + Y: polygon2D.Anchor.Y, + Boundary: polygon2D, } barrierLocalIdInBattle++ @@ -321,11 +326,17 @@ func (pR *Room) ChooseStage() error { return nil } -func (pR *Room) ConvertToInputFrameId(originalFrameId int32, inputDelayFrames int32) int32 { - if originalFrameId < inputDelayFrames { - return 0 - } - return ((originalFrameId - inputDelayFrames) >> pR.InputScaleFrames) +func (pR *Room) ConvertToInputFrameId(renderFrameId int32, inputDelayFrames int32) int32 { + // Specifically when "renderFrameId < inputDelayFrames", the result is 0. + return ((renderFrameId - inputDelayFrames) >> pR.InputScaleFrames) +} + +func (pR *Room) ConvertToFirstUsedRenderFrameId(inputFrameId int32, inputDelayFrames int32) int32 { + return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames) +} + +func (pR *Room) ConvertToLastUsedRenderFrameId(inputFrameId int32, inputDelayFrames int32) int32 { + return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames + (1 << pR.InputScaleFrames)-1) } func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 { @@ -335,19 +346,6 @@ func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 { return ret } -func (pR *Room) CanPopSt(refLowerInputFrameId int32) bool { - rb := pR.AllPlayerInputsBuffer - if rb.Cnt <= 0 { - return false - } - if rb.StFrameId <= refLowerInputFrameId { - // already delayed too much - return true - } - - return false -} - func (pR *Room) AllPlayerInputsBufferString() string { s := make([]string, 0) s = append(s, fmt.Sprintf("{lastAllConfirmedInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v}", pR.LastAllConfirmedInputFrameId, pR.LastAllConfirmedInputFrameIdWithChange)) @@ -374,9 +372,10 @@ func (pR *Room) StartBattle() { // Always instantiates a new channel and let the old one die out due to not being retained by any root reference. nanosPerFrame := 1000000000 / int64(pR.ServerFPS) - pR.Tick = 0 + pR.RenderFrameId = 0 + pR.CurDynamicsRenderFrameId = 0 - // Refresh "Colliders" for server-side contact listening of B2World. + // Refresh "Colliders" pR.refreshColliders() /** @@ -397,15 +396,19 @@ func (pR *Room) StartBattle() { Logger.Info("The `battleMainLoop` is started for:", zap.Any("roomId", pR.Id)) for { - if 0 == pR.Tick { + stCalculation := utils.UnixtimeNano() + + if 0 == pR.RenderFrameId { // The legacy frontend code needs this "kickoffFrame" to remove the "ready to start 3-2-1" panel kickoffFrame := pb.RoomDownsyncFrame{ - Id: pR.Tick, + Id: pR.RenderFrameId, RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START, Players: toPbPlayers(pR.Players), SentAt: utils.UnixtimeMilli(), CountdownNanos: (pR.BattleDurationNanos - totalElapsedNanos), } + + pR.RenderFrameBuffer.Put(&kickoffFrame) for playerId, player := range pR.Players { if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped { /* @@ -418,80 +421,102 @@ func (pR *Room) StartBattle() { } if totalElapsedNanos > pR.BattleDurationNanos { - Logger.Info(fmt.Sprintf("The `battleMainLoop` is stopped:\n%v", pR.AllPlayerInputsBufferString())) + Logger.Info(fmt.Sprintf("The `battleMainLoop` for roomId=%v is stopped:\n%v", pR.Id, pR.AllPlayerInputsBufferString())) pR.StopBattleForSettlement() } if swapped := atomic.CompareAndSwapInt32(&pR.State, RoomBattleStateIns.IN_BATTLE, RoomBattleStateIns.IN_BATTLE); !swapped { return } - stCalculation := utils.UnixtimeNano() - // TODO: Force confirm some non-all-confirmed but outdated input frames, derive server-sider RenderFrameBuffer elements from them, and send to respective players with "RoomDownsyncFrame.refFrameId=MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED" - - refInputFrameId := int32(999999999) // Hardcoded as a max reference. - for playerId, _ := range pR.Players { - thatId := atomic.LoadInt32(&(pR.Players[playerId].AckingInputFrameId)) - if thatId > refInputFrameId { - continue - } - refInputFrameId = thatId + // Prefab and buffer backend inputFrameDownsync + if pR.shouldPrefabInputFrameDownsync(pR.RenderFrameId) { + noDelayInputFrameId := pR.ConvertToInputFrameId(pR.RenderFrameId, 0) + pR.prefabInputFrameDownsync(noDelayInputFrameId) } + + // Force setting all-confirmed of buffered inputFrames periodically + unconfirmedMask := pR.forceConfirmationIfApplicable() - for pR.CanPopSt(refInputFrameId) { - // _ = pR.AllPlayerInputsBuffer.Pop() - f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync) - if pR.inputFrameIdDebuggable(f.InputFrameId) { - // Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked - Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("refInputFrameId", refInputFrameId), zap.Any("inputFrameId", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) - } - } + // Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId" + if 0 <= pR.CurDynamicsRenderFrameId { + nextDynamicsRenderFrameId := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames) + pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId) + } - lastAllConfirmedInputFrameIdWithChange := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameIdWithChange)) - for playerId, player := range pR.Players { - if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped { - /* - [WARNING] DON'T send anything into "DedicatedForwardingChanForPlayer" if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player". - */ - continue - } + lastAllConfirmedInputFrameIdWithChange := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameIdWithChange)) + for playerId, player := range pR.Players { + if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped { + /* + [WARNING] DON'T send anything into "DedicatedForwardingChanForPlayer" if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player". + */ + continue + } + // [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player! + toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt) + candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1 + if candidateToSendInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { + Logger.Warn("LastSentInputFrameId already popped:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("lastSentInputFrameId", candidateToSendInputFrameId-1), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) + candidateToSendInputFrameId = pR.AllPlayerInputsBuffer.StFrameId + } - toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt) - // [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player! - anchorInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) - candidateToSendInputFrameId := anchorInputFrameId + 1 + // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! + debugSendingInputFrameId := int32(-1) - // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! - debugSendingInputFrameId := int32(-1) + for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange { + tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId) + if nil == tmp { + panic(fmt.Sprintf("Required inputFrameId=%v for roomId=%v, playerId=%v doesn't exist! AllPlayerInputsBuffer=%v", candidateToSendInputFrameId, pR.Id, playerId, pR.AllPlayerInputsBufferString())) + } + f := tmp.(*pb.InputFrameDownsync) + if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) { + debugSendingInputFrameId = candidateToSendInputFrameId + Logger.Info("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()), zap.Any("ConfirmedList", f.ConfirmedList)) + } + toSendInputFrames = append(toSendInputFrames, f) + candidateToSendInputFrameId++ + } - for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange { - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId) - if nil == tmp { - break - } - f := tmp.(*pb.InputFrameDownsync) - if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) { - debugSendingInputFrameId = candidateToSendInputFrameId - Logger.Info("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("refInputFrameId", refInputFrameId), zap.Any("playerId", playerId), zap.Any("playerAnchorInputFrameId", anchorInputFrameId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId), zap.Any("ConfirmedList", f.ConfirmedList)) - } - toSendInputFrames = append(toSendInputFrames, f) - candidateToSendInputFrameId++ - } + indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) + var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) + if 0 < (unconfirmedMask & joinMask) { + refRenderFrame := pR.RenderFrameBuffer.GetByFrameId(pR.CurDynamicsRenderFrameId).(*pb.RoomDownsyncFrame) + resp := pb.WsResp { + Ret: int32(Constants.RetCode.Ok), + EchoedMsgId: int32(0), + Act: DOWNSYNC_MSG_ACT_ROOM_FRAME, + InputFrameDownsyncBatch: toSendInputFrames, + Rdf: refRenderFrame, + } + pR.sendSafely(resp, playerId) + } else { + if 0 >= len(toSendInputFrames) { + continue + } + pR.sendSafely(toSendInputFrames, playerId) + atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) + if -1 != debugSendingInputFrameId { + Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) + } + } + } - if 0 >= len(toSendInputFrames) { - continue - } + for 0 < pR.RenderFrameBuffer.Cnt && pR.RenderFrameBuffer.StFrameId < pR.CurDynamicsRenderFrameId { + _ = pR.RenderFrameBuffer.Pop() + } - pR.sendSafely(toSendInputFrames, playerId) - atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) - if -1 != debugSendingInputFrameId { - Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("refInputFrameId", refInputFrameId), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) - } - } + toApplyInputFrameId := pR.ConvertToInputFrameId(pR.CurDynamicsRenderFrameId, pR.InputDelayFrames) + for 0 < pR.AllPlayerInputsBuffer.Cnt && pR.AllPlayerInputsBuffer.StFrameId < toApplyInputFrameId { + f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync) + if pR.inputFrameIdDebuggable(f.InputFrameId) { + // Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked + Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + } + } - pR.Tick++ + pR.RenderFrameId++ now := utils.UnixtimeNano() - elapsedInCalculation := now - stCalculation + elapsedInCalculation := (now - stCalculation) totalElapsedNanos = (now - battleMainLoopStartedNanos) // Logger.Info("Elapsed time statistics:", zap.Any("roomId", pR.Id), zap.Any("elapsedInCalculation", elapsedInCalculation), zap.Any("totalElapsedNanos", totalElapsedNanos)) time.Sleep(time.Duration(nanosPerFrame - elapsedInCalculation)) @@ -515,19 +540,20 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { ackingFrameId := pReq.AckingFrameId ackingInputFrameId := pReq.AckingInputFrameId + if _, existent := pR.Players[playerId]; !existent { + Logger.Warn("upcmd player doesn't exist:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId)) + return + } + + if swapped := atomic.CompareAndSwapInt32(&(pR.Players[playerId].AckingFrameId), pR.Players[playerId].AckingFrameId, ackingFrameId); !swapped { + panic(fmt.Sprintf("Failed to update AckingFrameId to %v for roomId=%v, playerId=%v", ackingFrameId, pR.Id, playerId)) + } + + if swapped := atomic.CompareAndSwapInt32(&(pR.Players[playerId].AckingInputFrameId), pR.Players[playerId].AckingInputFrameId, ackingInputFrameId); !swapped { + panic(fmt.Sprintf("Failed to update AckingInputFrameId to %v for roomId=%v, playerId=%v", ackingInputFrameId, pR.Id, playerId)) + } + for _, inputFrameUpsync := range inputFrameUpsyncBatch { - if _, existent := pR.Players[playerId]; !existent { - Logger.Warn("upcmd player doesn't exist:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId)) - return - } - - if swapped := atomic.CompareAndSwapInt32(&(pR.Players[playerId].AckingFrameId), pR.Players[playerId].AckingFrameId, ackingFrameId); !swapped { - panic(fmt.Sprintf("Failed to update AckingFrameId to %v for roomId=%v, playerId=%v", ackingFrameId, pR.Id, playerId)) - } - - if swapped := atomic.CompareAndSwapInt32(&(pR.Players[playerId].AckingInputFrameId), pR.Players[playerId].AckingInputFrameId, ackingInputFrameId); !swapped { - panic(fmt.Sprintf("Failed to update AckingInputFrameId to %v for roomId=%v, playerId=%v", ackingInputFrameId, pR.Id, playerId)) - } clientInputFrameId := inputFrameUpsync.InputFrameId if clientInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { Logger.Warn("Obsolete inputFrameUpsync:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) @@ -538,66 +564,56 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { encodedInput := pR.EncodeUpsyncCmd(inputFrameUpsync) if clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId { - // The outer-if branching is for reducing an extra get-and-set operation, which is now placed in the else-branch. - for clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId { - newInputList := make([]uint64, len(pR.Players)) - newInputList[indiceInJoinIndexBooleanArr] = encodedInput - pR.AllPlayerInputsBuffer.Put(&pb.InputFrameDownsync{ - InputFrameId: pR.AllPlayerInputsBuffer.EdFrameId, - InputList: newInputList, - ConfirmedList: joinMask, // by now only the current player has confirmed this input frame - }) - if pR.inputFrameIdDebuggable(clientInputFrameId) { - Logger.Info("inputFrame lifecycle#1[inserted]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) - } - } - } else { - tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId) - if nil == tmp2 { - // This shouldn't happen due to the previous 2 checks - Logger.Warn("Mysterious error getting an input frame:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) - return - } - inputFrameDownsync := tmp2.(*pb.InputFrameDownsync) - oldConfirmedList := inputFrameDownsync.ConfirmedList - if (oldConfirmedList & joinMask) > 0 { - Logger.Warn("Cmd already confirmed but getting set attempt:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) - return - } - - // In Golang 1.12, there's no "compare-and-swap primitive" on a custom struct (or it's pointer, unless it's an unsafe pointer https://pkg.go.dev/sync/atomic@go1.12#CompareAndSwapPointer). Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. - if swapped := atomic.CompareAndSwapUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], uint64(0), encodedInput); !swapped { - if encodedInput > 0 { - Logger.Warn("Failed input CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId)) - } - return - } - - newConfirmedList := (oldConfirmedList | joinMask) - if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped { - if encodedInput > 0 { - Logger.Warn("Failed confirm CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId)) - } - return - } - - totPlayerCnt := uint32(len(pR.Players)) - allConfirmedMask := uint64((1 << totPlayerCnt) - 1) // TODO: What if a player is disconnected backthen? - if allConfirmedMask == newConfirmedList { - if false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { - atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), clientInputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here! - Logger.Info("Key inputFrame change", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", clientInputFrameId), zap.Any("lastInputFrameId", pR.LastAllConfirmedInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId), zap.Any("newInputList", inputFrameDownsync.InputList), zap.Any("lastInputList", pR.LastAllConfirmedInputList)) - } - atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! - for i, v := range inputFrameDownsync.InputList { - // To avoid potential misuse of pointers - pR.LastAllConfirmedInputList[i] = v - } - if pR.inputFrameIdDebuggable(clientInputFrameId) { - Logger.Info("inputFrame lifecycle#2[allconfirmed]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) - } - } + Logger.Warn("inputFrame too advanced! is the player cheating?", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + return } + tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId) + if nil == tmp2 { + // This shouldn't happen due to the previous 2 checks + Logger.Warn("Mysterious error getting an input frame:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + return + } + inputFrameDownsync := tmp2.(*pb.InputFrameDownsync) + oldConfirmedList := atomic.LoadUint64(&(inputFrameDownsync.ConfirmedList)) + if (oldConfirmedList & joinMask) > 0 { + Logger.Warn("Cmd already confirmed but getting set attempt, omitting this upsync cmd:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + return + } + + // In Golang 1.12, there's no "compare-and-swap primitive" on a custom struct (or it's pointer, unless it's an unsafe pointer https://pkg.go.dev/sync/atomic@go1.12#CompareAndSwapPointer). Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. + if swapped := atomic.CompareAndSwapUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], uint64(0), encodedInput); !swapped { + Logger.Warn("Failed input CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId)) + return + } + + newConfirmedList := (oldConfirmedList | joinMask) + if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped { + // [WARNING] Upon this error, the actual input has already been updated, which is an expected result if it caused by the force confirmation from "battleMainLoop". + Logger.Warn("Failed confirm CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId)) + return + } + + totPlayerCnt := uint32(len(pR.Players)) + allConfirmedMask := uint64((1 << totPlayerCnt) - 1) + if allConfirmedMask == newConfirmedList { + pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, playerId) + } + } +} + +func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFrameDownsync, playerId int32) { + clientInputFrameId := inputFrameDownsync.InputFrameId + if false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { + atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), clientInputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here! + Logger.Info("Key inputFrame change", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", clientInputFrameId), zap.Any("lastInputFrameId", pR.LastAllConfirmedInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()), zap.Any("newInputList", inputFrameDownsync.InputList), zap.Any("lastInputList", pR.LastAllConfirmedInputList)) + } + atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! + for i, v := range inputFrameDownsync.InputList { + // To avoid potential misuse of pointers + pR.LastAllConfirmedInputList[i] = v + } + if pR.inputFrameIdDebuggable(clientInputFrameId) { + Logger.Info("inputFrame lifecycle#2[allconfirmed]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) } } @@ -619,11 +635,11 @@ func (pR *Room) StopBattleForSettlement() { } pR.State = RoomBattleStateIns.STOPPING_BATTLE_FOR_SETTLEMENT Logger.Info("Stopping the `battleMainLoop` for:", zap.Any("roomId", pR.Id)) - pR.Tick++ + pR.RenderFrameId++ for playerId, _ := range pR.Players { assembledFrame := pb.RoomDownsyncFrame{ - Id: pR.Tick, - RefFrameId: pR.Tick, // Hardcoded for now. + Id: pR.RenderFrameId, + RefFrameId: pR.RenderFrameId, // Hardcoded for now. Players: toPbPlayers(pR.Players), SentAt: utils.UnixtimeMilli(), CountdownNanos: -1, // TODO: Replace this magic constant! @@ -661,7 +677,7 @@ func (pR *Room) onBattlePrepare(cb BattleStartCbType) { } battleReadyToStartFrame := pb.RoomDownsyncFrame{ - Id: pR.Tick, + Id: pR.RenderFrameId, Players: toPbPlayers(pR.Players), SentAt: utils.UnixtimeMilli(), RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START, @@ -733,6 +749,8 @@ func (pR *Room) onDismissed() { // Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference. pR.Players = make(map[int32]*Player) + pR.PlayersArr = make([]*Player, pR.Capacity) + pR.CollisionSysMap = make(map[int32]*resolv.Object) pR.PlayerDownsyncSessionDict = make(map[int32]*websocket.Conn) pR.PlayerSignalToCloseDict = make(map[int32]SignalToCloseConnCbType) @@ -908,7 +926,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { switch pPlayer.BattleState { case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK: playerAckedFrame = pb.RoomDownsyncFrame{ - Id: pR.Tick, + Id: pR.RenderFrameId, Players: toPbPlayers(pR.Players), SentAt: utils.UnixtimeMilli(), RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_ADDED_AND_ACKED, @@ -916,7 +934,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { } case PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK: playerAckedFrame = pb.RoomDownsyncFrame{ - Id: pR.Tick, + Id: pR.RenderFrameId, Players: toPbPlayers(pR.Players), SentAt: utils.UnixtimeMilli(), RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED, @@ -963,12 +981,15 @@ func (pR *Room) sendSafely(s interface{}, playerId int32) { } }() - var resp *pb.WsResp = nil + var pResp *pb.WsResp = nil switch v := s.(type) { + case pb.WsResp: + resp := s.(pb.WsResp) + pResp = &resp case pb.RoomDownsyncFrame: roomDownsyncFrame := s.(pb.RoomDownsyncFrame) - resp = &pb.WsResp{ + pResp = &pb.WsResp{ Ret: int32(Constants.RetCode.Ok), EchoedMsgId: int32(0), Act: DOWNSYNC_MSG_ACT_ROOM_FRAME, @@ -976,7 +997,7 @@ func (pR *Room) sendSafely(s interface{}, playerId int32) { } case []*pb.InputFrameDownsync: toSendFrames := s.([]*pb.InputFrameDownsync) - resp = &pb.WsResp{ + pResp = &pb.WsResp{ Ret: int32(Constants.RetCode.Ok), EchoedMsgId: int32(0), Act: DOWNSYNC_MSG_ACT_INPUT_BATCH, @@ -986,7 +1007,7 @@ func (pR *Room) sendSafely(s interface{}, playerId int32) { panic(fmt.Sprintf("Unknown downsync message type, roomId=%v, playerId=%v, roomState=%v, v=%v", pR.Id, playerId, v)) } - theBytes, marshalErr := proto.Marshal(resp) + theBytes, marshalErr := proto.Marshal(pResp) if nil != marshalErr { panic(fmt.Sprintf("Error marshaling downsync message: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount)) } @@ -996,156 +1017,142 @@ func (pR *Room) sendSafely(s interface{}, playerId int32) { } } +func (pR *Room) shouldPrefabInputFrameDownsync(renderFrameId int32) bool { + return ((renderFrameId & ((1 << pR.InputScaleFrames) - 1)) == 0) +} + +func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDownsync { + /* + Kindly note that on backend the prefab is much simpler than its frontend counterpart, because frontend will upsync its latest command immediately if there's any change w.r.t. its own prev cmd, thus if no upsync received from a frontend, + - EITHER it's due to local lag and bad network, + - OR there's no change w.r.t. to its prev cmd. + */ + var currInputFrameDownsync *pb.InputFrameDownsync = nil + + if 0 == inputFrameId && 0 == pR.AllPlayerInputsBuffer.Cnt { + currInputFrameDownsync = &pb.InputFrameDownsync{ + InputFrameId: 0, + InputList: make([]uint64, pR.Capacity), + ConfirmedList: uint64(0), + } + } else { + tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId - 1) + if nil == tmp { + panic(fmt.Sprintf("Error prefabbing inputFrameDownsync: roomId=%v, AllPlayerInputsBuffer=%v", pR.Id, pR.AllPlayerInputsBufferString())) + } + prevInputFrameDownsync := tmp.(*pb.InputFrameDownsync) + currInputList := prevInputFrameDownsync.InputList // Would be a clone of the values + currInputFrameDownsync = &pb.InputFrameDownsync{ + InputFrameId: inputFrameId, + InputList: currInputList, + ConfirmedList: uint64(0), + } + } + + pR.AllPlayerInputsBuffer.Put(currInputFrameDownsync) + return currInputFrameDownsync +} + +func (pR *Room) forceConfirmationIfApplicable() uint64 { + // Force confirmation of non-all-confirmed inputFrame EXACTLY ONE AT A TIME, returns the non-confirmed mask of players, e.g. in a 4-player-battle returning 1001 means that players with JoinIndex=1 and JoinIndex=4 are non-confirmed for inputFrameId2 + renderFrameId1 := (pR.RenderFrameId - pR.NstDelayFrames) // the renderFrameId which should've been rendered on frontend + if 0 > renderFrameId1 || !pR.shouldPrefabInputFrameDownsync(renderFrameId1) { + /* + The backend "shouldPrefabInputFrameDownsync" shares the same rule as frontend "shouldGenerateInputFrameUpsync". + */ + return 0 + } + + inputFrameId2 := pR.ConvertToInputFrameId(renderFrameId1, 0) // The inputFrame to force confirmation (if necessary) + tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId2) + if nil == tmp { + panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! AllPlayerInputsBuffer=%v", inputFrameId2, pR.Id, pR.AllPlayerInputsBufferString())) + } + inputFrame2 := tmp.(*pb.InputFrameDownsync) + + totPlayerCnt := uint32(pR.Capacity) + allConfirmedMask := uint64((1 << totPlayerCnt) - 1) + if swapped := atomic.CompareAndSwapUint64(&(inputFrame2.ConfirmedList), allConfirmedMask, allConfirmedMask); swapped { + Logger.Info(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v, no need to force confirmation of it", inputFrameId2, pR.Id)) + return 0 + } + + // Force confirmation of "inputFrame2" + oldConfirmedList := atomic.LoadUint64(&(inputFrame2.ConfirmedList)) + atomic.StoreUint64(&(inputFrame2.ConfirmedList), allConfirmedMask) + pR.onInputFrameDownsyncAllConfirmed(inputFrame2, -1) + + return (oldConfirmedList^allConfirmedMask) +} + +func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRenderFrameId int32) { + if fromRenderFrameId >= toRenderFrameId { + return + } + + totPlayerCnt := uint32(pR.Capacity) + allConfirmedMask := uint64((1 << totPlayerCnt) - 1) + + for collisionSysRenderFrameId := fromRenderFrameId; collisionSysRenderFrameId < toRenderFrameId; collisionSysRenderFrameId++ { + delayedInputFrameId := pR.ConvertToInputFrameId(collisionSysRenderFrameId, pR.InputDelayFrames) + if 0 <= delayedInputFrameId { + tmp := pR.AllPlayerInputsBuffer.GetByFrameId(delayedInputFrameId) + if nil == tmp { + panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString())) + } + delayedInputFrame := tmp.(pb.InputFrameDownsync) + if swapped := atomic.CompareAndSwapUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask, allConfirmedMask); !swapped { + panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString())) + } + + inputList := delayedInputFrame.InputList + // Ordered by joinIndex to guarantee determinism + for _, player := range pR.PlayersArr { + joinIndex := player.JoinIndex + collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex + playerCollider := pR.CollisionSysMap[collisionPlayerIndex] + encodedInput := inputList[joinIndex-1] + decodedInput := DIRECTION_DECODER[encodedInput] + decodedInputSpeedFactor := DIRECTION_DECODER_INVERSE_LENGTH[encodedInput] + baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor + dx := baseChange * float64(decodedInput[0]) + dy := baseChange * float64(decodedInput[1]) + if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { + changeWithCollision := collision.ContactWithObject(collision.Objects[0]) + dx = changeWithCollision.X() + dy = changeWithCollision.Y() + } + playerCollider.X += dx + playerCollider.Y += dy + // Update in "collision space" + playerCollider.Update() + + player.Dir.Dx = decodedInput[0] + player.Dir.Dy = decodedInput[1] + player.X = playerCollider.X + player.Y = playerCollider.Y + } + } + + newRenderFrame := pb.RoomDownsyncFrame{ + Id: collisionSysRenderFrameId+1, + RefFrameId: collisionSysRenderFrameId, + Players: toPbPlayers(pR.Players), + SentAt: utils.UnixtimeMilli(), + CountdownNanos: (pR.BattleDurationNanos - int64(collisionSysRenderFrameId)*int64(pR.RollbackEstimatedDt*1000000000)), + } + pR.RenderFrameBuffer.Put(&newRenderFrame) + pR.CurDynamicsRenderFrameId++ + } +} + func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool { return 0 == (inputFrameId % 10) } func (pR *Room) refreshColliders() { - gravity := box2d.MakeB2Vec2(0.0, 0.0) - world := box2d.MakeB2World(gravity) - world.SetContactFilter(&box2d.B2ContactFilter{}) - pR.CollidableWorld = &world - - Logger.Info("Begins players collider processing:", zap.Any("roomId", pR.Id)) for _, player := range pR.Players { - var bdDef box2d.B2BodyDef - colliderOffset := box2d.MakeB2Vec2(0, 0) // Matching that of client-side setting. - bdDef = box2d.MakeB2BodyDef() - bdDef.Type = box2d.B2BodyType.B2_dynamicBody - bdDef.Position.Set(player.X+colliderOffset.X, player.Y+colliderOffset.Y) - - b2Body := pR.CollidableWorld.CreateBody(&bdDef) - - b2CircleShape := box2d.MakeB2CircleShape() - b2CircleShape.M_radius = 32 // Matching that of client-side setting. - - fd := box2d.MakeB2FixtureDef() - fd.Shape = &b2CircleShape - fd.Filter.CategoryBits = COLLISION_CATEGORY_CONTROLLED_PLAYER - fd.Filter.MaskBits = COLLISION_MASK_FOR_CONTROLLED_PLAYER - fd.Density = 0.0 - b2Body.CreateFixtureFromDef(&fd) - - player.CollidableBody = b2Body - b2Body.SetUserData(player) - } - Logger.Info("Ends players collider processing:", zap.Any("roomId", pR.Id)) - - Logger.Info("Begins barriers collider processing:", zap.Any("roomId", pR.Id)) - for _, barrier := range pR.Barriers { - var bdDef box2d.B2BodyDef - bdDef.Type = box2d.B2BodyType.B2_dynamicBody - bdDef = box2d.MakeB2BodyDef() - bdDef.Position.Set(barrier.Boundary.Anchor.X, barrier.Boundary.Anchor.Y) - - b2Body := pR.CollidableWorld.CreateBody(&bdDef) - - pointsCount := len(barrier.Boundary.Points) - - b2Vertices := make([]box2d.B2Vec2, pointsCount) - for vIndex, v2 := range barrier.Boundary.Points { - b2Vertices[vIndex] = v2.ToB2Vec2() - } - - b2PolygonShape := box2d.MakeB2PolygonShape() - b2PolygonShape.Set(b2Vertices, pointsCount) - - fd := box2d.MakeB2FixtureDef() - fd.Shape = &b2PolygonShape - fd.Filter.CategoryBits = COLLISION_CATEGORY_BARRIER - fd.Filter.MaskBits = COLLISION_MASK_FOR_BARRIER - fd.Density = 0.0 - b2Body.CreateFixtureFromDef(&fd) - - barrier.CollidableBody = b2Body - b2Body.SetUserData(barrier) - } - Logger.Info("Ends barriers collider processing:", zap.Any("roomId", pR.Id)) - - listener := RoomBattleContactListener{ - name: "DelayNoMore", - room: pR, - } - /* - * Setting a "ContactListener" for "pR.CollidableWorld" - * will only trigger corresponding callbacks in the - * SAME GOROUTINE of "pR.CollidableWorld.Step(...)" according - * to "https://github.com/ByteArena/box2d/blob/master/DynamicsB2World.go" and - * "https://github.com/ByteArena/box2d/blob/master/DynamicsB2Contact.go". - * - * The invocation-chain involves "Step -> SolveTOI -> B2ContactUpdate -> [BeginContact, EndContact, PreSolve]". - */ - pR.CollidableWorld.SetContactListener(listener) -} - -type RoomBattleContactListener struct { - name string - room *Room -} - -// Implementing the GolangBox2d contact listeners [begins]. -/** - * Note that the execution of these listeners is within the SAME GOROUTINE as that of "`battleMainLoop` in the same room". - * See the comments in `Room.refreshColliders()` for details. - */ -func (l RoomBattleContactListener) BeginContact(contact box2d.B2ContactInterface) { - var pBarrier *Barrier - var pPlayer *Player - - switch v := contact.GetNodeA().Other.GetUserData().(type) { - case *Barrier: - pBarrier = v - case *Player: - pPlayer = v - default: - // - } - - switch v := contact.GetNodeB().Other.GetUserData().(type) { - case *Barrier: - pBarrier = v - case *Player: - pPlayer = v - default: - } - - if pBarrier != nil && pPlayer != nil { - Logger.Info("player begins collision with barrier:", zap.Any("barrier", pBarrier), zap.Any("player", pPlayer)) - // TODO: Push back player + joinIndex := player.JoinIndex + pR.PlayersArr[joinIndex-1] = player } } - -func (l RoomBattleContactListener) EndContact(contact box2d.B2ContactInterface) { - var pBarrier *Barrier - var pPlayer *Player - - switch v := contact.GetNodeA().Other.GetUserData().(type) { - case *Barrier: - pBarrier = v - case *Player: - pPlayer = v - default: - } - - switch v := contact.GetNodeB().Other.GetUserData().(type) { - case *Barrier: - pBarrier = v - case *Player: - pPlayer = v - default: - } - - if pBarrier != nil && pPlayer != nil { - Logger.Info("player ends collision with barrier:", zap.Any("barrier", pBarrier), zap.Any("player", pPlayer)) - } -} - -func (l RoomBattleContactListener) PreSolve(contact box2d.B2ContactInterface, oldManifold box2d.B2Manifold) { - //fmt.Printf("I am PreSolve %s\n", l.name); -} - -func (l RoomBattleContactListener) PostSolve(contact box2d.B2ContactInterface, impulse *box2d.B2ContactImpulse) { - //fmt.Printf("PostSolve %s\n", l.name); -} - -// Implementing the GolangBox2d contact listeners [ends]. diff --git a/battle_srv/models/room_heap_manager.go b/battle_srv/models/room_heap_manager.go index fc207b5..7669724 100644 --- a/battle_srv/models/room_heap_manager.go +++ b/battle_srv/models/room_heap_manager.go @@ -4,6 +4,7 @@ import ( "container/heap" "fmt" "github.com/gorilla/websocket" + "github.com/solarlune/resolv" "go.uber.org/zap" . "server/common" "sync" @@ -100,27 +101,31 @@ func InitRoomHeapManager() { pq[i] = &Room{ Id: int32(i + 1), Players: make(map[int32]*Player), + PlayersArr: make([]*Player, roomCapacity), + CollisionSysMap: make(map[int32]*resolv.Object), PlayerDownsyncSessionDict: make(map[int32]*websocket.Conn), PlayerSignalToCloseDict: make(map[int32]SignalToCloseConnCbType), Capacity: roomCapacity, Score: calRoomScore(0, roomCapacity, currentRoomBattleState), State: currentRoomBattleState, Index: i, - Tick: 0, + RenderFrameId: 0, + CurDynamicsRenderFrameId: 0, EffectivePlayerCount: 0, //BattleDurationNanos: int64(5 * 1000 * 1000 * 1000), BattleDurationNanos: int64(30 * 1000 * 1000 * 1000), ServerFPS: 60, Barriers: make(map[int32]*Barrier), - AccumulatedLocalIdForBullets: 0, AllPlayerInputsBuffer: NewRingBuffer(1024), - RenderFrameBuffer: NewRingBuffer(1024), + RenderFrameBuffer: NewRingBuffer(1024), LastAllConfirmedInputFrameId: -1, LastAllConfirmedInputFrameIdWithChange: -1, LastAllConfirmedInputList: make([]uint64, roomCapacity), InputDelayFrames: 4, + NstDelayFrames: 2, InputScaleFrames: 2, JoinIndexBooleanArr: joinIndexBooleanArr, + RollbackEstimatedDt: float64(1.0) / 60, } roomMap[pq[i].Id] = pq[i] pq[i].ChooseStage() diff --git a/battle_srv/models/tiled_map.go b/battle_srv/models/tiled_map.go index c6943c4..aeb5f00 100644 --- a/battle_srv/models/tiled_map.go +++ b/battle_srv/models/tiled_map.go @@ -19,7 +19,7 @@ import ( const ( LOW_SCORE_TREASURE_TYPE = 1 HIGH_SCORE_TREASURE_TYPE = 2 - SPEED_SHOES_TYPE = 3 + SPEED_SHOES_TYPE = 3 LOW_SCORE_TREASURE_SCORE = 100 HIGH_SCORE_TREASURE_SCORE = 200 diff --git a/battle_srv/pb_output/room_downsync_frame.pb.go b/battle_srv/pb_output/room_downsync_frame.pb.go index 5693445..d9d60ff 100644 --- a/battle_srv/pb_output/room_downsync_frame.pb.go +++ b/battle_srv/pb_output/room_downsync_frame.pb.go @@ -407,7 +407,7 @@ type Player struct { X float64 `protobuf:"fixed64,2,opt,name=x,proto3" json:"x,omitempty"` Y float64 `protobuf:"fixed64,3,opt,name=y,proto3" json:"y,omitempty"` Dir *Direction `protobuf:"bytes,4,opt,name=dir,proto3" json:"dir,omitempty"` - Speed int32 `protobuf:"varint,5,opt,name=speed,proto3" json:"speed,omitempty"` + Speed float64 `protobuf:"fixed64,5,opt,name=speed,proto3" json:"speed,omitempty"` BattleState int32 `protobuf:"varint,6,opt,name=battleState,proto3" json:"battleState,omitempty"` LastMoveGmtMillis int32 `protobuf:"varint,7,opt,name=lastMoveGmtMillis,proto3" json:"lastMoveGmtMillis,omitempty"` Score int32 `protobuf:"varint,10,opt,name=score,proto3" json:"score,omitempty"` @@ -475,7 +475,7 @@ func (x *Player) GetDir() *Direction { return nil } -func (x *Player) GetSpeed() int32 { +func (x *Player) GetSpeed() float64 { if x != nil { return x.Speed } @@ -596,6 +596,171 @@ func (x *PlayerMeta) GetJoinIndex() int32 { return 0 } +type InputFrameUpsync struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InputFrameId int32 `protobuf:"varint,1,opt,name=inputFrameId,proto3" json:"inputFrameId,omitempty"` + EncodedDir int32 `protobuf:"varint,6,opt,name=encodedDir,proto3" json:"encodedDir,omitempty"` +} + +func (x *InputFrameUpsync) Reset() { + *x = InputFrameUpsync{} + if protoimpl.UnsafeEnabled { + mi := &file_room_downsync_frame_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InputFrameUpsync) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InputFrameUpsync) ProtoMessage() {} + +func (x *InputFrameUpsync) ProtoReflect() protoreflect.Message { + mi := &file_room_downsync_frame_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InputFrameUpsync.ProtoReflect.Descriptor instead. +func (*InputFrameUpsync) Descriptor() ([]byte, []int) { + return file_room_downsync_frame_proto_rawDescGZIP(), []int{8} +} + +func (x *InputFrameUpsync) GetInputFrameId() int32 { + if x != nil { + return x.InputFrameId + } + return 0 +} + +func (x *InputFrameUpsync) GetEncodedDir() int32 { + if x != nil { + return x.EncodedDir + } + return 0 +} + +type InputFrameDownsync struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InputFrameId int32 `protobuf:"varint,1,opt,name=inputFrameId,proto3" json:"inputFrameId,omitempty"` + InputList []uint64 `protobuf:"varint,2,rep,packed,name=inputList,proto3" json:"inputList,omitempty"` // Indexed by "joinIndex", we try to compress the "single player input" into 1 word (64-bit for 64-bit Golang runtime) because atomic compare-and-swap only works on 1 word. Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. + ConfirmedList uint64 `protobuf:"varint,3,opt,name=confirmedList,proto3" json:"confirmedList,omitempty"` // Indexed by "joinIndex", same compression concern as above +} + +func (x *InputFrameDownsync) Reset() { + *x = InputFrameDownsync{} + if protoimpl.UnsafeEnabled { + mi := &file_room_downsync_frame_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InputFrameDownsync) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InputFrameDownsync) ProtoMessage() {} + +func (x *InputFrameDownsync) ProtoReflect() protoreflect.Message { + mi := &file_room_downsync_frame_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InputFrameDownsync.ProtoReflect.Descriptor instead. +func (*InputFrameDownsync) Descriptor() ([]byte, []int) { + return file_room_downsync_frame_proto_rawDescGZIP(), []int{9} +} + +func (x *InputFrameDownsync) GetInputFrameId() int32 { + if x != nil { + return x.InputFrameId + } + return 0 +} + +func (x *InputFrameDownsync) GetInputList() []uint64 { + if x != nil { + return x.InputList + } + return nil +} + +func (x *InputFrameDownsync) GetConfirmedList() uint64 { + if x != nil { + return x.ConfirmedList + } + return 0 +} + +type HeartbeatUpsync struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClientTimestamp int64 `protobuf:"varint,1,opt,name=clientTimestamp,proto3" json:"clientTimestamp,omitempty"` +} + +func (x *HeartbeatUpsync) Reset() { + *x = HeartbeatUpsync{} + if protoimpl.UnsafeEnabled { + mi := &file_room_downsync_frame_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HeartbeatUpsync) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HeartbeatUpsync) ProtoMessage() {} + +func (x *HeartbeatUpsync) ProtoReflect() protoreflect.Message { + mi := &file_room_downsync_frame_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HeartbeatUpsync.ProtoReflect.Descriptor instead. +func (*HeartbeatUpsync) Descriptor() ([]byte, []int) { + return file_room_downsync_frame_proto_rawDescGZIP(), []int{10} +} + +func (x *HeartbeatUpsync) GetClientTimestamp() int64 { + if x != nil { + return x.ClientTimestamp + } + return 0 +} + type RoomDownsyncFrame struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -612,7 +777,7 @@ type RoomDownsyncFrame struct { func (x *RoomDownsyncFrame) Reset() { *x = RoomDownsyncFrame{} if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[8] + mi := &file_room_downsync_frame_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -625,7 +790,7 @@ func (x *RoomDownsyncFrame) String() string { func (*RoomDownsyncFrame) ProtoMessage() {} func (x *RoomDownsyncFrame) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[8] + mi := &file_room_downsync_frame_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -638,7 +803,7 @@ func (x *RoomDownsyncFrame) ProtoReflect() protoreflect.Message { // Deprecated: Use RoomDownsyncFrame.ProtoReflect.Descriptor instead. func (*RoomDownsyncFrame) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{8} + return file_room_downsync_frame_proto_rawDescGZIP(), []int{11} } func (x *RoomDownsyncFrame) GetId() int32 { @@ -683,171 +848,6 @@ func (x *RoomDownsyncFrame) GetPlayerMetas() map[int32]*PlayerMeta { return nil } -type InputFrameUpsync struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - InputFrameId int32 `protobuf:"varint,1,opt,name=inputFrameId,proto3" json:"inputFrameId,omitempty"` - EncodedDir int32 `protobuf:"varint,6,opt,name=encodedDir,proto3" json:"encodedDir,omitempty"` -} - -func (x *InputFrameUpsync) Reset() { - *x = InputFrameUpsync{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *InputFrameUpsync) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*InputFrameUpsync) ProtoMessage() {} - -func (x *InputFrameUpsync) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use InputFrameUpsync.ProtoReflect.Descriptor instead. -func (*InputFrameUpsync) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{9} -} - -func (x *InputFrameUpsync) GetInputFrameId() int32 { - if x != nil { - return x.InputFrameId - } - return 0 -} - -func (x *InputFrameUpsync) GetEncodedDir() int32 { - if x != nil { - return x.EncodedDir - } - return 0 -} - -type InputFrameDownsync struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - InputFrameId int32 `protobuf:"varint,1,opt,name=inputFrameId,proto3" json:"inputFrameId,omitempty"` - InputList []uint64 `protobuf:"varint,2,rep,packed,name=inputList,proto3" json:"inputList,omitempty"` // Indexed by "joinIndex", we try to compress the "single player input" into 1 word (64-bit for 64-bit Golang runtime) because atomic compare-and-swap only works on 1 word. Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. - ConfirmedList uint64 `protobuf:"varint,3,opt,name=confirmedList,proto3" json:"confirmedList,omitempty"` // Indexed by "joinIndex", same compression concern as above -} - -func (x *InputFrameDownsync) Reset() { - *x = InputFrameDownsync{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *InputFrameDownsync) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*InputFrameDownsync) ProtoMessage() {} - -func (x *InputFrameDownsync) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use InputFrameDownsync.ProtoReflect.Descriptor instead. -func (*InputFrameDownsync) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{10} -} - -func (x *InputFrameDownsync) GetInputFrameId() int32 { - if x != nil { - return x.InputFrameId - } - return 0 -} - -func (x *InputFrameDownsync) GetInputList() []uint64 { - if x != nil { - return x.InputList - } - return nil -} - -func (x *InputFrameDownsync) GetConfirmedList() uint64 { - if x != nil { - return x.ConfirmedList - } - return 0 -} - -type HeartbeatUpsync struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ClientTimestamp int64 `protobuf:"varint,1,opt,name=clientTimestamp,proto3" json:"clientTimestamp,omitempty"` -} - -func (x *HeartbeatUpsync) Reset() { - *x = HeartbeatUpsync{} - if protoimpl.UnsafeEnabled { - mi := &file_room_downsync_frame_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HeartbeatUpsync) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HeartbeatUpsync) ProtoMessage() {} - -func (x *HeartbeatUpsync) ProtoReflect() protoreflect.Message { - mi := &file_room_downsync_frame_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HeartbeatUpsync.ProtoReflect.Descriptor instead. -func (*HeartbeatUpsync) Descriptor() ([]byte, []int) { - return file_room_downsync_frame_proto_rawDescGZIP(), []int{11} -} - -func (x *HeartbeatUpsync) GetClientTimestamp() int64 { - if x != nil { - return x.ClientTimestamp - } - return 0 -} - type WsReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1119,7 +1119,7 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x12, 0x20, 0x0a, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, 0x4d, 0x69, @@ -1138,54 +1138,54 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69, - 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xd7, 0x03, 0x0a, 0x11, 0x52, 0x6f, 0x6f, 0x6d, 0x44, - 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, - 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x49, 0x0a, 0x07, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, - 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, - 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41, 0x74, 0x12, - 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, - 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, - 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, - 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, - 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, - 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, - 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, + 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, + 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x22, 0x7c, + 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, - 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, - 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, - 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, - 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, - 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, - 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x22, 0xca, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, + 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f, + 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, + 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xd7, 0x03, 0x0a, 0x11, 0x52, 0x6f, + 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, + 0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, + 0x6e, 0x74, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x74, + 0x41, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, + 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, + 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0xca, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, @@ -1250,10 +1250,10 @@ var file_room_downsync_frame_proto_goTypes = []interface{}{ (*BattleColliderInfo)(nil), // 5: treasurehunterx.BattleColliderInfo (*Player)(nil), // 6: treasurehunterx.Player (*PlayerMeta)(nil), // 7: treasurehunterx.PlayerMeta - (*RoomDownsyncFrame)(nil), // 8: treasurehunterx.RoomDownsyncFrame - (*InputFrameUpsync)(nil), // 9: treasurehunterx.InputFrameUpsync - (*InputFrameDownsync)(nil), // 10: treasurehunterx.InputFrameDownsync - (*HeartbeatUpsync)(nil), // 11: treasurehunterx.HeartbeatUpsync + (*InputFrameUpsync)(nil), // 8: treasurehunterx.InputFrameUpsync + (*InputFrameDownsync)(nil), // 9: treasurehunterx.InputFrameDownsync + (*HeartbeatUpsync)(nil), // 10: treasurehunterx.HeartbeatUpsync + (*RoomDownsyncFrame)(nil), // 11: treasurehunterx.RoomDownsyncFrame (*WsReq)(nil), // 12: treasurehunterx.WsReq (*WsResp)(nil), // 13: treasurehunterx.WsResp nil, // 14: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry @@ -1271,10 +1271,10 @@ var file_room_downsync_frame_proto_depIdxs = []int32{ 0, // 6: treasurehunterx.Player.dir:type_name -> treasurehunterx.Direction 16, // 7: treasurehunterx.RoomDownsyncFrame.players:type_name -> treasurehunterx.RoomDownsyncFrame.PlayersEntry 17, // 8: treasurehunterx.RoomDownsyncFrame.playerMetas:type_name -> treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry - 9, // 9: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync - 11, // 10: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync - 8, // 11: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame - 10, // 12: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync + 8, // 9: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync + 10, // 10: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync + 11, // 11: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame + 9, // 12: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync 5, // 13: treasurehunterx.WsResp.bciFrame:type_name -> treasurehunterx.BattleColliderInfo 3, // 14: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> treasurehunterx.Vec2DList 4, // 15: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> treasurehunterx.Polygon2DList @@ -1390,18 +1390,6 @@ func file_room_downsync_frame_proto_init() { } } file_room_downsync_frame_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoomDownsyncFrame); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_room_downsync_frame_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InputFrameUpsync); i { case 0: return &v.state @@ -1413,7 +1401,7 @@ func file_room_downsync_frame_proto_init() { return nil } } - file_room_downsync_frame_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_room_downsync_frame_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InputFrameDownsync); i { case 0: return &v.state @@ -1425,7 +1413,7 @@ func file_room_downsync_frame_proto_init() { return nil } } - file_room_downsync_frame_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_room_downsync_frame_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HeartbeatUpsync); i { case 0: return &v.state @@ -1437,6 +1425,18 @@ func file_room_downsync_frame_proto_init() { return nil } } + file_room_downsync_frame_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoomDownsyncFrame); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } file_room_downsync_frame_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WsReq); i { case 0: diff --git a/frontend/assets/resources/pbfiles/room_downsync_frame.proto b/frontend/assets/resources/pbfiles/room_downsync_frame.proto index a48aa0f..36d141a 100644 --- a/frontend/assets/resources/pbfiles/room_downsync_frame.proto +++ b/frontend/assets/resources/pbfiles/room_downsync_frame.proto @@ -45,7 +45,7 @@ message Player { double x = 2; double y = 3; Direction dir = 4; - int32 speed = 5; + double speed = 5; int32 battleState = 6; int32 lastMoveGmtMillis = 7; int32 score = 10; @@ -61,15 +61,6 @@ message PlayerMeta { int32 joinIndex = 5; } -message RoomDownsyncFrame { - int32 id = 1; - int32 refFrameId = 2; - map players = 3; - int64 sentAt = 4; - int64 countdownNanos = 5; - map playerMetas = 6; -} - message InputFrameUpsync { int32 inputFrameId = 1; int32 encodedDir = 6; @@ -85,6 +76,15 @@ message HeartbeatUpsync { int64 clientTimestamp = 1; } +message RoomDownsyncFrame { + int32 id = 1; + int32 refFrameId = 2; + map players = 3; + int64 sentAt = 4; + int64 countdownNanos = 5; + map playerMetas = 6; +} + message WsReq { int32 msgId = 1; int32 playerId = 2; diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index b92a56f..1782942 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -182,11 +182,11 @@ cc.Class({ return ((renderFrameId - inputDelayFrames) >> this.inputScaleFrames); }, - _convertToRenderFrameId(inputFrameId, inputDelayFrames) { + _convertToFirstUsedRenderFrameId(inputFrameId, inputDelayFrames) { return ((inputFrameId << this.inputScaleFrames) + inputDelayFrames); }, - _shouldGenerateInputFrameUpsync(renderFrameId) { + shouldGenerateInputFrameUpsync(renderFrameId) { return ((renderFrameId & ((1 << this.inputScaleFrames)-1)) == 0); }, @@ -608,7 +608,7 @@ cc.Class({ if (null != firstPredictedYetIncorrectInputFrameId) { const inputFrameId1 = firstPredictedYetIncorrectInputFrameId; - const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" + const renderFrameId1 = self._convertToFirstUsedRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" if (renderFrameId1 < self.renderFrameId) { /* A typical case is as follows. @@ -787,7 +787,7 @@ cc.Class({ try { let prevSelfInput = null, currSelfInput = null; const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here - if (self._shouldGenerateInputFrameUpsync(self.renderFrameId)) { + if (self.shouldGenerateInputFrameUpsync(self.renderFrameId)) { const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId); prevSelfInput = prevAndCurrInputs[0]; currSelfInput = prevAndCurrInputs[1]; @@ -937,7 +937,7 @@ cc.Class({ _createRoomDownsyncFrameLocally(renderFrameId, collisionSys, collisionSysMap) { const self = this; const prevRenderFrameId = renderFrameId-1; - const inputFrameForPrevRenderFrame = ( + const inputFrameAppliedOnPrevRenderFrame = ( 0 > prevRenderFrameId ? null @@ -948,11 +948,11 @@ cc.Class({ // TODO: Find a better way to assign speeds instead of using "speedRefRenderFrameId". const speedRefRenderFrameId = prevRenderFrameId; const speedRefRenderFrame = ( - 0 > prevRenderFrameId + 0 > speedRefRenderFrameId ? null : - self.recentRenderCache.getByFrameId(prevRenderFrameId) + self.recentRenderCache.getByFrameId(speedRefRenderFrameId) ); const rdf = { @@ -968,13 +968,13 @@ cc.Class({ id: playerRichInfo.id, x: playerCollider.x, y: playerCollider.y, - dir: self.ctrl.decodeDirection(null == inputFrameForPrevRenderFrame ? 0 : inputFrameForPrevRenderFrame.inputList[joinIndex-1]), + dir: self.ctrl.decodeDirection(null == inputFrameAppliedOnPrevRenderFrame ? 0 : inputFrameAppliedOnPrevRenderFrame.inputList[joinIndex-1]), speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed), joinIndex: joinIndex }; }); if ( - null != inputFrameForPrevRenderFrame && self._allConfirmed(inputFrameForPrevRenderFrame.confirmedList) + null != inputFrameAppliedOnPrevRenderFrame && self._allConfirmed(inputFrameAppliedOnPrevRenderFrame.confirmedList) && self.lastAllConfirmedRenderFrameId >= prevRenderFrameId && @@ -1033,6 +1033,8 @@ cc.Class({ playerCollider.y = player.y; }); + // [WARNING] Traverse in the order of joinIndices to guarantee determinism. + /* This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd". */ @@ -1040,8 +1042,8 @@ cc.Class({ const renderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame" const j = self._convertToInputFrameId(i, self.inputDelayFrames); const inputList = self.getCachedInputFrameDownsyncWithPrediction(j).inputList; - self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { - const joinIndex = playerRichInfo.joinIndex; + for (let j in self.playerRichInfoArr) { + const joinIndex = parseInt(j) + 1; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const playerCollider = collisionSysMap.get(collisionPlayerIndex); const player = renderFrame.players[playerId]; @@ -1055,12 +1057,11 @@ cc.Class({ console.log("playerId=", playerId, "@renderFrameId=", i, ", delayedInputFrameId=", j, ", baseChange=", baseChange, ": x=", playerCollider.x, ", y=", playerCollider.y); } */ - }); + } collisionSys.update(); const result = collisionSys.createResult(); // Can I reuse a "self.latestCollisionSysResult" object throughout the whole battle? - // [WARNING] Traverse in the order of joinIndices to guarantee determinism. for (let i in self.playerRichInfoArr) { const joinIndex = parseInt(i) + 1; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; diff --git a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js index 3cb3729..4a2a3e8 100644 --- a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js +++ b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js @@ -1815,7 +1815,7 @@ $root.treasurehunterx = (function() { if (message.dir != null && Object.hasOwnProperty.call(message, "dir")) $root.treasurehunterx.Direction.encode(message.dir, writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); if (message.speed != null && Object.hasOwnProperty.call(message, "speed")) - writer.uint32(/* id 5, wireType 0 =*/40).int32(message.speed); + writer.uint32(/* id 5, wireType 1 =*/41).double(message.speed); if (message.battleState != null && Object.hasOwnProperty.call(message, "battleState")) writer.uint32(/* id 6, wireType 0 =*/48).int32(message.battleState); if (message.lastMoveGmtMillis != null && Object.hasOwnProperty.call(message, "lastMoveGmtMillis")) @@ -1877,7 +1877,7 @@ $root.treasurehunterx = (function() { break; } case 5: { - message.speed = reader.int32(); + message.speed = reader.double(); break; } case 6: { @@ -1950,8 +1950,8 @@ $root.treasurehunterx = (function() { return "dir." + error; } if (message.speed != null && message.hasOwnProperty("speed")) - if (!$util.isInteger(message.speed)) - return "speed: integer expected"; + if (typeof message.speed !== "number") + return "speed: number expected"; if (message.battleState != null && message.hasOwnProperty("battleState")) if (!$util.isInteger(message.battleState)) return "battleState: integer expected"; @@ -1994,7 +1994,7 @@ $root.treasurehunterx = (function() { message.dir = $root.treasurehunterx.Direction.fromObject(object.dir); } if (object.speed != null) - message.speed = object.speed | 0; + message.speed = Number(object.speed); if (object.battleState != null) message.battleState = object.battleState | 0; if (object.lastMoveGmtMillis != null) @@ -2042,7 +2042,7 @@ $root.treasurehunterx = (function() { if (message.dir != null && message.hasOwnProperty("dir")) object.dir = $root.treasurehunterx.Direction.toObject(message.dir, options); if (message.speed != null && message.hasOwnProperty("speed")) - object.speed = message.speed; + object.speed = options.json && !isFinite(message.speed) ? String(message.speed) : message.speed; if (message.battleState != null && message.hasOwnProperty("battleState")) object.battleState = message.battleState; if (message.lastMoveGmtMillis != null && message.hasOwnProperty("lastMoveGmtMillis")) @@ -2381,446 +2381,6 @@ $root.treasurehunterx = (function() { return PlayerMeta; })(); - treasurehunterx.RoomDownsyncFrame = (function() { - - /** - * Properties of a RoomDownsyncFrame. - * @memberof treasurehunterx - * @interface IRoomDownsyncFrame - * @property {number|null} [id] RoomDownsyncFrame id - * @property {number|null} [refFrameId] RoomDownsyncFrame refFrameId - * @property {Object.|null} [players] RoomDownsyncFrame players - * @property {number|Long|null} [sentAt] RoomDownsyncFrame sentAt - * @property {number|Long|null} [countdownNanos] RoomDownsyncFrame countdownNanos - * @property {Object.|null} [playerMetas] RoomDownsyncFrame playerMetas - */ - - /** - * Constructs a new RoomDownsyncFrame. - * @memberof treasurehunterx - * @classdesc Represents a RoomDownsyncFrame. - * @implements IRoomDownsyncFrame - * @constructor - * @param {treasurehunterx.IRoomDownsyncFrame=} [properties] Properties to set - */ - function RoomDownsyncFrame(properties) { - this.players = {}; - this.playerMetas = {}; - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * RoomDownsyncFrame id. - * @member {number} id - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.id = 0; - - /** - * RoomDownsyncFrame refFrameId. - * @member {number} refFrameId - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.refFrameId = 0; - - /** - * RoomDownsyncFrame players. - * @member {Object.} players - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.players = $util.emptyObject; - - /** - * RoomDownsyncFrame sentAt. - * @member {number|Long} sentAt - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.sentAt = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - - /** - * RoomDownsyncFrame countdownNanos. - * @member {number|Long} countdownNanos - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.countdownNanos = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - - /** - * RoomDownsyncFrame playerMetas. - * @member {Object.} playerMetas - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.playerMetas = $util.emptyObject; - - /** - * Creates a new RoomDownsyncFrame instance using the specified properties. - * @function create - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {treasurehunterx.IRoomDownsyncFrame=} [properties] Properties to set - * @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame instance - */ - RoomDownsyncFrame.create = function create(properties) { - return new RoomDownsyncFrame(properties); - }; - - /** - * Encodes the specified RoomDownsyncFrame message. Does not implicitly {@link treasurehunterx.RoomDownsyncFrame.verify|verify} messages. - * @function encode - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - RoomDownsyncFrame.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id); - if (message.refFrameId != null && Object.hasOwnProperty.call(message, "refFrameId")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.refFrameId); - if (message.players != null && Object.hasOwnProperty.call(message, "players")) - for (var keys = Object.keys(message.players), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 3, wireType 2 =*/26).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); - $root.treasurehunterx.Player.encode(message.players[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); - } - if (message.sentAt != null && Object.hasOwnProperty.call(message, "sentAt")) - writer.uint32(/* id 4, wireType 0 =*/32).int64(message.sentAt); - if (message.countdownNanos != null && Object.hasOwnProperty.call(message, "countdownNanos")) - writer.uint32(/* id 5, wireType 0 =*/40).int64(message.countdownNanos); - if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas")) - for (var keys = Object.keys(message.playerMetas), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 6, wireType 2 =*/50).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); - $root.treasurehunterx.PlayerMeta.encode(message.playerMetas[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); - } - return writer; - }; - - /** - * Encodes the specified RoomDownsyncFrame message, length delimited. Does not implicitly {@link treasurehunterx.RoomDownsyncFrame.verify|verify} messages. - * @function encodeDelimited - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - RoomDownsyncFrame.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a RoomDownsyncFrame message from the specified reader or buffer. - * @function decode - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - RoomDownsyncFrame.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.RoomDownsyncFrame(), key, value; - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.id = reader.int32(); - break; - } - case 2: { - message.refFrameId = reader.int32(); - break; - } - case 3: { - if (message.players === $util.emptyObject) - message.players = {}; - var end2 = reader.uint32() + reader.pos; - key = 0; - value = null; - while (reader.pos < end2) { - var tag2 = reader.uint32(); - switch (tag2 >>> 3) { - case 1: - key = reader.int32(); - break; - case 2: - value = $root.treasurehunterx.Player.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag2 & 7); - break; - } - } - message.players[key] = value; - break; - } - case 4: { - message.sentAt = reader.int64(); - break; - } - case 5: { - message.countdownNanos = reader.int64(); - break; - } - case 6: { - if (message.playerMetas === $util.emptyObject) - message.playerMetas = {}; - var end2 = reader.uint32() + reader.pos; - key = 0; - value = null; - while (reader.pos < end2) { - var tag2 = reader.uint32(); - switch (tag2 >>> 3) { - case 1: - key = reader.int32(); - break; - case 2: - value = $root.treasurehunterx.PlayerMeta.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag2 & 7); - break; - } - } - message.playerMetas[key] = value; - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a RoomDownsyncFrame message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - RoomDownsyncFrame.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a RoomDownsyncFrame message. - * @function verify - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - RoomDownsyncFrame.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isInteger(message.id)) - return "id: integer expected"; - if (message.refFrameId != null && message.hasOwnProperty("refFrameId")) - if (!$util.isInteger(message.refFrameId)) - return "refFrameId: integer expected"; - if (message.players != null && message.hasOwnProperty("players")) { - if (!$util.isObject(message.players)) - return "players: object expected"; - var key = Object.keys(message.players); - for (var i = 0; i < key.length; ++i) { - if (!$util.key32Re.test(key[i])) - return "players: integer key{k:int32} expected"; - { - var error = $root.treasurehunterx.Player.verify(message.players[key[i]]); - if (error) - return "players." + error; - } - } - } - if (message.sentAt != null && message.hasOwnProperty("sentAt")) - if (!$util.isInteger(message.sentAt) && !(message.sentAt && $util.isInteger(message.sentAt.low) && $util.isInteger(message.sentAt.high))) - return "sentAt: integer|Long expected"; - if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos")) - if (!$util.isInteger(message.countdownNanos) && !(message.countdownNanos && $util.isInteger(message.countdownNanos.low) && $util.isInteger(message.countdownNanos.high))) - return "countdownNanos: integer|Long expected"; - if (message.playerMetas != null && message.hasOwnProperty("playerMetas")) { - if (!$util.isObject(message.playerMetas)) - return "playerMetas: object expected"; - var key = Object.keys(message.playerMetas); - for (var i = 0; i < key.length; ++i) { - if (!$util.key32Re.test(key[i])) - return "playerMetas: integer key{k:int32} expected"; - { - var error = $root.treasurehunterx.PlayerMeta.verify(message.playerMetas[key[i]]); - if (error) - return "playerMetas." + error; - } - } - } - return null; - }; - - /** - * Creates a RoomDownsyncFrame message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {Object.} object Plain object - * @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame - */ - RoomDownsyncFrame.fromObject = function fromObject(object) { - if (object instanceof $root.treasurehunterx.RoomDownsyncFrame) - return object; - var message = new $root.treasurehunterx.RoomDownsyncFrame(); - if (object.id != null) - message.id = object.id | 0; - if (object.refFrameId != null) - message.refFrameId = object.refFrameId | 0; - if (object.players) { - if (typeof object.players !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected"); - message.players = {}; - for (var keys = Object.keys(object.players), i = 0; i < keys.length; ++i) { - if (typeof object.players[keys[i]] !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected"); - message.players[keys[i]] = $root.treasurehunterx.Player.fromObject(object.players[keys[i]]); - } - } - if (object.sentAt != null) - if ($util.Long) - (message.sentAt = $util.Long.fromValue(object.sentAt)).unsigned = false; - else if (typeof object.sentAt === "string") - message.sentAt = parseInt(object.sentAt, 10); - else if (typeof object.sentAt === "number") - message.sentAt = object.sentAt; - else if (typeof object.sentAt === "object") - message.sentAt = new $util.LongBits(object.sentAt.low >>> 0, object.sentAt.high >>> 0).toNumber(); - if (object.countdownNanos != null) - if ($util.Long) - (message.countdownNanos = $util.Long.fromValue(object.countdownNanos)).unsigned = false; - else if (typeof object.countdownNanos === "string") - message.countdownNanos = parseInt(object.countdownNanos, 10); - else if (typeof object.countdownNanos === "number") - message.countdownNanos = object.countdownNanos; - else if (typeof object.countdownNanos === "object") - message.countdownNanos = new $util.LongBits(object.countdownNanos.low >>> 0, object.countdownNanos.high >>> 0).toNumber(); - if (object.playerMetas) { - if (typeof object.playerMetas !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected"); - message.playerMetas = {}; - for (var keys = Object.keys(object.playerMetas), i = 0; i < keys.length; ++i) { - if (typeof object.playerMetas[keys[i]] !== "object") - throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected"); - message.playerMetas[keys[i]] = $root.treasurehunterx.PlayerMeta.fromObject(object.playerMetas[keys[i]]); - } - } - return message; - }; - - /** - * Creates a plain object from a RoomDownsyncFrame message. Also converts values to other types if specified. - * @function toObject - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - RoomDownsyncFrame.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.objects || options.defaults) { - object.players = {}; - object.playerMetas = {}; - } - if (options.defaults) { - object.id = 0; - object.refFrameId = 0; - if ($util.Long) { - var long = new $util.Long(0, 0, false); - object.sentAt = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.sentAt = options.longs === String ? "0" : 0; - if ($util.Long) { - var long = new $util.Long(0, 0, false); - object.countdownNanos = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.countdownNanos = options.longs === String ? "0" : 0; - } - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - if (message.refFrameId != null && message.hasOwnProperty("refFrameId")) - object.refFrameId = message.refFrameId; - var keys2; - if (message.players && (keys2 = Object.keys(message.players)).length) { - object.players = {}; - for (var j = 0; j < keys2.length; ++j) - object.players[keys2[j]] = $root.treasurehunterx.Player.toObject(message.players[keys2[j]], options); - } - if (message.sentAt != null && message.hasOwnProperty("sentAt")) - if (typeof message.sentAt === "number") - object.sentAt = options.longs === String ? String(message.sentAt) : message.sentAt; - else - object.sentAt = options.longs === String ? $util.Long.prototype.toString.call(message.sentAt) : options.longs === Number ? new $util.LongBits(message.sentAt.low >>> 0, message.sentAt.high >>> 0).toNumber() : message.sentAt; - if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos")) - if (typeof message.countdownNanos === "number") - object.countdownNanos = options.longs === String ? String(message.countdownNanos) : message.countdownNanos; - else - object.countdownNanos = options.longs === String ? $util.Long.prototype.toString.call(message.countdownNanos) : options.longs === Number ? new $util.LongBits(message.countdownNanos.low >>> 0, message.countdownNanos.high >>> 0).toNumber() : message.countdownNanos; - if (message.playerMetas && (keys2 = Object.keys(message.playerMetas)).length) { - object.playerMetas = {}; - for (var j = 0; j < keys2.length; ++j) - object.playerMetas[keys2[j]] = $root.treasurehunterx.PlayerMeta.toObject(message.playerMetas[keys2[j]], options); - } - return object; - }; - - /** - * Converts this RoomDownsyncFrame to JSON. - * @function toJSON - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - * @returns {Object.} JSON object - */ - RoomDownsyncFrame.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for RoomDownsyncFrame - * @function getTypeUrl - * @memberof treasurehunterx.RoomDownsyncFrame - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - RoomDownsyncFrame.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/treasurehunterx.RoomDownsyncFrame"; - }; - - return RoomDownsyncFrame; - })(); - treasurehunterx.InputFrameUpsync = (function() { /** @@ -3564,6 +3124,446 @@ $root.treasurehunterx = (function() { return HeartbeatUpsync; })(); + treasurehunterx.RoomDownsyncFrame = (function() { + + /** + * Properties of a RoomDownsyncFrame. + * @memberof treasurehunterx + * @interface IRoomDownsyncFrame + * @property {number|null} [id] RoomDownsyncFrame id + * @property {number|null} [refFrameId] RoomDownsyncFrame refFrameId + * @property {Object.|null} [players] RoomDownsyncFrame players + * @property {number|Long|null} [sentAt] RoomDownsyncFrame sentAt + * @property {number|Long|null} [countdownNanos] RoomDownsyncFrame countdownNanos + * @property {Object.|null} [playerMetas] RoomDownsyncFrame playerMetas + */ + + /** + * Constructs a new RoomDownsyncFrame. + * @memberof treasurehunterx + * @classdesc Represents a RoomDownsyncFrame. + * @implements IRoomDownsyncFrame + * @constructor + * @param {treasurehunterx.IRoomDownsyncFrame=} [properties] Properties to set + */ + function RoomDownsyncFrame(properties) { + this.players = {}; + this.playerMetas = {}; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * RoomDownsyncFrame id. + * @member {number} id + * @memberof treasurehunterx.RoomDownsyncFrame + * @instance + */ + RoomDownsyncFrame.prototype.id = 0; + + /** + * RoomDownsyncFrame refFrameId. + * @member {number} refFrameId + * @memberof treasurehunterx.RoomDownsyncFrame + * @instance + */ + RoomDownsyncFrame.prototype.refFrameId = 0; + + /** + * RoomDownsyncFrame players. + * @member {Object.} players + * @memberof treasurehunterx.RoomDownsyncFrame + * @instance + */ + RoomDownsyncFrame.prototype.players = $util.emptyObject; + + /** + * RoomDownsyncFrame sentAt. + * @member {number|Long} sentAt + * @memberof treasurehunterx.RoomDownsyncFrame + * @instance + */ + RoomDownsyncFrame.prototype.sentAt = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * RoomDownsyncFrame countdownNanos. + * @member {number|Long} countdownNanos + * @memberof treasurehunterx.RoomDownsyncFrame + * @instance + */ + RoomDownsyncFrame.prototype.countdownNanos = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * RoomDownsyncFrame playerMetas. + * @member {Object.} playerMetas + * @memberof treasurehunterx.RoomDownsyncFrame + * @instance + */ + RoomDownsyncFrame.prototype.playerMetas = $util.emptyObject; + + /** + * Creates a new RoomDownsyncFrame instance using the specified properties. + * @function create + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {treasurehunterx.IRoomDownsyncFrame=} [properties] Properties to set + * @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame instance + */ + RoomDownsyncFrame.create = function create(properties) { + return new RoomDownsyncFrame(properties); + }; + + /** + * Encodes the specified RoomDownsyncFrame message. Does not implicitly {@link treasurehunterx.RoomDownsyncFrame.verify|verify} messages. + * @function encode + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + RoomDownsyncFrame.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.id != null && Object.hasOwnProperty.call(message, "id")) + writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id); + if (message.refFrameId != null && Object.hasOwnProperty.call(message, "refFrameId")) + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.refFrameId); + if (message.players != null && Object.hasOwnProperty.call(message, "players")) + for (var keys = Object.keys(message.players), i = 0; i < keys.length; ++i) { + writer.uint32(/* id 3, wireType 2 =*/26).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); + $root.treasurehunterx.Player.encode(message.players[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); + } + if (message.sentAt != null && Object.hasOwnProperty.call(message, "sentAt")) + writer.uint32(/* id 4, wireType 0 =*/32).int64(message.sentAt); + if (message.countdownNanos != null && Object.hasOwnProperty.call(message, "countdownNanos")) + writer.uint32(/* id 5, wireType 0 =*/40).int64(message.countdownNanos); + if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas")) + for (var keys = Object.keys(message.playerMetas), i = 0; i < keys.length; ++i) { + writer.uint32(/* id 6, wireType 2 =*/50).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); + $root.treasurehunterx.PlayerMeta.encode(message.playerMetas[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); + } + return writer; + }; + + /** + * Encodes the specified RoomDownsyncFrame message, length delimited. Does not implicitly {@link treasurehunterx.RoomDownsyncFrame.verify|verify} messages. + * @function encodeDelimited + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + RoomDownsyncFrame.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a RoomDownsyncFrame message from the specified reader or buffer. + * @function decode + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + RoomDownsyncFrame.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.RoomDownsyncFrame(), key, value; + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.id = reader.int32(); + break; + } + case 2: { + message.refFrameId = reader.int32(); + break; + } + case 3: { + if (message.players === $util.emptyObject) + message.players = {}; + var end2 = reader.uint32() + reader.pos; + key = 0; + value = null; + while (reader.pos < end2) { + var tag2 = reader.uint32(); + switch (tag2 >>> 3) { + case 1: + key = reader.int32(); + break; + case 2: + value = $root.treasurehunterx.Player.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag2 & 7); + break; + } + } + message.players[key] = value; + break; + } + case 4: { + message.sentAt = reader.int64(); + break; + } + case 5: { + message.countdownNanos = reader.int64(); + break; + } + case 6: { + if (message.playerMetas === $util.emptyObject) + message.playerMetas = {}; + var end2 = reader.uint32() + reader.pos; + key = 0; + value = null; + while (reader.pos < end2) { + var tag2 = reader.uint32(); + switch (tag2 >>> 3) { + case 1: + key = reader.int32(); + break; + case 2: + value = $root.treasurehunterx.PlayerMeta.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag2 & 7); + break; + } + } + message.playerMetas[key] = value; + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a RoomDownsyncFrame message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + RoomDownsyncFrame.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a RoomDownsyncFrame message. + * @function verify + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + RoomDownsyncFrame.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.id != null && message.hasOwnProperty("id")) + if (!$util.isInteger(message.id)) + return "id: integer expected"; + if (message.refFrameId != null && message.hasOwnProperty("refFrameId")) + if (!$util.isInteger(message.refFrameId)) + return "refFrameId: integer expected"; + if (message.players != null && message.hasOwnProperty("players")) { + if (!$util.isObject(message.players)) + return "players: object expected"; + var key = Object.keys(message.players); + for (var i = 0; i < key.length; ++i) { + if (!$util.key32Re.test(key[i])) + return "players: integer key{k:int32} expected"; + { + var error = $root.treasurehunterx.Player.verify(message.players[key[i]]); + if (error) + return "players." + error; + } + } + } + if (message.sentAt != null && message.hasOwnProperty("sentAt")) + if (!$util.isInteger(message.sentAt) && !(message.sentAt && $util.isInteger(message.sentAt.low) && $util.isInteger(message.sentAt.high))) + return "sentAt: integer|Long expected"; + if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos")) + if (!$util.isInteger(message.countdownNanos) && !(message.countdownNanos && $util.isInteger(message.countdownNanos.low) && $util.isInteger(message.countdownNanos.high))) + return "countdownNanos: integer|Long expected"; + if (message.playerMetas != null && message.hasOwnProperty("playerMetas")) { + if (!$util.isObject(message.playerMetas)) + return "playerMetas: object expected"; + var key = Object.keys(message.playerMetas); + for (var i = 0; i < key.length; ++i) { + if (!$util.key32Re.test(key[i])) + return "playerMetas: integer key{k:int32} expected"; + { + var error = $root.treasurehunterx.PlayerMeta.verify(message.playerMetas[key[i]]); + if (error) + return "playerMetas." + error; + } + } + } + return null; + }; + + /** + * Creates a RoomDownsyncFrame message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {Object.} object Plain object + * @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame + */ + RoomDownsyncFrame.fromObject = function fromObject(object) { + if (object instanceof $root.treasurehunterx.RoomDownsyncFrame) + return object; + var message = new $root.treasurehunterx.RoomDownsyncFrame(); + if (object.id != null) + message.id = object.id | 0; + if (object.refFrameId != null) + message.refFrameId = object.refFrameId | 0; + if (object.players) { + if (typeof object.players !== "object") + throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected"); + message.players = {}; + for (var keys = Object.keys(object.players), i = 0; i < keys.length; ++i) { + if (typeof object.players[keys[i]] !== "object") + throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected"); + message.players[keys[i]] = $root.treasurehunterx.Player.fromObject(object.players[keys[i]]); + } + } + if (object.sentAt != null) + if ($util.Long) + (message.sentAt = $util.Long.fromValue(object.sentAt)).unsigned = false; + else if (typeof object.sentAt === "string") + message.sentAt = parseInt(object.sentAt, 10); + else if (typeof object.sentAt === "number") + message.sentAt = object.sentAt; + else if (typeof object.sentAt === "object") + message.sentAt = new $util.LongBits(object.sentAt.low >>> 0, object.sentAt.high >>> 0).toNumber(); + if (object.countdownNanos != null) + if ($util.Long) + (message.countdownNanos = $util.Long.fromValue(object.countdownNanos)).unsigned = false; + else if (typeof object.countdownNanos === "string") + message.countdownNanos = parseInt(object.countdownNanos, 10); + else if (typeof object.countdownNanos === "number") + message.countdownNanos = object.countdownNanos; + else if (typeof object.countdownNanos === "object") + message.countdownNanos = new $util.LongBits(object.countdownNanos.low >>> 0, object.countdownNanos.high >>> 0).toNumber(); + if (object.playerMetas) { + if (typeof object.playerMetas !== "object") + throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected"); + message.playerMetas = {}; + for (var keys = Object.keys(object.playerMetas), i = 0; i < keys.length; ++i) { + if (typeof object.playerMetas[keys[i]] !== "object") + throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected"); + message.playerMetas[keys[i]] = $root.treasurehunterx.PlayerMeta.fromObject(object.playerMetas[keys[i]]); + } + } + return message; + }; + + /** + * Creates a plain object from a RoomDownsyncFrame message. Also converts values to other types if specified. + * @function toObject + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + RoomDownsyncFrame.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.objects || options.defaults) { + object.players = {}; + object.playerMetas = {}; + } + if (options.defaults) { + object.id = 0; + object.refFrameId = 0; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.sentAt = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.sentAt = options.longs === String ? "0" : 0; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.countdownNanos = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.countdownNanos = options.longs === String ? "0" : 0; + } + if (message.id != null && message.hasOwnProperty("id")) + object.id = message.id; + if (message.refFrameId != null && message.hasOwnProperty("refFrameId")) + object.refFrameId = message.refFrameId; + var keys2; + if (message.players && (keys2 = Object.keys(message.players)).length) { + object.players = {}; + for (var j = 0; j < keys2.length; ++j) + object.players[keys2[j]] = $root.treasurehunterx.Player.toObject(message.players[keys2[j]], options); + } + if (message.sentAt != null && message.hasOwnProperty("sentAt")) + if (typeof message.sentAt === "number") + object.sentAt = options.longs === String ? String(message.sentAt) : message.sentAt; + else + object.sentAt = options.longs === String ? $util.Long.prototype.toString.call(message.sentAt) : options.longs === Number ? new $util.LongBits(message.sentAt.low >>> 0, message.sentAt.high >>> 0).toNumber() : message.sentAt; + if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos")) + if (typeof message.countdownNanos === "number") + object.countdownNanos = options.longs === String ? String(message.countdownNanos) : message.countdownNanos; + else + object.countdownNanos = options.longs === String ? $util.Long.prototype.toString.call(message.countdownNanos) : options.longs === Number ? new $util.LongBits(message.countdownNanos.low >>> 0, message.countdownNanos.high >>> 0).toNumber() : message.countdownNanos; + if (message.playerMetas && (keys2 = Object.keys(message.playerMetas)).length) { + object.playerMetas = {}; + for (var j = 0; j < keys2.length; ++j) + object.playerMetas[keys2[j]] = $root.treasurehunterx.PlayerMeta.toObject(message.playerMetas[keys2[j]], options); + } + return object; + }; + + /** + * Converts this RoomDownsyncFrame to JSON. + * @function toJSON + * @memberof treasurehunterx.RoomDownsyncFrame + * @instance + * @returns {Object.} JSON object + */ + RoomDownsyncFrame.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for RoomDownsyncFrame + * @function getTypeUrl + * @memberof treasurehunterx.RoomDownsyncFrame + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + RoomDownsyncFrame.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/treasurehunterx.RoomDownsyncFrame"; + }; + + return RoomDownsyncFrame; + })(); + treasurehunterx.WsReq = (function() { /** From 527cc9424252baf81db030f4ad9f8d7ddbc10c6f Mon Sep 17 00:00:00 2001 From: Wing Date: Fri, 30 Sep 2022 23:32:26 +0800 Subject: [PATCH 03/13] Minor fix on markdown file. --- ConcerningEdgeCases.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ConcerningEdgeCases.md b/ConcerningEdgeCases.md index e1a849f..9534f89 100644 --- a/ConcerningEdgeCases.md +++ b/ConcerningEdgeCases.md @@ -30,14 +30,14 @@ The following setup is chosen deliberately for some "%4" number coincidence. If "InputDelayFrames" is changed, the impact would be as follows, kindly note that "372%4 == 0". ### pR.InputDelayFrames = 4 -toApplyInputFrameId | renderFrameId ---------------------------|----------------------------------------- - 92 | _EMP_, _EMP_, _EMP_, _EMP_, 372, 373, 374, 375 - 91 | 368, 369, 370, 371 +renderFrameId | toApplyInputFrameId +--------------------------|---------------------------------------------------- +368, 369, 370, 371 | 91 +372, 373, 374, 375 | 92 ### pR.InputDelayFrames = 5 -toApplyInputFrameId | renderFrameId ---------------------------|----------------------------------------- - 92 | _EMP_, _EMP_, _EMP_, _EMP_, 373, 374, 375 - 91 | _EMP_, 369, 370, 371, 372 - 90 | 368 +renderFrameId | toApplyInputFrameId +--------------------------|---------------------------------------------------- +..., ..., ..., 368 | 90 +369, 370, 371, 372 | 91 +373, 374, 375, ... | 92 From a2a8be9068c29c52197317964f85995c7e53a3b0 Mon Sep 17 00:00:00 2001 From: genxium Date: Sat, 1 Oct 2022 15:14:05 +0800 Subject: [PATCH 04/13] Added backend collider initialization codes. --- battle_srv/models/barrier.go | 7 -- battle_srv/models/player.go | 2 - battle_srv/models/room.go | 92 ++++++++++++++----------- frontend/assets/scripts/Map.js | 25 ++++--- frontend/assets/scripts/WsSessionMgr.js | 8 +++ 5 files changed, 73 insertions(+), 61 deletions(-) diff --git a/battle_srv/models/barrier.go b/battle_srv/models/barrier.go index 81b78d5..99c37dc 100644 --- a/battle_srv/models/barrier.go +++ b/battle_srv/models/barrier.go @@ -1,12 +1,5 @@ package models -import ( - "github.com/ByteArena/box2d" -) - type Barrier struct { - X float64 - Y float64 Boundary *Polygon2D - CollidableBody *box2d.B2Body } diff --git a/battle_srv/models/player.go b/battle_srv/models/player.go index 7dbe018..50b9b3f 100644 --- a/battle_srv/models/player.go +++ b/battle_srv/models/player.go @@ -3,7 +3,6 @@ package models import ( "database/sql" "fmt" - "github.com/ByteArena/box2d" sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" ) @@ -54,7 +53,6 @@ type Player struct { UpdatedAt int64 `json:"-" db:"updated_at"` DeletedAt NullInt64 `json:"-" db:"deleted_at"` TutorialStage int `json:"-" db:"tutorial_stage"` - CollidableBody *box2d.B2Body `json:"-"` AckingFrameId int32 `json:"ackingFrameId"` AckingInputFrameId int32 `json:"-"` LastSentInputFrameId int32 `json:"-"` diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 1d57b01..2b0142f 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" "io/ioutil" "math/rand" + "math" "os" "path/filepath" . "server/common" @@ -28,6 +29,7 @@ const ( DOWNSYNC_MSG_ACT_HB_REQ = int32(1) DOWNSYNC_MSG_ACT_INPUT_BATCH = int32(2) DOWNSYNC_MSG_ACT_ROOM_FRAME = int32(3) + DOWNSYNC_MSG_ACT_FORCED_RESYNC = int32(4) ) const ( @@ -315,8 +317,6 @@ func (pR *Room) ChooseStage() error { Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points)) */ pR.Barriers[barrierLocalIdInBattle] = &Barrier{ - X: polygon2D.Anchor.X, - Y: polygon2D.Anchor.Y, Boundary: polygon2D, } @@ -416,7 +416,7 @@ func (pR *Room) StartBattle() { */ continue } - pR.sendSafely(kickoffFrame, playerId) + pR.sendSafely(&kickoffFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, playerId) } } @@ -481,19 +481,12 @@ func (pR *Room) StartBattle() { var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) if 0 < (unconfirmedMask & joinMask) { refRenderFrame := pR.RenderFrameBuffer.GetByFrameId(pR.CurDynamicsRenderFrameId).(*pb.RoomDownsyncFrame) - resp := pb.WsResp { - Ret: int32(Constants.RetCode.Ok), - EchoedMsgId: int32(0), - Act: DOWNSYNC_MSG_ACT_ROOM_FRAME, - InputFrameDownsyncBatch: toSendInputFrames, - Rdf: refRenderFrame, - } - pR.sendSafely(resp, playerId) + pR.sendSafely(refRenderFrame, toSendInputFrames, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId) } else { if 0 >= len(toSendInputFrames) { continue } - pR.sendSafely(toSendInputFrames, playerId) + pR.sendSafely(nil, toSendInputFrames, DOWNSYNC_MSG_ACT_INPUT_BATCH, playerId) atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) if -1 != debugSendingInputFrameId { Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) @@ -644,7 +637,7 @@ func (pR *Room) StopBattleForSettlement() { SentAt: utils.UnixtimeMilli(), CountdownNanos: -1, // TODO: Replace this magic constant! } - pR.sendSafely(assembledFrame, playerId) + pR.sendSafely(&assembledFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, playerId) } // Note that `pR.onBattleStoppedForSettlement` will be called by `battleMainLoop`. } @@ -687,7 +680,7 @@ func (pR *Room) onBattlePrepare(cb BattleStartCbType) { Logger.Info("Sending out frame for RoomBattleState.PREPARE ", zap.Any("battleReadyToStartFrame", battleReadyToStartFrame)) for _, player := range pR.Players { - pR.sendSafely(battleReadyToStartFrame, player.Id) + pR.sendSafely(&battleReadyToStartFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, player.Id) } battlePreparationNanos := int64(6000000000) @@ -950,7 +943,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { By making use of the sequential nature of each ws session, all later "RoomDownsyncFrame"s generated after `pRoom.StartBattle()` will be put behind this `playerAckedFrame`. */ - pR.sendSafely(playerAckedFrame, player.Id) + pR.sendSafely(&playerAckedFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, player.Id) } pPlayer.BattleState = PlayerBattleStateIns.ACTIVE @@ -974,38 +967,19 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { return true } -func (pR *Room) sendSafely(s interface{}, playerId int32) { +func (pR *Room) sendSafely(roomDownsyncFrame *pb.RoomDownsyncFrame, toSendFrames []*pb.InputFrameDownsync, act int32, playerId int32) { defer func() { if r := recover(); r != nil { pR.PlayerSignalToCloseDict[playerId](Constants.RetCode.UnknownError, fmt.Sprintf("%v", r)) } }() - var pResp *pb.WsResp = nil - - switch v := s.(type) { - case pb.WsResp: - resp := s.(pb.WsResp) - pResp = &resp - case pb.RoomDownsyncFrame: - roomDownsyncFrame := s.(pb.RoomDownsyncFrame) - pResp = &pb.WsResp{ - Ret: int32(Constants.RetCode.Ok), - EchoedMsgId: int32(0), - Act: DOWNSYNC_MSG_ACT_ROOM_FRAME, - Rdf: &roomDownsyncFrame, - } - case []*pb.InputFrameDownsync: - toSendFrames := s.([]*pb.InputFrameDownsync) - pResp = &pb.WsResp{ - Ret: int32(Constants.RetCode.Ok), - EchoedMsgId: int32(0), - Act: DOWNSYNC_MSG_ACT_INPUT_BATCH, - InputFrameDownsyncBatch: toSendFrames, - } - default: - panic(fmt.Sprintf("Unknown downsync message type, roomId=%v, playerId=%v, roomState=%v, v=%v", pR.Id, playerId, v)) - } + pResp := &pb.WsResp{ + Ret: int32(Constants.RetCode.Ok), + Act: act, + Rdf: roomDownsyncFrame, + InputFrameDownsyncBatch: toSendFrames, + } theBytes, marshalErr := proto.Marshal(pResp) if nil != marshalErr { @@ -1151,8 +1125,44 @@ func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool { } func (pR *Room) refreshColliders() { + // Kindly note that by now, we've already got all the shapes in the tmx file into "pR.(Players | Barriers)" from "ParseTmxLayersAndGroups" + space := resolv.NewSpace(int(pR.StageDiscreteW), int(pR.StageDiscreteH), int(pR.StageTileW), int(pR.StageTileH)) // allocate a new collision space everytime after a battle is settled for _, player := range pR.Players { + playerCollider := resolv.NewObject(player.X, player.Y, 12, 12) // Radius=12 is hardcoded + playerColliderShape := resolv.NewCircle(player.X, player.Y, 12) + playerCollider.SetShape(playerColliderShape) + space.Add(playerCollider) + // Keep track of the collider in "pR.CollisionSysMap" joinIndex := player.JoinIndex pR.PlayersArr[joinIndex-1] = player + collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex + pR.CollisionSysMap[collisionPlayerIndex] = playerCollider } + + for _, barrier := range pR.Barriers { + var w float64 = 0 + var h float64 = 0 + for i, pi := range barrier.Boundary.Points { + for j, pj := range barrier.Boundary.Points { + if i == j { + continue + } + if math.Abs(pj.X - pi.X) > w { + w = math.Abs(pj.X - pi.X) + } + if math.Abs(pj.Y - pi.Y) > h { + h = math.Abs(pj.Y - pi.Y) + } + } + } + + barrierColliderShape := resolv.NewConvexPolygon() + for _, p := range barrier.Boundary.Points { + barrierColliderShape.AddPoints(p.X+barrier.Boundary.Anchor.X, p.Y+barrier.Boundary.Anchor.Y) + } + + barrierCollider := resolv.NewObject(barrier.Boundary.Anchor.X, barrier.Boundary.Anchor.Y, w, h, "Barrier") + barrierCollider.SetShape(barrierColliderShape) + space.Add(barrierCollider) + } } diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 1782942..87bf748 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -222,11 +222,14 @@ cc.Class({ shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, lastUpsyncInputFrameId, currInputFrameId) { /* - For a 2-player-battle, this "shouldUpsyncForEarlyAllConfirmedOnServer" can be omitted, however for more players in a same battle, to avoid a "long time non-moving player" jamming the downsync of other moving players, we should use this flag. + For a 2-player-battle, this "shouldUpsyncForEarlyAllConfirmedOnBackend" can be omitted, however for more players in a same battle, to avoid a "long time non-moving player" jamming the downsync of other moving players, we should use this flag. + + When backend implements the "force confirmation" feature, we can have "false == shouldUpsyncForEarlyAllConfirmedOnBackend" all the time as well! */ if (null == currSelfInput) return false; - const shouldUpsyncForEarlyAllConfirmedOnServer = (currInputFrameId - lastUpsyncInputFrameId >= this.inputFrameUpsyncDelayTolerance); - return shouldUpsyncForEarlyAllConfirmedOnServer || (prevSelfInput != currSelfInput); + + const shouldUpsyncForEarlyAllConfirmedOnBackend = (currInputFrameId - lastUpsyncInputFrameId >= this.inputFrameUpsyncDelayTolerance); + return shouldUpsyncForEarlyAllConfirmedOnBackend || (prevSelfInput != currSelfInput); }, sendInputFrameUpsyncBatch(inputFrameId) { @@ -562,14 +565,11 @@ cc.Class({ self.onBattleReadyToStart(rdf.playerMetas, false); return; case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START: - self.onBattleStarted(rdf); + self.onBattleStartedOrResynced(rdf); return; case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.PLAYER_READDED_AND_ACKED: - // [WARNING] The "frameId" from server could be quite fast-forwarding, don't assign it in other cases. - self.renderFrameId = frameId; - self.lastAllConfirmedRenderFrameId = frameId; self.onBattleReadyToStart(rdf.playerMetas, true); - self.onBattleStarted(rdf); + self.onBattleStartedOrResynced(rdf); return; } @@ -689,10 +689,14 @@ cc.Class({ this._inputControlEnabled = false; }, - onBattleStarted(rdf) { + onBattleStartedOrResynced(rdf) { // This function is also applicable to "re-joining". - console.log('On battle started!'); + console.log('On battle started or resynced! renderFrameId=', rdf.id); const self = window.mapIns; + self.renderFrameId = rdf.id; + self.lastAllConfirmedRenderFrameId = rdf.id; + self.chaserRenderFrameId = rdf.id; + const players = rdf.players; const playerMetas = rdf.playerMetas; self._initPlayerRichInfoDict(players, playerMetas); @@ -717,7 +721,6 @@ cc.Class({ self.countdownToBeginGameNode.parent.removeChild(self.countdownToBeginGameNode); } self.transitToState(ALL_MAP_STATES.VISUAL); - self.chaserRenderFrameId = rdf.id; self.applyRoomDownsyncFrameDynamics(rdf); self._dumpToRenderCache(rdf); self.battleState = ALL_BATTLE_STATES.IN_BATTLE; // Starts the increment of "self.renderFrameId" in "self.update(dt)" diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index 6d81061..7069cf8 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -5,6 +5,7 @@ window.UPSYNC_MSG_ACT_PLAYER_COLLIDER_ACK = 3; window.DOWNSYNC_MSG_ACT_HB_REQ = 1; window.DOWNSYNC_MSG_ACT_INPUT_BATCH = 2; window.DOWNSYNC_MSG_ACT_ROOM_FRAME = 3; +window.DOWNSYNC_MSG_ACT_FORCED_RESYNC = 4; window.sendSafely = function(msgStr) { /** @@ -163,6 +164,13 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); } break; + case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC: + if (window.handleInputFrameDownsyncBatch && window.handleRoomDownsyncFrame) { + // The following order of execution is important, because "handleInputFrameDownsyncBatch" is only available when state is IN_BATTLE + window.handleRoomDownsyncFrame(resp.rdf); + window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); + } + break; default: break; } From cd835391978a9317569b2d34aa375bec8053857b Mon Sep 17 00:00:00 2001 From: genxium Date: Sat, 1 Oct 2022 17:26:37 +0800 Subject: [PATCH 05/13] Fixed some trivial runtime errors. --- battle_srv/models/room.go | 26 ++++++------ battle_srv/models/tiled_map.go | 55 +++++++------------------ frontend/assets/scenes/login.fire | 2 +- frontend/assets/scripts/Map.js | 1 + frontend/assets/scripts/WsSessionMgr.js | 1 + 5 files changed, 31 insertions(+), 54 deletions(-) diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 2b0142f..2b65d7f 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -348,7 +348,7 @@ func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 { func (pR *Room) AllPlayerInputsBufferString() string { s := make([]string, 0) - s = append(s, fmt.Sprintf("{lastAllConfirmedInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v}", pR.LastAllConfirmedInputFrameId, pR.LastAllConfirmedInputFrameIdWithChange)) + s = append(s, fmt.Sprintf("\n{stInputFrameId: %v, edInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v, lastAllConfirmedInputFrameId: %v}", pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId)) for playerId, player := range pR.Players { s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId)) } @@ -456,7 +456,7 @@ func (pR *Room) StartBattle() { toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt) candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1 if candidateToSendInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { - Logger.Warn("LastSentInputFrameId already popped:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("lastSentInputFrameId", candidateToSendInputFrameId-1), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) + Logger.Warn(fmt.Sprintf("LastSentInputFrameId already popped: roomId=%v, playerId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, candidateToSendInputFrameId-1, player.AckingInputFrameId, pR.AllPlayerInputsBufferString())) candidateToSendInputFrameId = pR.AllPlayerInputsBuffer.StFrameId } @@ -534,7 +534,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { ackingInputFrameId := pReq.AckingInputFrameId if _, existent := pR.Players[playerId]; !existent { - Logger.Warn("upcmd player doesn't exist:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId)) + Logger.Warn(fmt.Sprintf("upcmd player doesn't exist: roomId=%v, playerId=%v", pR.Id, playerId)) return } @@ -549,7 +549,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { for _, inputFrameUpsync := range inputFrameUpsyncBatch { clientInputFrameId := inputFrameUpsync.InputFrameId if clientInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { - Logger.Warn("Obsolete inputFrameUpsync:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + Logger.Warn(fmt.Sprintf("Obsolete inputFrameUpsync: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString())) return } @@ -557,36 +557,36 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { encodedInput := pR.EncodeUpsyncCmd(inputFrameUpsync) if clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId { - Logger.Warn("inputFrame too advanced! is the player cheating?", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + Logger.Warn(fmt.Sprintf("inputFrame too advanced! is the player cheating?: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString())) return } tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId) if nil == tmp2 { // This shouldn't happen due to the previous 2 checks - Logger.Warn("Mysterious error getting an input frame:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + Logger.Warn(fmt.Sprintf("Mysterious error getting an input frame: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString())) return } inputFrameDownsync := tmp2.(*pb.InputFrameDownsync) oldConfirmedList := atomic.LoadUint64(&(inputFrameDownsync.ConfirmedList)) if (oldConfirmedList & joinMask) > 0 { - Logger.Warn("Cmd already confirmed but getting set attempt, omitting this upsync cmd:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + Logger.Warn(fmt.Sprintf("Cmd already confirmed but getting set attempt, omitting this upsync cmd: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString())) return } // In Golang 1.12, there's no "compare-and-swap primitive" on a custom struct (or it's pointer, unless it's an unsafe pointer https://pkg.go.dev/sync/atomic@go1.12#CompareAndSwapPointer). Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. if swapped := atomic.CompareAndSwapUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], uint64(0), encodedInput); !swapped { - Logger.Warn("Failed input CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId)) + Logger.Warn(fmt.Sprintf("Failed input CAS: roomId=%v, playerId=%v, clientInputFrameId=%v", pR.Id, playerId, clientInputFrameId)) return } newConfirmedList := (oldConfirmedList | joinMask) if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped { // [WARNING] Upon this error, the actual input has already been updated, which is an expected result if it caused by the force confirmation from "battleMainLoop". - Logger.Warn("Failed confirm CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId)) + Logger.Warn(fmt.Sprintf("Failed confirm CAS: roomId=%v, playerId=%v, clientInputFrameId=%v", pR.Id, playerId, clientInputFrameId)) return } - totPlayerCnt := uint32(len(pR.Players)) + totPlayerCnt := uint32(pR.Capacity) allConfirmedMask := uint64((1 << totPlayerCnt) - 1) if allConfirmedMask == newConfirmedList { pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, playerId) @@ -598,7 +598,7 @@ func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFra clientInputFrameId := inputFrameDownsync.InputFrameId if false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), clientInputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here! - Logger.Info("Key inputFrame change", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", clientInputFrameId), zap.Any("lastInputFrameId", pR.LastAllConfirmedInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()), zap.Any("newInputList", inputFrameDownsync.InputList), zap.Any("lastInputList", pR.LastAllConfirmedInputList)) + Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, clientInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString())) } atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! for i, v := range inputFrameDownsync.InputList { @@ -606,7 +606,7 @@ func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFra pR.LastAllConfirmedInputList[i] = v } if pR.inputFrameIdDebuggable(clientInputFrameId) { - Logger.Info("inputFrame lifecycle#2[allconfirmed]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) + Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[allconfirmed]: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.LastAllConfirmedInputFrameId, pR.AllPlayerInputsBufferString())) } } @@ -1074,7 +1074,7 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende if nil == tmp { panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString())) } - delayedInputFrame := tmp.(pb.InputFrameDownsync) + delayedInputFrame := tmp.(*pb.InputFrameDownsync) if swapped := atomic.CompareAndSwapUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask, allConfirmedMask); !swapped { panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString())) } diff --git a/battle_srv/models/tiled_map.go b/battle_srv/models/tiled_map.go index aeb5f00..a483211 100644 --- a/battle_srv/models/tiled_map.go +++ b/battle_srv/models/tiled_map.go @@ -6,8 +6,6 @@ import ( "encoding/base64" "encoding/xml" "errors" - "fmt" - "github.com/ByteArena/box2d" "go.uber.org/zap" "io/ioutil" "math" @@ -181,17 +179,12 @@ type Polygon2DList []*Polygon2D type StrToVec2DListMap map[string]*Vec2DList // Note that it's deliberately NOT using "map[string]Vec2DList", for the easy of passing return value to "models/room.go". type StrToPolygon2DListMap map[string]*Polygon2DList // Note that it's deliberately NOT using "map[string]Polygon2DList", for the easy of passing return value to "models/room.go". -func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) { +func tmxPolylineToPolygon2D(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) { if nil == targetPolyline { return nil, nil } singleValueArray := strings.Split(targetPolyline.Points, " ") - pointsCount := len(singleValueArray) - - if pointsCount >= box2d.B2_maxPolygonVertices { - return nil, errors.New(fmt.Sprintf("During `TmxPolylineToPolygon2DInB2World`, you have a polygon with pointsCount == %v, exceeding or equal to box2d.B2_maxPolygonVertices == %v, of polyines [%v]", pointsCount, box2d.B2_maxPolygonVertices, singleValueArray)) - } theUntransformedAnchor := &Vec2D{ X: singleObjInTmxFile.X, @@ -217,7 +210,6 @@ func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *Tmx } } - // Transform to B2World space coordinate. tmp := &Vec2D{ X: thePolygon2DFromPolyline.Points[k].X, Y: thePolygon2DFromPolyline.Points[k].Y, @@ -230,7 +222,7 @@ func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *Tmx return thePolygon2DFromPolyline, nil } -func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInTsxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline, pTsxIns *Tsx) (*Polygon2D, error) { +func tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns *TmxMap, singleObjInTsxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline, pTsxIns *Tsx) (*Polygon2D, error) { if nil == targetPolyline { return nil, nil } @@ -241,10 +233,6 @@ func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInT singleValueArray := strings.Split(targetPolyline.Points, " ") pointsCount := len(singleValueArray) - if pointsCount >= box2d.B2_maxPolygonVertices { - return nil, errors.New(fmt.Sprintf("During `TsxPolylineToOffsetsWrtTileCenterInB2World`, you have a polygon with pointsCount == %v, exceeding or equal to box2d.B2_maxPolygonVertices == %v", pointsCount, box2d.B2_maxPolygonVertices)) - } - thePolygon2DFromPolyline := &Polygon2D{ Anchor: nil, Points: make([]*Vec2D, pointsCount), @@ -253,7 +241,7 @@ func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInT } /* - [WARNING] In this case, the "Treasure"s and "GuardTower"s are put into Tmx file as "ImageObject"s, of each the "ProportionalAnchor" is (0.5, 0). Therefore we calculate that "thePolygon2DFromPolyline.Points" are "offsets(in B2World) w.r.t. the BottomCenter". See https://shimo.im/docs/SmLJJhXm2C8XMzZT for details. + [WARNING] In this case, the "Treasure"s and "GuardTower"s are put into Tmx file as "ImageObject"s, of each the "ProportionalAnchor" is (0.5, 0). Therefore the "thePolygon2DFromPolyline.Points" are "offsets w.r.t. the BottomCenter". See https://shimo.im/docs/SmLJJhXm2C8XMzZT for details. */ for k, value := range singleValueArray { @@ -271,14 +259,12 @@ func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInT thePolygon2DFromPolyline.Points[k].Y = float64(pTsxIns.TileHeight) - (coordinateValue + offsetFromTopLeftInTileLocalCoordY) } } - - // No need to transform for B2World space coordinate because the marks in a Tsx file is already rectilinear. } return thePolygon2DFromPolyline, nil } -func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, firstGid int, gidBoundariesMapInB2World map[int]StrToPolygon2DListMap) error { +func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, firstGid int, gidBoundariesMap map[int]StrToPolygon2DListMap) error { pTsxIns := &Tsx{} err := xml.Unmarshal(byteArrOfTsxFile, pTsxIns) if nil != err { @@ -312,7 +298,7 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f ``` , we currently REQUIRE that "`an object of a tile` with ONE OR MORE polylines must come with a single corresponding '', and viceversa". - Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for how we theoretically fit a "Polyline in Tsx" into a "Polygon2D" and then into the corresponding "B2BodyDef & B2Body in the `world of colliding bodies`". + Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for how we theoretically fit a "Polyline in Tsx" into a "Polygon2D". */ theObjGroup := tile.ObjectGroup @@ -331,11 +317,11 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f key := singleObj.Properties.Property[0].Value var theStrToPolygon2DListMap StrToPolygon2DListMap - if existingStrToPolygon2DListMap, ok := gidBoundariesMapInB2World[globalGid]; ok { + if existingStrToPolygon2DListMap, ok := gidBoundariesMap[globalGid]; ok { theStrToPolygon2DListMap = existingStrToPolygon2DListMap } else { - gidBoundariesMapInB2World[globalGid] = make(StrToPolygon2DListMap, 0) - theStrToPolygon2DListMap = gidBoundariesMapInB2World[globalGid] + gidBoundariesMap[globalGid] = make(StrToPolygon2DListMap, 0) + theStrToPolygon2DListMap = gidBoundariesMap[globalGid] } var pThePolygon2DList *Polygon2DList @@ -347,7 +333,7 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f pThePolygon2DList = theStrToPolygon2DListMap[key] } - thePolygon2DFromPolyline, err := TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns) + thePolygon2DFromPolyline, err := tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns) if nil != err { panic(err) } @@ -357,16 +343,9 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f return nil } -func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[int]StrToPolygon2DListMap) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) { +func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToPolygon2DListMap) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) { toRetStrToVec2DListMap := make(StrToVec2DListMap, 0) toRetStrToPolygon2DListMap := make(StrToPolygon2DListMap, 0) - /* - Note that both - - "Vec2D"s of "toRetStrToVec2DListMap", and - - "Polygon2D"s of "toRetStrToPolygon2DListMap" - - are already transformed into the "coordinate of B2World". - */ for _, objGroup := range pTmxMapIns.ObjectGroups { switch objGroup.Name { @@ -376,10 +355,8 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i if false == ok { theVec2DListToCache := make(Vec2DList, 0) toRetStrToVec2DListMap[objGroup.Name] = &theVec2DListToCache - pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name] - } else { - pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name] - } + } + pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name] for _, singleObjInTmxFile := range objGroup.Objects { theUntransformedPos := &Vec2D{ X: singleObjInTmxFile.X, @@ -389,16 +366,14 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i *pTheVec2DListToCache = append(*pTheVec2DListToCache, &thePosInWorld) } case "Barrier": - // Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]" w.r.t. B2World. + // Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]". var pThePolygon2DListToCache *Polygon2DList _, ok := toRetStrToPolygon2DListMap[objGroup.Name] if false == ok { thePolygon2DListToCache := make(Polygon2DList, 0) toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache - pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name] - } else { - pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name] } + pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name] for _, singleObjInTmxFile := range objGroup.Objects { if nil == singleObjInTmxFile.Polyline { @@ -408,7 +383,7 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i continue } - thePolygon2DInWorld, err := TmxPolylineToPolygon2DInB2World(pTmxMapIns, singleObjInTmxFile, singleObjInTmxFile.Polyline) + thePolygon2DInWorld, err := tmxPolylineToPolygon2D(pTmxMapIns, singleObjInTmxFile, singleObjInTmxFile.Polyline) if nil != err { panic(err) } diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index 020e9a8..31ccba3 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 216.05530045313827, + 209.73151519075364, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 87bf748..c670e39 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -1047,6 +1047,7 @@ cc.Class({ const inputList = self.getCachedInputFrameDownsyncWithPrediction(j).inputList; for (let j in self.playerRichInfoArr) { const joinIndex = parseInt(j) + 1; + const playerId = self.playerRichInfoArr[j].id; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const playerCollider = collisionSysMap.get(collisionPlayerIndex); const player = renderFrame.players[playerId]; diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index 7069cf8..5cf08e9 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -166,6 +166,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { break; case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC: if (window.handleInputFrameDownsyncBatch && window.handleRoomDownsyncFrame) { + console.warn("GOT forced resync:", resp); // The following order of execution is important, because "handleInputFrameDownsyncBatch" is only available when state is IN_BATTLE window.handleRoomDownsyncFrame(resp.rdf); window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); From 2264c0d3624a0cbb9a49bf0b607d219e051c8aa0 Mon Sep 17 00:00:00 2001 From: genxium Date: Sat, 1 Oct 2022 20:45:38 +0800 Subject: [PATCH 06/13] Updated logging. --- battle_srv/models/barrier.go | 2 +- battle_srv/models/player.go | 18 +- battle_srv/models/room.go | 458 +++++++++++++------------ battle_srv/models/room_heap_manager.go | 4 +- battle_srv/models/tiled_map.go | 6 +- frontend/assets/scripts/Map.js | 15 +- 6 files changed, 259 insertions(+), 244 deletions(-) diff --git a/battle_srv/models/barrier.go b/battle_srv/models/barrier.go index 99c37dc..cb3db06 100644 --- a/battle_srv/models/barrier.go +++ b/battle_srv/models/barrier.go @@ -1,5 +1,5 @@ package models type Barrier struct { - Boundary *Polygon2D + Boundary *Polygon2D } diff --git a/battle_srv/models/player.go b/battle_srv/models/player.go index 50b9b3f..eb9424f 100644 --- a/battle_srv/models/player.go +++ b/battle_srv/models/player.go @@ -47,15 +47,15 @@ type Player struct { DisplayName string `json:"displayName,omitempty" db:"display_name"` Avatar string `json:"avatar,omitempty"` - FrozenAtGmtMillis int64 `json:"-" db:"-"` - AddSpeedAtGmtMillis int64 `json:"-" db:"-"` - CreatedAt int64 `json:"-" db:"created_at"` - UpdatedAt int64 `json:"-" db:"updated_at"` - DeletedAt NullInt64 `json:"-" db:"deleted_at"` - TutorialStage int `json:"-" db:"tutorial_stage"` - AckingFrameId int32 `json:"ackingFrameId"` - AckingInputFrameId int32 `json:"-"` - LastSentInputFrameId int32 `json:"-"` + FrozenAtGmtMillis int64 `json:"-" db:"-"` + AddSpeedAtGmtMillis int64 `json:"-" db:"-"` + CreatedAt int64 `json:"-" db:"created_at"` + UpdatedAt int64 `json:"-" db:"updated_at"` + DeletedAt NullInt64 `json:"-" db:"deleted_at"` + TutorialStage int `json:"-" db:"tutorial_stage"` + AckingFrameId int32 `json:"ackingFrameId"` + AckingInputFrameId int32 `json:"-"` + LastSentInputFrameId int32 `json:"-"` } func ExistPlayerByName(name string) (bool, error) { diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 2b65d7f..2281706 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -8,8 +8,8 @@ import ( "github.com/solarlune/resolv" "go.uber.org/zap" "io/ioutil" + "math" "math/rand" - "math" "os" "path/filepath" . "server/common" @@ -26,9 +26,9 @@ const ( UPSYNC_MSG_ACT_PLAYER_CMD = int32(2) UPSYNC_MSG_ACT_PLAYER_COLLIDER_ACK = int32(3) - DOWNSYNC_MSG_ACT_HB_REQ = int32(1) - DOWNSYNC_MSG_ACT_INPUT_BATCH = int32(2) - DOWNSYNC_MSG_ACT_ROOM_FRAME = int32(3) + DOWNSYNC_MSG_ACT_HB_REQ = int32(1) + DOWNSYNC_MSG_ACT_INPUT_BATCH = int32(2) + DOWNSYNC_MSG_ACT_ROOM_FRAME = int32(3) DOWNSYNC_MSG_ACT_FORCED_RESYNC = int32(4) ) @@ -327,7 +327,7 @@ func (pR *Room) ChooseStage() error { } func (pR *Room) ConvertToInputFrameId(renderFrameId int32, inputDelayFrames int32) int32 { - // Specifically when "renderFrameId < inputDelayFrames", the result is 0. + // Specifically when "renderFrameId < inputDelayFrames", the result is 0. return ((renderFrameId - inputDelayFrames) >> pR.InputScaleFrames) } @@ -336,7 +336,7 @@ func (pR *Room) ConvertToFirstUsedRenderFrameId(inputFrameId int32, inputDelayFr } func (pR *Room) ConvertToLastUsedRenderFrameId(inputFrameId int32, inputDelayFrames int32) int32 { - return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames + (1 << pR.InputScaleFrames)-1) + return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames + (1 << pR.InputScaleFrames) - 1) } func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 { @@ -346,19 +346,21 @@ func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 { return ret } -func (pR *Room) AllPlayerInputsBufferString() string { +func (pR *Room) AllPlayerInputsBufferString(allDetails bool) string { s := make([]string, 0) - s = append(s, fmt.Sprintf("\n{stInputFrameId: %v, edInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v, lastAllConfirmedInputFrameId: %v}", pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId)) - for playerId, player := range pR.Players { - s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId)) - } - for i := pR.AllPlayerInputsBuffer.StFrameId; i < pR.AllPlayerInputsBuffer.EdFrameId; i++ { - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(i) - if nil == tmp { - break + s = append(s, fmt.Sprintf("{renderFrameId: %v, stInputFrameId: %v, edInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v, lastAllConfirmedInputFrameId: %v}", pR.RenderFrameId, pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId)) + if allDetails { + for playerId, player := range pR.Players { + s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId)) + } + for i := pR.AllPlayerInputsBuffer.StFrameId; i < pR.AllPlayerInputsBuffer.EdFrameId; i++ { + tmp := pR.AllPlayerInputsBuffer.GetByFrameId(i) + if nil == tmp { + break + } + f := tmp.(*pb.InputFrameDownsync) + s = append(s, fmt.Sprintf("{inputFrameId: %v, inputList: %v, confirmedList: %v}", f.InputFrameId, f.InputList, f.ConfirmedList)) } - f := tmp.(*pb.InputFrameDownsync) - s = append(s, fmt.Sprintf("{inputFrameId: %v, inputList: %v, confirmedList: %v}", f.InputFrameId, f.InputList, f.ConfirmedList)) } return strings.Join(s, "\n") @@ -373,7 +375,17 @@ func (pR *Room) StartBattle() { // Always instantiates a new channel and let the old one die out due to not being retained by any root reference. nanosPerFrame := 1000000000 / int64(pR.ServerFPS) pR.RenderFrameId = 0 + + // Initialize the "collisionSys" as well as "RenderFrameBuffer" pR.CurDynamicsRenderFrameId = 0 + kickoffFrame := &pb.RoomDownsyncFrame{ + Id: pR.RenderFrameId, + RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START, // Legacy frontend codes need this special "refFrameId" to remove the "3-2-1-countdown" logo + Players: toPbPlayers(pR.Players), + SentAt: utils.UnixtimeMilli(), + CountdownNanos: pR.BattleDurationNanos, + } + pR.RenderFrameBuffer.Put(kickoffFrame) // Refresh "Colliders" pR.refreshColliders() @@ -398,30 +410,8 @@ func (pR *Room) StartBattle() { for { stCalculation := utils.UnixtimeNano() - if 0 == pR.RenderFrameId { - // The legacy frontend code needs this "kickoffFrame" to remove the "ready to start 3-2-1" panel - kickoffFrame := pb.RoomDownsyncFrame{ - Id: pR.RenderFrameId, - RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START, - Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), - CountdownNanos: (pR.BattleDurationNanos - totalElapsedNanos), - } - - pR.RenderFrameBuffer.Put(&kickoffFrame) - for playerId, player := range pR.Players { - if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped { - /* - [WARNING] DON'T send anything into "DedicatedForwardingChanForPlayer" if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player". - */ - continue - } - pR.sendSafely(&kickoffFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, playerId) - } - } - if totalElapsedNanos > pR.BattleDurationNanos { - Logger.Info(fmt.Sprintf("The `battleMainLoop` for roomId=%v is stopped:\n%v", pR.Id, pR.AllPlayerInputsBufferString())) + Logger.Info(fmt.Sprintf("The `battleMainLoop` for roomId=%v is stopped:\n%v", pR.Id, pR.AllPlayerInputsBufferString(true))) pR.StopBattleForSettlement() } @@ -434,84 +424,93 @@ func (pR *Room) StartBattle() { noDelayInputFrameId := pR.ConvertToInputFrameId(pR.RenderFrameId, 0) pR.prefabInputFrameDownsync(noDelayInputFrameId) } - - // Force setting all-confirmed of buffered inputFrames periodically - unconfirmedMask := pR.forceConfirmationIfApplicable() - // Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId" - if 0 <= pR.CurDynamicsRenderFrameId { - nextDynamicsRenderFrameId := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames) - pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId) - } + // Force setting all-confirmed of buffered inputFrames periodically + unconfirmedMask := pR.forceConfirmationIfApplicable() - lastAllConfirmedInputFrameIdWithChange := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameIdWithChange)) - for playerId, player := range pR.Players { - if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped { - /* - [WARNING] DON'T send anything into "DedicatedForwardingChanForPlayer" if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player". - */ - continue - } - // [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player! - toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt) - candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1 - if candidateToSendInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { - Logger.Warn(fmt.Sprintf("LastSentInputFrameId already popped: roomId=%v, playerId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, candidateToSendInputFrameId-1, player.AckingInputFrameId, pR.AllPlayerInputsBufferString())) - candidateToSendInputFrameId = pR.AllPlayerInputsBuffer.StFrameId - } + dynamicsDuration := int64(0) + if 0 <= pR.LastAllConfirmedInputFrameId { + dynamicsStartedAt := utils.UnixtimeNano() + // Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId" + nextDynamicsRenderFrameId := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames) + Logger.Debug(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, LastAllConfirmedInputFrameId=%v, InputDelayFrames=%v, nextDynamicsRenderFrameId=%v", pR.Id, pR.RenderFrameId, pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames, nextDynamicsRenderFrameId)) + pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId) + dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt + } - // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! - debugSendingInputFrameId := int32(-1) + lastAllConfirmedInputFrameIdWithChange := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameIdWithChange)) + for playerId, player := range pR.Players { + if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped { + // [WARNING] DON'T send anything if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player". + continue + } + if 0 == pR.RenderFrameId { + kickoffFrame := pR.RenderFrameBuffer.GetByFrameId(0).(*pb.RoomDownsyncFrame) + pR.sendSafely(kickoffFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, playerId) + } else { + // [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player! + toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt) + candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1 + if candidateToSendInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { + // [WARNING] As "player.LastSentInputFrameId <= lastAllConfirmedInputFrameIdWithChange" for each iteration, and "lastAllConfirmedInputFrameIdWithChange <= lastAllConfirmedInputFrameId" where the latter is used to "applyInputFrameDownsyncDynamics" and then evict "pR.AllPlayerInputsBuffer", thus there's a very high possibility that "player.LastSentInputFrameId" is already evicted. + // Logger.Debug(fmt.Sprintf("LastSentInputFrameId already popped: roomId=%v, playerId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, candidateToSendInputFrameId-1, player.AckingInputFrameId, pR.AllPlayerInputsBufferString(false))) + candidateToSendInputFrameId = pR.AllPlayerInputsBuffer.StFrameId + } - for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange { - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId) - if nil == tmp { - panic(fmt.Sprintf("Required inputFrameId=%v for roomId=%v, playerId=%v doesn't exist! AllPlayerInputsBuffer=%v", candidateToSendInputFrameId, pR.Id, playerId, pR.AllPlayerInputsBufferString())) - } - f := tmp.(*pb.InputFrameDownsync) - if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) { - debugSendingInputFrameId = candidateToSendInputFrameId - Logger.Info("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()), zap.Any("ConfirmedList", f.ConfirmedList)) - } - toSendInputFrames = append(toSendInputFrames, f) - candidateToSendInputFrameId++ - } + // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! + debugSendingInputFrameId := int32(-1) - indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) - var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) - if 0 < (unconfirmedMask & joinMask) { - refRenderFrame := pR.RenderFrameBuffer.GetByFrameId(pR.CurDynamicsRenderFrameId).(*pb.RoomDownsyncFrame) - pR.sendSafely(refRenderFrame, toSendInputFrames, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId) - } else { - if 0 >= len(toSendInputFrames) { - continue - } - pR.sendSafely(nil, toSendInputFrames, DOWNSYNC_MSG_ACT_INPUT_BATCH, playerId) - atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) - if -1 != debugSendingInputFrameId { - Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) - } - } - } + for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange { + tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId) + if nil == tmp { + panic(fmt.Sprintf("Required inputFrameId=%v for roomId=%v, playerId=%v doesn't exist! AllPlayerInputsBuffer=%v", candidateToSendInputFrameId, pR.Id, playerId, pR.AllPlayerInputsBufferString(false))) + } + f := tmp.(*pb.InputFrameDownsync) + if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) { + debugSendingInputFrameId = candidateToSendInputFrameId + Logger.Info("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false)), zap.Any("ConfirmedList", f.ConfirmedList)) + } + toSendInputFrames = append(toSendInputFrames, f) + candidateToSendInputFrameId++ + } - for 0 < pR.RenderFrameBuffer.Cnt && pR.RenderFrameBuffer.StFrameId < pR.CurDynamicsRenderFrameId { - _ = pR.RenderFrameBuffer.Pop() - } + indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) + var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) + if 0 < (unconfirmedMask & joinMask) { + refRenderFrame := pR.RenderFrameBuffer.GetByFrameId(pR.CurDynamicsRenderFrameId).(*pb.RoomDownsyncFrame) + pR.sendSafely(refRenderFrame, toSendInputFrames, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId) + } else { + if 0 >= len(toSendInputFrames) { + continue + } + pR.sendSafely(nil, toSendInputFrames, DOWNSYNC_MSG_ACT_INPUT_BATCH, playerId) + if -1 != debugSendingInputFrameId { + Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) + } + } + atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) + } + } - toApplyInputFrameId := pR.ConvertToInputFrameId(pR.CurDynamicsRenderFrameId, pR.InputDelayFrames) - for 0 < pR.AllPlayerInputsBuffer.Cnt && pR.AllPlayerInputsBuffer.StFrameId < toApplyInputFrameId { - f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync) - if pR.inputFrameIdDebuggable(f.InputFrameId) { - // Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked - Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) - } - } + for 0 < pR.RenderFrameBuffer.Cnt && pR.RenderFrameBuffer.StFrameId < pR.CurDynamicsRenderFrameId { + _ = pR.RenderFrameBuffer.Pop() + } + + toApplyInputFrameId := pR.ConvertToInputFrameId(pR.CurDynamicsRenderFrameId, pR.InputDelayFrames) + for 0 < pR.AllPlayerInputsBuffer.Cnt && pR.AllPlayerInputsBuffer.StFrameId < toApplyInputFrameId { + f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync) + if pR.inputFrameIdDebuggable(f.InputFrameId) { + // Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked + Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + } + } pR.RenderFrameId++ - now := utils.UnixtimeNano() - elapsedInCalculation := (now - stCalculation) - totalElapsedNanos = (now - battleMainLoopStartedNanos) - // Logger.Info("Elapsed time statistics:", zap.Any("roomId", pR.Id), zap.Any("elapsedInCalculation", elapsedInCalculation), zap.Any("totalElapsedNanos", totalElapsedNanos)) + elapsedInCalculation := (utils.UnixtimeNano() - stCalculation) + totalElapsedNanos = (utils.UnixtimeNano() - battleMainLoopStartedNanos) + if elapsedInCalculation > nanosPerFrame { + Logger.Warn(fmt.Sprintf("SLOW FRAME! Elapsed time statistics: roomId=%v, room.RenderFrameId=%v, elapsedInCalculation=%v, dynamicsDuration=%v, nanosPerFrame=%v", pR.Id, pR.RenderFrameId, elapsedInCalculation, dynamicsDuration, nanosPerFrame)) + } time.Sleep(time.Duration(nanosPerFrame - elapsedInCalculation)) } } @@ -534,7 +533,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { ackingInputFrameId := pReq.AckingInputFrameId if _, existent := pR.Players[playerId]; !existent { - Logger.Warn(fmt.Sprintf("upcmd player doesn't exist: roomId=%v, playerId=%v", pR.Id, playerId)) + Logger.Warn(fmt.Sprintf("upcmd player doesn't exist: roomId=%v, playerId=%v", pR.Id, playerId)) return } @@ -549,7 +548,8 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { for _, inputFrameUpsync := range inputFrameUpsyncBatch { clientInputFrameId := inputFrameUpsync.InputFrameId if clientInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { - Logger.Warn(fmt.Sprintf("Obsolete inputFrameUpsync: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString())) + // Obsolete is actually not as concerned as advanced inputFrame. + Logger.Debug(fmt.Sprintf("Obsolete inputFrameUpsync: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) return } @@ -557,19 +557,19 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { encodedInput := pR.EncodeUpsyncCmd(inputFrameUpsync) if clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId { - Logger.Warn(fmt.Sprintf("inputFrame too advanced! is the player cheating?: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString())) + Logger.Warn(fmt.Sprintf("inputFrame too advanced! is the player cheating?: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) return } tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId) if nil == tmp2 { // This shouldn't happen due to the previous 2 checks - Logger.Warn(fmt.Sprintf("Mysterious error getting an input frame: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString())) + Logger.Warn(fmt.Sprintf("Mysterious error getting an input frame: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) return } inputFrameDownsync := tmp2.(*pb.InputFrameDownsync) oldConfirmedList := atomic.LoadUint64(&(inputFrameDownsync.ConfirmedList)) if (oldConfirmedList & joinMask) > 0 { - Logger.Warn(fmt.Sprintf("Cmd already confirmed but getting set attempt, omitting this upsync cmd: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString())) + Logger.Warn(fmt.Sprintf("Cmd already confirmed but getting set attempt, omitting this upsync cmd: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) return } @@ -587,7 +587,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { } totPlayerCnt := uint32(pR.Capacity) - allConfirmedMask := uint64((1 << totPlayerCnt) - 1) + allConfirmedMask := uint64((1 << totPlayerCnt) - 1) if allConfirmedMask == newConfirmedList { pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, playerId) } @@ -595,18 +595,22 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { } func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFrameDownsync, playerId int32) { - clientInputFrameId := inputFrameDownsync.InputFrameId - if false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { - atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), clientInputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here! - Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, clientInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString())) + inputFrameId := inputFrameDownsync.InputFrameId + if -1 == pR.LastAllConfirmedInputFrameIdWithChange || false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { + atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), inputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here! + Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, inputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString(false))) } - atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! + atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), inputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! for i, v := range inputFrameDownsync.InputList { // To avoid potential misuse of pointers pR.LastAllConfirmedInputList[i] = v } - if pR.inputFrameIdDebuggable(clientInputFrameId) { - Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[allconfirmed]: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.LastAllConfirmedInputFrameId, pR.AllPlayerInputsBufferString())) + if pR.inputFrameIdDebuggable(inputFrameId) { + if -1 == playerId { + Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[forced-allconfirmed]: roomId=%v, inputFrameId=%v, lastAllConfirmedInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, inputFrameId, pR.LastAllConfirmedInputFrameId, pR.AllPlayerInputsBufferString(false))) + } else { + Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[allconfirmed]: roomId=%v, playerId=%v, inputFrameId=%v, lastAllConfirmedInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, pR.AllPlayerInputsBufferString(false))) + } } } @@ -974,12 +978,12 @@ func (pR *Room) sendSafely(roomDownsyncFrame *pb.RoomDownsyncFrame, toSendFrames } }() - pResp := &pb.WsResp{ - Ret: int32(Constants.RetCode.Ok), - Act: act, - Rdf: roomDownsyncFrame, - InputFrameDownsyncBatch: toSendFrames, - } + pResp := &pb.WsResp{ + Ret: int32(Constants.RetCode.Ok), + Act: act, + Rdf: roomDownsyncFrame, + InputFrameDownsyncBatch: toSendFrames, + } theBytes, marshalErr := proto.Marshal(pResp) if nil != marshalErr { @@ -1012,9 +1016,9 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDowns } else { tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId - 1) if nil == tmp { - panic(fmt.Sprintf("Error prefabbing inputFrameDownsync: roomId=%v, AllPlayerInputsBuffer=%v", pR.Id, pR.AllPlayerInputsBufferString())) + panic(fmt.Sprintf("Error prefabbing inputFrameDownsync: roomId=%v, AllPlayerInputsBuffer=%v", pR.Id, pR.AllPlayerInputsBufferString(false))) } - prevInputFrameDownsync := tmp.(*pb.InputFrameDownsync) + prevInputFrameDownsync := tmp.(*pb.InputFrameDownsync) currInputList := prevInputFrameDownsync.InputList // Would be a clone of the values currInputFrameDownsync = &pb.InputFrameDownsync{ InputFrameId: inputFrameId, @@ -1028,35 +1032,38 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDowns } func (pR *Room) forceConfirmationIfApplicable() uint64 { - // Force confirmation of non-all-confirmed inputFrame EXACTLY ONE AT A TIME, returns the non-confirmed mask of players, e.g. in a 4-player-battle returning 1001 means that players with JoinIndex=1 and JoinIndex=4 are non-confirmed for inputFrameId2 - renderFrameId1 := (pR.RenderFrameId - pR.NstDelayFrames) // the renderFrameId which should've been rendered on frontend - if 0 > renderFrameId1 || !pR.shouldPrefabInputFrameDownsync(renderFrameId1) { - /* - The backend "shouldPrefabInputFrameDownsync" shares the same rule as frontend "shouldGenerateInputFrameUpsync". - */ - return 0 - } + // Force confirmation of non-all-confirmed inputFrame EXACTLY ONE AT A TIME, returns the non-confirmed mask of players, e.g. in a 4-player-battle returning 1001 means that players with JoinIndex=1 and JoinIndex=4 are non-confirmed for inputFrameId2 + renderFrameId1 := (pR.RenderFrameId - pR.NstDelayFrames) // the renderFrameId which should've been rendered on frontend + if 0 > renderFrameId1 || !pR.shouldPrefabInputFrameDownsync(renderFrameId1) { + /* + The backend "shouldPrefabInputFrameDownsync" shares the same rule as frontend "shouldGenerateInputFrameUpsync". - inputFrameId2 := pR.ConvertToInputFrameId(renderFrameId1, 0) // The inputFrame to force confirmation (if necessary) - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId2) - if nil == tmp { - panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! AllPlayerInputsBuffer=%v", inputFrameId2, pR.Id, pR.AllPlayerInputsBufferString())) - } - inputFrame2 := tmp.(*pb.InputFrameDownsync) + It's also important that "forceConfirmationIfApplicable" is NOT EXECUTED for every renderFrame, such that when a player is forced to resync, it has some time, i.e. (1 << InputScaleFrames) renderFrames, to upsync again. + */ + return 0 + } - totPlayerCnt := uint32(pR.Capacity) - allConfirmedMask := uint64((1 << totPlayerCnt) - 1) - if swapped := atomic.CompareAndSwapUint64(&(inputFrame2.ConfirmedList), allConfirmedMask, allConfirmedMask); swapped { + inputFrameId2 := pR.ConvertToInputFrameId(renderFrameId1, 0) // The inputFrame to force confirmation (if necessary) + tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId2) + if nil == tmp { + panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! AllPlayerInputsBuffer=%v", inputFrameId2, pR.Id, pR.AllPlayerInputsBufferString(false))) + } + inputFrame2 := tmp.(*pb.InputFrameDownsync) + + totPlayerCnt := uint32(pR.Capacity) + allConfirmedMask := uint64((1 << totPlayerCnt) - 1) + if swapped := atomic.CompareAndSwapUint64(&(inputFrame2.ConfirmedList), allConfirmedMask, allConfirmedMask); swapped { Logger.Info(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v, no need to force confirmation of it", inputFrameId2, pR.Id)) - return 0 - } + return 0 + } - // Force confirmation of "inputFrame2" - oldConfirmedList := atomic.LoadUint64(&(inputFrame2.ConfirmedList)) - atomic.StoreUint64(&(inputFrame2.ConfirmedList), allConfirmedMask) - pR.onInputFrameDownsyncAllConfirmed(inputFrame2, -1) + // Force confirmation of "inputFrame2" + oldConfirmedList := atomic.LoadUint64(&(inputFrame2.ConfirmedList)) + atomic.StoreUint64(&(inputFrame2.ConfirmedList), allConfirmedMask) + pR.onInputFrameDownsyncAllConfirmed(inputFrame2, -1) - return (oldConfirmedList^allConfirmedMask) + unconfirmedMask := (oldConfirmedList ^ allConfirmedMask) + return unconfirmedMask } func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRenderFrameId int32) { @@ -1064,59 +1071,64 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende return } - totPlayerCnt := uint32(pR.Capacity) - allConfirmedMask := uint64((1 << totPlayerCnt) - 1) + Logger.Debug(fmt.Sprintf("Applying inputFrame dynamics: roomId=%v, room.RenderFrameId=%v, fromRenderFrameId=%v, toRenderFrameId=%v", pR.Id, pR.RenderFrameId, fromRenderFrameId, toRenderFrameId)) + + totPlayerCnt := uint32(pR.Capacity) + allConfirmedMask := uint64((1 << totPlayerCnt) - 1) for collisionSysRenderFrameId := fromRenderFrameId; collisionSysRenderFrameId < toRenderFrameId; collisionSysRenderFrameId++ { delayedInputFrameId := pR.ConvertToInputFrameId(collisionSysRenderFrameId, pR.InputDelayFrames) if 0 <= delayedInputFrameId { - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(delayedInputFrameId) - if nil == tmp { - panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString())) - } - delayedInputFrame := tmp.(*pb.InputFrameDownsync) - if swapped := atomic.CompareAndSwapUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask, allConfirmedMask); !swapped { - panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString())) - } - + tmp := pR.AllPlayerInputsBuffer.GetByFrameId(delayedInputFrameId) + if nil == tmp { + panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString(false))) + } + delayedInputFrame := tmp.(*pb.InputFrameDownsync) + if swapped := atomic.CompareAndSwapUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask, allConfirmedMask); !swapped { + panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString(false))) + } + inputList := delayedInputFrame.InputList // Ordered by joinIndex to guarantee determinism for _, player := range pR.PlayersArr { joinIndex := player.JoinIndex - collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex - playerCollider := pR.CollisionSysMap[collisionPlayerIndex] encodedInput := inputList[joinIndex-1] decodedInput := DIRECTION_DECODER[encodedInput] decodedInputSpeedFactor := DIRECTION_DECODER_INVERSE_LENGTH[encodedInput] baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor dx := baseChange * float64(decodedInput[0]) dy := baseChange * float64(decodedInput[1]) - if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { - changeWithCollision := collision.ContactWithObject(collision.Objects[0]) - dx = changeWithCollision.X() - dy = changeWithCollision.Y() - } - playerCollider.X += dx - playerCollider.Y += dy - // Update in "collision space" - playerCollider.Update() + /* + // The collision lib seems very slow at worst cases, omitting for now + collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex + playerCollider := pR.CollisionSysMap[collisionPlayerIndex] + if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { + changeWithCollision := collision.ContactWithObject(collision.Objects[0]) + dx = changeWithCollision.X() + dy = changeWithCollision.Y() + } + playerCollider.X += dx + playerCollider.Y += dy + // Update in "collision space" + playerCollider.Update() + */ - player.Dir.Dx = decodedInput[0] - player.Dir.Dy = decodedInput[1] - player.X = playerCollider.X - player.Y = playerCollider.Y + player.Dir.Dx = decodedInput[0] + player.Dir.Dy = decodedInput[1] + player.X += dx + player.Y += dy } } - newRenderFrame := pb.RoomDownsyncFrame{ - Id: collisionSysRenderFrameId+1, - RefFrameId: collisionSysRenderFrameId, - Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), - CountdownNanos: (pR.BattleDurationNanos - int64(collisionSysRenderFrameId)*int64(pR.RollbackEstimatedDt*1000000000)), - } - pR.RenderFrameBuffer.Put(&newRenderFrame) - pR.CurDynamicsRenderFrameId++ + newRenderFrame := pb.RoomDownsyncFrame{ + Id: collisionSysRenderFrameId + 1, + RefFrameId: collisionSysRenderFrameId, + Players: toPbPlayers(pR.Players), + SentAt: utils.UnixtimeMilli(), + CountdownNanos: (pR.BattleDurationNanos - int64(collisionSysRenderFrameId)*int64(pR.RollbackEstimatedDt*1000000000)), + } + pR.RenderFrameBuffer.Put(&newRenderFrame) + pR.CurDynamicsRenderFrameId++ } } @@ -1125,44 +1137,44 @@ func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool { } func (pR *Room) refreshColliders() { - // Kindly note that by now, we've already got all the shapes in the tmx file into "pR.(Players | Barriers)" from "ParseTmxLayersAndGroups" - space := resolv.NewSpace(int(pR.StageDiscreteW), int(pR.StageDiscreteH), int(pR.StageTileW), int(pR.StageTileH)) // allocate a new collision space everytime after a battle is settled + // Kindly note that by now, we've already got all the shapes in the tmx file into "pR.(Players | Barriers)" from "ParseTmxLayersAndGroups" + space := resolv.NewSpace(int(pR.StageDiscreteW), int(pR.StageDiscreteH), int(pR.StageTileW), int(pR.StageTileH)) // allocate a new collision space everytime after a battle is settled for _, player := range pR.Players { - playerCollider := resolv.NewObject(player.X, player.Y, 12, 12) // Radius=12 is hardcoded - playerColliderShape := resolv.NewCircle(player.X, player.Y, 12) - playerCollider.SetShape(playerColliderShape) - space.Add(playerCollider) - // Keep track of the collider in "pR.CollisionSysMap" + playerCollider := resolv.NewObject(player.X, player.Y, 12, 12) // Radius=12 is hardcoded + playerColliderShape := resolv.NewCircle(player.X, player.Y, 12) + playerCollider.SetShape(playerColliderShape) + space.Add(playerCollider) + // Keep track of the collider in "pR.CollisionSysMap" joinIndex := player.JoinIndex pR.PlayersArr[joinIndex-1] = player - collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex - pR.CollisionSysMap[collisionPlayerIndex] = playerCollider + collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex + pR.CollisionSysMap[collisionPlayerIndex] = playerCollider } - - for _, barrier := range pR.Barriers { - var w float64 = 0 - var h float64 = 0 - for i, pi := range barrier.Boundary.Points { - for j, pj := range barrier.Boundary.Points { - if i == j { - continue - } - if math.Abs(pj.X - pi.X) > w { - w = math.Abs(pj.X - pi.X) - } - if math.Abs(pj.Y - pi.Y) > h { - h = math.Abs(pj.Y - pi.Y) - } - } - } - barrierColliderShape := resolv.NewConvexPolygon() - for _, p := range barrier.Boundary.Points { - barrierColliderShape.AddPoints(p.X+barrier.Boundary.Anchor.X, p.Y+barrier.Boundary.Anchor.Y) - } + for _, barrier := range pR.Barriers { + var w float64 = 0 + var h float64 = 0 + for i, pi := range barrier.Boundary.Points { + for j, pj := range barrier.Boundary.Points { + if i == j { + continue + } + if math.Abs(pj.X-pi.X) > w { + w = math.Abs(pj.X - pi.X) + } + if math.Abs(pj.Y-pi.Y) > h { + h = math.Abs(pj.Y - pi.Y) + } + } + } - barrierCollider := resolv.NewObject(barrier.Boundary.Anchor.X, barrier.Boundary.Anchor.Y, w, h, "Barrier") - barrierCollider.SetShape(barrierColliderShape) - space.Add(barrierCollider) - } + barrierColliderShape := resolv.NewConvexPolygon() + for _, p := range barrier.Boundary.Points { + barrierColliderShape.AddPoints(p.X+barrier.Boundary.Anchor.X, p.Y+barrier.Boundary.Anchor.Y) + } + + barrierCollider := resolv.NewObject(barrier.Boundary.Anchor.X, barrier.Boundary.Anchor.Y, w, h, "Barrier") + barrierCollider.SetShape(barrierColliderShape) + space.Add(barrierCollider) + } } diff --git a/battle_srv/models/room_heap_manager.go b/battle_srv/models/room_heap_manager.go index 7669724..bd33264 100644 --- a/battle_srv/models/room_heap_manager.go +++ b/battle_srv/models/room_heap_manager.go @@ -110,7 +110,7 @@ func InitRoomHeapManager() { State: currentRoomBattleState, Index: i, RenderFrameId: 0, - CurDynamicsRenderFrameId: 0, + CurDynamicsRenderFrameId: 0, EffectivePlayerCount: 0, //BattleDurationNanos: int64(5 * 1000 * 1000 * 1000), BattleDurationNanos: int64(30 * 1000 * 1000 * 1000), @@ -122,7 +122,7 @@ func InitRoomHeapManager() { LastAllConfirmedInputFrameIdWithChange: -1, LastAllConfirmedInputList: make([]uint64, roomCapacity), InputDelayFrames: 4, - NstDelayFrames: 2, + NstDelayFrames: 8, InputScaleFrames: 2, JoinIndexBooleanArr: joinIndexBooleanArr, RollbackEstimatedDt: float64(1.0) / 60, diff --git a/battle_srv/models/tiled_map.go b/battle_srv/models/tiled_map.go index a483211..6eadb62 100644 --- a/battle_srv/models/tiled_map.go +++ b/battle_srv/models/tiled_map.go @@ -355,8 +355,8 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP if false == ok { theVec2DListToCache := make(Vec2DList, 0) toRetStrToVec2DListMap[objGroup.Name] = &theVec2DListToCache - } - pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name] + } + pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name] for _, singleObjInTmxFile := range objGroup.Objects { theUntransformedPos := &Vec2D{ X: singleObjInTmxFile.X, @@ -373,7 +373,7 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP thePolygon2DListToCache := make(Polygon2DList, 0) toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache } - pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name] + pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name] for _, singleObjInTmxFile := range objGroup.Objects { if nil == singleObjInTmxFile.Polyline { diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index c670e39..5466ffa 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -124,6 +124,10 @@ cc.Class({ type: cc.Float, default: 1.0/60 }, + perFrameDtMaxTolerance: { + type: cc.Float, + default: 1.0/600 + }, maxChasingRenderFramesPerUpdate: { type: cc.Integer, default: 10 @@ -787,7 +791,12 @@ cc.Class({ update(dt) { const self = this; if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { + if (dt < self.rollbackEstimatedDt-self.perFrameDtMaxTolerance) { + console.warn("Avoiding too fast frame@renderFrameId=", self.renderFrameId, ": dt=", dt); + return; + } try { + let st = performance.now(); let prevSelfInput = null, currSelfInput = null; const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here if (self.shouldGenerateInputFrameUpsync(self.renderFrameId)) { @@ -813,14 +822,8 @@ cc.Class({ // Inside "self.rollbackAndChase", the "self.latestCollisionSys" is ALWAYS ROLLED BACK to "self.recentRenderCache.get(self.renderFrameId)" before being applied dynamics from corresponding inputFrameDownsync, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now. const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId+1, self.latestCollisionSys, self.latestCollisionSysMap); - self.applyRoomDownsyncFrameDynamics(rdf); let t3 = performance.now(); - /* - if (prevChaserRenderFrameId < nextChaserRenderFrameId) { - console.log("Took ", t1-t0, " milliseconds to send upsync cmds, ", t2-t1, " milliseconds to chase renderFrameIds=[", prevChaserRenderFrameId, ", ", nextChaserRenderFrameId, "], @renderFrameId=", self.renderFrameId); - } - */ } catch (err) { console.error("Error during Map.update", err); } finally { From f3a576ba13bc96c2f293d0224c896a02c361a91a Mon Sep 17 00:00:00 2001 From: genxium Date: Sat, 1 Oct 2022 23:54:48 +0800 Subject: [PATCH 07/13] Minor updates. --- battle_srv/models/room.go | 108 ++++++------- .../pb_output/room_downsync_frame.pb.go | 150 ++++++++---------- frontend/assets/plugin_scripts/constants.js | 20 --- .../pbfiles/room_downsync_frame.proto | 8 +- frontend/assets/scenes/login.fire | 2 +- frontend/assets/scripts/Map.js | 48 +++--- frontend/assets/scripts/WsSessionMgr.js | 15 +- ...om_downsync_frame_proto_bundle.forcemsg.js | 70 +------- 8 files changed, 159 insertions(+), 262 deletions(-) diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 2281706..0a972db 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -30,14 +30,15 @@ const ( DOWNSYNC_MSG_ACT_INPUT_BATCH = int32(2) DOWNSYNC_MSG_ACT_ROOM_FRAME = int32(3) DOWNSYNC_MSG_ACT_FORCED_RESYNC = int32(4) + + DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START = int32(-1) + DOWNSYNC_MSG_ACT_BATTLE_START = int32(0) + + DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED = int32(-98) + DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED = int32(-97) ) const ( - MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START = -1 - MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START = 0 - MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_ADDED_AND_ACKED = -98 - MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED = -97 - MAGIC_JOIN_INDEX_DEFAULT = 0 MAGIC_JOIN_INDEX_INVALID = -1 ) @@ -380,9 +381,7 @@ func (pR *Room) StartBattle() { pR.CurDynamicsRenderFrameId = 0 kickoffFrame := &pb.RoomDownsyncFrame{ Id: pR.RenderFrameId, - RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START, // Legacy frontend codes need this special "refFrameId" to remove the "3-2-1-countdown" logo Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), CountdownNanos: pR.BattleDurationNanos, } pR.RenderFrameBuffer.Put(kickoffFrame) @@ -433,6 +432,10 @@ func (pR *Room) StartBattle() { dynamicsStartedAt := utils.UnixtimeNano() // Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId" nextDynamicsRenderFrameId := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames) + if nextDynamicsRenderFrameId > pR.RenderFrameId { + // [WARNING] DON'T apply dynamics too fast, otherwise upon DOWNSYNC_MSG_ACT_FORCED_RESYNC the frontend would resync itself to a "too advanced frontend.renderFrameId", and then start upsyncing "too advanced inputFrameId". + nextDynamicsRenderFrameId = pR.RenderFrameId + } Logger.Debug(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, LastAllConfirmedInputFrameId=%v, InputDelayFrames=%v, nextDynamicsRenderFrameId=%v", pR.Id, pR.RenderFrameId, pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames, nextDynamicsRenderFrameId)) pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId) dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt @@ -446,7 +449,7 @@ func (pR *Room) StartBattle() { } if 0 == pR.RenderFrameId { kickoffFrame := pR.RenderFrameBuffer.GetByFrameId(0).(*pb.RoomDownsyncFrame) - pR.sendSafely(kickoffFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, playerId) + pR.sendSafely(kickoffFrame, nil, DOWNSYNC_MSG_ACT_BATTLE_START, playerId) } else { // [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player! toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt) @@ -484,10 +487,10 @@ func (pR *Room) StartBattle() { continue } pR.sendSafely(nil, toSendInputFrames, DOWNSYNC_MSG_ACT_INPUT_BATCH, playerId) - if -1 != debugSendingInputFrameId { - Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) - } } + if -1 != debugSendingInputFrameId { + Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) + } atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) } } @@ -557,7 +560,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { encodedInput := pR.EncodeUpsyncCmd(inputFrameUpsync) if clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId { - Logger.Warn(fmt.Sprintf("inputFrame too advanced! is the player cheating?: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) + Logger.Warn(fmt.Sprintf("inputFrame too advanced! is the player cheating? roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) return } tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId) @@ -597,8 +600,8 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFrameDownsync, playerId int32) { inputFrameId := inputFrameDownsync.InputFrameId if -1 == pR.LastAllConfirmedInputFrameIdWithChange || false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { - atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), inputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here! - Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, inputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString(false))) + Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, newInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString(false))) + atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), inputFrameId) } atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), inputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! for i, v := range inputFrameDownsync.InputList { @@ -636,9 +639,7 @@ func (pR *Room) StopBattleForSettlement() { for playerId, _ := range pR.Players { assembledFrame := pb.RoomDownsyncFrame{ Id: pR.RenderFrameId, - RefFrameId: pR.RenderFrameId, // Hardcoded for now. Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), CountdownNanos: -1, // TODO: Replace this magic constant! } pR.sendSafely(&assembledFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, playerId) @@ -673,18 +674,16 @@ func (pR *Room) onBattlePrepare(cb BattleStartCbType) { } } - battleReadyToStartFrame := pb.RoomDownsyncFrame{ - Id: pR.RenderFrameId, + battleReadyToStartFrame := &pb.RoomDownsyncFrame{ + Id: DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START, Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), - RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START, PlayerMetas: playerMetas, CountdownNanos: pR.BattleDurationNanos, } Logger.Info("Sending out frame for RoomBattleState.PREPARE ", zap.Any("battleReadyToStartFrame", battleReadyToStartFrame)) for _, player := range pR.Players { - pR.sendSafely(&battleReadyToStartFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, player.Id) + pR.sendSafely(battleReadyToStartFrame, nil, DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START, player.Id) } battlePreparationNanos := int64(6000000000) @@ -902,58 +901,53 @@ func (pR *Room) onPlayerReAdded(playerId int32) { } func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { - pPlayer, ok := pR.Players[playerId] + targetPlayer, ok := pR.Players[playerId] if false == ok { return false } playerMetas := make(map[int32]*pb.PlayerMeta, 0) - for _, player := range pR.Players { - playerMetas[player.Id] = &pb.PlayerMeta{ - Id: player.Id, - Name: player.Name, - DisplayName: player.DisplayName, - Avatar: player.Avatar, - JoinIndex: player.JoinIndex, + for _, eachPlayer := range pR.Players { + playerMetas[eachPlayer.Id] = &pb.PlayerMeta{ + Id: eachPlayer.Id, + Name: eachPlayer.Name, + DisplayName: eachPlayer.DisplayName, + Avatar: eachPlayer.Avatar, + JoinIndex: eachPlayer.JoinIndex, } } - var playerAckedFrame pb.RoomDownsyncFrame - - switch pPlayer.BattleState { - case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK: - playerAckedFrame = pb.RoomDownsyncFrame{ - Id: pR.RenderFrameId, - Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), - RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_ADDED_AND_ACKED, - PlayerMetas: playerMetas, - } - case PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK: - playerAckedFrame = pb.RoomDownsyncFrame{ - Id: pR.RenderFrameId, - Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), - RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED, - PlayerMetas: playerMetas, - } - default: - } - - for _, player := range pR.Players { + // Broadcast added or readded player info to all players in the same room + for _, eachPlayer := range pR.Players { /* [WARNING] This `playerAckedFrame` is the first ever "RoomDownsyncFrame" for every "PersistentSessionClient on the frontend", and it goes right after each "BattleColliderInfo". By making use of the sequential nature of each ws session, all later "RoomDownsyncFrame"s generated after `pRoom.StartBattle()` will be put behind this `playerAckedFrame`. */ - pR.sendSafely(&playerAckedFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, player.Id) + switch targetPlayer.BattleState { + case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK: + playerAckedFrame := &pb.RoomDownsyncFrame{ + Id: pR.RenderFrameId, + Players: toPbPlayers(pR.Players), + PlayerMetas: playerMetas, + } + pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED, eachPlayer.Id) + case PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK: + playerAckedFrame := &pb.RoomDownsyncFrame{ + Id: pR.RenderFrameId, + Players: toPbPlayers(pR.Players), + PlayerMetas: playerMetas, + } + pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED, eachPlayer.Id) + default: + } } - pPlayer.BattleState = PlayerBattleStateIns.ACTIVE - Logger.Info("OnPlayerBattleColliderAcked", zap.Any("roomId", pR.Id), zap.Any("roomState", pR.State), zap.Any("playerId", playerId), zap.Any("capacity", pR.Capacity), zap.Any("len(players)", len(pR.Players))) + targetPlayer.BattleState = PlayerBattleStateIns.ACTIVE + Logger.Info(fmt.Sprintf("OnPlayerBattleColliderAcked: roomId=%v, roomState=%v, targetPlayerId=%v, capacity=%v, EffectivePlayerCount=%v", pR.Id, pR.State, targetPlayer.Id, pR.Capacity, pR.EffectivePlayerCount)) - if pR.Capacity == len(pR.Players) { + if pR.Capacity == int(pR.EffectivePlayerCount) { allAcked := true for _, p := range pR.Players { if PlayerBattleStateIns.ACTIVE != p.BattleState { @@ -1122,9 +1116,7 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende newRenderFrame := pb.RoomDownsyncFrame{ Id: collisionSysRenderFrameId + 1, - RefFrameId: collisionSysRenderFrameId, Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), CountdownNanos: (pR.BattleDurationNanos - int64(collisionSysRenderFrameId)*int64(pR.RollbackEstimatedDt*1000000000)), } pR.RenderFrameBuffer.Put(&newRenderFrame) diff --git a/battle_srv/pb_output/room_downsync_frame.pb.go b/battle_srv/pb_output/room_downsync_frame.pb.go index d9d60ff..808a826 100644 --- a/battle_srv/pb_output/room_downsync_frame.pb.go +++ b/battle_srv/pb_output/room_downsync_frame.pb.go @@ -767,11 +767,9 @@ type RoomDownsyncFrame struct { unknownFields protoimpl.UnknownFields Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - RefFrameId int32 `protobuf:"varint,2,opt,name=refFrameId,proto3" json:"refFrameId,omitempty"` - Players map[int32]*Player `protobuf:"bytes,3,rep,name=players,proto3" json:"players,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - SentAt int64 `protobuf:"varint,4,opt,name=sentAt,proto3" json:"sentAt,omitempty"` - CountdownNanos int64 `protobuf:"varint,5,opt,name=countdownNanos,proto3" json:"countdownNanos,omitempty"` - PlayerMetas map[int32]*PlayerMeta `protobuf:"bytes,6,rep,name=playerMetas,proto3" json:"playerMetas,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Players map[int32]*Player `protobuf:"bytes,2,rep,name=players,proto3" json:"players,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + CountdownNanos int64 `protobuf:"varint,3,opt,name=countdownNanos,proto3" json:"countdownNanos,omitempty"` + PlayerMetas map[int32]*PlayerMeta `protobuf:"bytes,4,rep,name=playerMetas,proto3" json:"playerMetas,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *RoomDownsyncFrame) Reset() { @@ -813,13 +811,6 @@ func (x *RoomDownsyncFrame) GetId() int32 { return 0 } -func (x *RoomDownsyncFrame) GetRefFrameId() int32 { - if x != nil { - return x.RefFrameId - } - return 0 -} - func (x *RoomDownsyncFrame) GetPlayers() map[int32]*Player { if x != nil { return x.Players @@ -827,13 +818,6 @@ func (x *RoomDownsyncFrame) GetPlayers() map[int32]*Player { return nil } -func (x *RoomDownsyncFrame) GetSentAt() int64 { - if x != nil { - return x.SentAt - } - return 0 -} - func (x *RoomDownsyncFrame) GetCountdownNanos() int64 { if x != nil { return x.CountdownNanos @@ -1155,77 +1139,73 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xd7, 0x03, 0x0a, 0x11, 0x52, 0x6f, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9f, 0x03, 0x0a, 0x11, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, - 0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, - 0x6e, 0x74, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x74, - 0x41, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, - 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, - 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, - 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0xca, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, - 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, - 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, - 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, - 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, - 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, - 0x0a, 0x02, 0x68, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, - 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, - 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, - 0x22, 0xa4, 0x02, 0x0a, 0x06, 0x57, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, - 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, - 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, - 0x74, 0x12, 0x34, 0x0a, 0x03, 0x72, 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, - 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, - 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x52, 0x03, 0x72, 0x64, 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, - 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, - 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, - 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, - 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, + 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, + 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, + 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, + 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xca, 0x02, 0x0a, 0x05, + 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, + 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, + 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, + 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, + 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x2e, 0x0a, + 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, + 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x57, 0x0a, + 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, + 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, + 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, + 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, + 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x06, 0x57, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, + 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, 0x63, 0x68, 0x6f, + 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x03, 0x72, 0x64, 0x66, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, + 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, + 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x72, 0x64, 0x66, 0x12, + 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, + 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, + 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3f, + 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x42, + 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/frontend/assets/plugin_scripts/constants.js b/frontend/assets/plugin_scripts/constants.js index 4fb537c..fb3d975 100644 --- a/frontend/assets/plugin_scripts/constants.js +++ b/frontend/assets/plugin_scripts/constants.js @@ -29,23 +29,6 @@ var constants = { BGM: "BGM" } }, - PLAYER_NAME: { - 1: "Merdan", - 2: "Monroe", - }, - SOCKET_EVENT: { - CONTROL: "control", - SYNC: "sync", - LOGIN: "login", - CREATE: "create" - }, - WECHAT: { - AUTHORIZE_PATH: "/connect/oauth2/authorize", - REDIRECT_RUI_KEY: "redirect_uri=", - RESPONSE_TYPE: "response_type=code", - SCOPE: "scope=snsapi_userinfo", - FIN: "#wechat_redirect" - }, ROUTE_PATH: (_ROUTE_PATH = { PLAYER: "/player", JSCONFIG: "/jsconfig", @@ -61,8 +44,6 @@ var constants = { LIST: "/list", READ: "/read", PROFILE: "/profile", - WECHAT: "/wechat", - WECHATGAME: "/wechatGame", FETCH: "/fetch", }, _defineProperty(_ROUTE_PATH, "LOGIN", "/login"), _defineProperty(_ROUTE_PATH, "RET_CODE", "/retCode"), _defineProperty(_ROUTE_PATH, "REGEX", "/regex"), _defineProperty(_ROUTE_PATH, "SMS_CAPTCHA", "/SmsCaptcha"), _defineProperty(_ROUTE_PATH, "GET", "/get"), _ROUTE_PATH), REQUEST_QUERY: { @@ -138,7 +119,6 @@ var constants = { INCORRECT_PHONE_NUMBER: '手机号不正确', LOG_OUT: '您已在其他地方登陆', GAME_OVER: '游戏结束,您的得分是', - WECHAT_LOGIN_FAILS: "微信登录失败", }, CONFIRM_BUTTON_LABEL: { RESTART: '重新开始' diff --git a/frontend/assets/resources/pbfiles/room_downsync_frame.proto b/frontend/assets/resources/pbfiles/room_downsync_frame.proto index 36d141a..e54f010 100644 --- a/frontend/assets/resources/pbfiles/room_downsync_frame.proto +++ b/frontend/assets/resources/pbfiles/room_downsync_frame.proto @@ -78,11 +78,9 @@ message HeartbeatUpsync { message RoomDownsyncFrame { int32 id = 1; - int32 refFrameId = 2; - map players = 3; - int64 sentAt = 4; - int64 countdownNanos = 5; - map playerMetas = 6; + map players = 2; + int64 countdownNanos = 3; + map playerMetas = 4; } message WsReq { diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index 31ccba3..a8af76d 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 209.73151519075364, + 210.43837950571782, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 5466ffa..35d0d5a 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -18,11 +18,8 @@ window.ALL_BATTLE_STATES = { }; window.MAGIC_ROOM_DOWNSYNC_FRAME_ID = { - PLAYER_ADDED_AND_ACKED: -98, - PLAYER_READDED_AND_ACKED: -97, - BATTLE_READY_TO_START: -1, - BATTLE_START: 0, + BATTLE_START: 0 }; cc.Class({ @@ -124,9 +121,13 @@ cc.Class({ type: cc.Float, default: 1.0/60 }, - perFrameDtMaxTolerance: { + rollbackEstimatedDtMillis: { type: cc.Float, - default: 1.0/600 + default: 1000.0/60 + }, + rollbackEstimatedDtToleranceMillis: { + type: cc.Float, + default: 1.0/60 }, maxChasingRenderFramesPerUpdate: { type: cc.Integer, @@ -353,7 +354,7 @@ cc.Class({ self.lastAllConfirmedInputFrameId = -1; self.chaserRenderFrameId = -1; // at any moment, "lastAllConfirmedRenderFrameId <= chaserRenderFrameId <= renderFrameId", but "chaserRenderFrameId" would fluctuate according to "handleInputFrameDownsyncBatch" - self.inputDelayFrames = 8; + self.inputDelayFrames = 4; self.inputScaleFrames = 2; self.lastUpsyncInputFrameId = -1; self.inputFrameUpsyncDelayTolerance = 2; @@ -547,34 +548,24 @@ cc.Class({ self._inputControlEnabled = false; let findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer"); - window.handleRoomDownsyncFrame = function(rdf) { - if (ALL_BATTLE_STATES.WAITING != self.battleState - && ALL_BATTLE_STATES.IN_BATTLE != self.battleState - && ALL_BATTLE_STATES.IN_SETTLEMENT != self.battleState) { - return; + window.handlePlayerAdded = function(rdf) { + // Update the "finding player" GUI and show it if not previously present + if (!self.findingPlayerNode.parent) { + self.showPopupInCanvas(self.findingPlayerNode); } + findingPlayerScriptIns.updatePlayersInfo(rdf.playerMetas); + }; + window.handleRoomDownsyncFrame = function(rdf) { const frameId = rdf.id; // Right upon establishment of the "PersistentSessionClient", we should receive an initial signal "BattleColliderInfo" earlier than any "RoomDownsyncFrame" containing "PlayerMeta" data. - const refFrameId = rdf.refFrameId; - switch (refFrameId) { - case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.PLAYER_ADDED_AND_ACKED: - // Update the "finding player" GUI and show it if not previously present - if (!self.findingPlayerNode.parent) { - self.showPopupInCanvas(self.findingPlayerNode); - } - findingPlayerScriptIns.updatePlayersInfo(rdf.playerMetas); - return; + switch (frameId) { case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_READY_TO_START: self.onBattleReadyToStart(rdf.playerMetas, false); return; case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START: self.onBattleStartedOrResynced(rdf); return; - case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.PLAYER_READDED_AND_ACKED: - self.onBattleReadyToStart(rdf.playerMetas, true); - self.onBattleStartedOrResynced(rdf); - return; } // TODO: Inject a NetworkDoctor as introduced in https://app.yinxiang.com/shard/s61/nl/13267014/5c575124-01db-419b-9c02-ec81f78c6ddc/. @@ -728,6 +719,7 @@ cc.Class({ self.applyRoomDownsyncFrameDynamics(rdf); self._dumpToRenderCache(rdf); self.battleState = ALL_BATTLE_STATES.IN_BATTLE; // Starts the increment of "self.renderFrameId" in "self.update(dt)" + self.lastRenderFrameIdTriggeredAt = performance.now(); if (null != window.boundRoomId) { self.boundRoomIdLabel.string = window.boundRoomId; } @@ -791,8 +783,9 @@ cc.Class({ update(dt) { const self = this; if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { - if (dt < self.rollbackEstimatedDt-self.perFrameDtMaxTolerance) { - console.warn("Avoiding too fast frame@renderFrameId=", self.renderFrameId, ": dt=", dt); + const elapsedMillisSinceLastFrameIdTriggered = performance.now() - self.lastRenderFrameIdTriggeredAt; + if (elapsedMillisSinceLastFrameIdTriggered < (self.rollbackEstimatedDtMillis)) { + // console.debug("Avoiding too fast frame@renderFrameId=", self.renderFrameId, ": elapsedMillisSinceLastFrameIdTriggered=", elapsedMillisSinceLastFrameIdTriggered); return; } try { @@ -842,6 +835,7 @@ cc.Class({ self.countdownLabel.string = countdownSeconds; } ++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!! + self.lastRenderFrameIdTriggeredAt = performance.now(); } } }, diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index 5cf08e9..da2abec 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -2,11 +2,16 @@ window.UPSYNC_MSG_ACT_HB_PING = 1; window.UPSYNC_MSG_ACT_PLAYER_CMD = 2; window.UPSYNC_MSG_ACT_PLAYER_COLLIDER_ACK = 3; +window.DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED = -98; +window.DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED = -97; +window.DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START = -1; +window.DOWNSYNC_MSG_ACT_BATTLE_START = 0; window.DOWNSYNC_MSG_ACT_HB_REQ = 1; window.DOWNSYNC_MSG_ACT_INPUT_BATCH = 2; window.DOWNSYNC_MSG_ACT_ROOM_FRAME = 3; window.DOWNSYNC_MSG_ACT_FORCED_RESYNC = 4; + window.sendSafely = function(msgStr) { /** * - "If the data can't be sent (for example, because it needs to be buffered but the buffer is full), the socket is closed automatically." @@ -154,6 +159,14 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { case window.DOWNSYNC_MSG_ACT_HB_REQ: window.handleHbRequirements(resp); // 获取boundRoomId并存储到localStorage break; + case window.DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED: + window.handlePlayerAdded(resp.rdf); + break; + case window.DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED: + // Deliberately left blank for now + break; + case window.DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START: + case window.DOWNSYNC_MSG_ACT_BATTLE_START: case window.DOWNSYNC_MSG_ACT_ROOM_FRAME: if (window.handleRoomDownsyncFrame) { window.handleRoomDownsyncFrame(resp.rdf); @@ -166,7 +179,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { break; case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC: if (window.handleInputFrameDownsyncBatch && window.handleRoomDownsyncFrame) { - console.warn("GOT forced resync:", resp); + console.warn("Got forced resync:", resp.toString(), " @localRenderFrameId=", mapIns.renderFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false)); // The following order of execution is important, because "handleInputFrameDownsyncBatch" is only available when state is IN_BATTLE window.handleRoomDownsyncFrame(resp.rdf); window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); diff --git a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js index 4a2a3e8..b79c89c 100644 --- a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js +++ b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js @@ -3131,9 +3131,7 @@ $root.treasurehunterx = (function() { * @memberof treasurehunterx * @interface IRoomDownsyncFrame * @property {number|null} [id] RoomDownsyncFrame id - * @property {number|null} [refFrameId] RoomDownsyncFrame refFrameId * @property {Object.|null} [players] RoomDownsyncFrame players - * @property {number|Long|null} [sentAt] RoomDownsyncFrame sentAt * @property {number|Long|null} [countdownNanos] RoomDownsyncFrame countdownNanos * @property {Object.|null} [playerMetas] RoomDownsyncFrame playerMetas */ @@ -3163,14 +3161,6 @@ $root.treasurehunterx = (function() { */ RoomDownsyncFrame.prototype.id = 0; - /** - * RoomDownsyncFrame refFrameId. - * @member {number} refFrameId - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.refFrameId = 0; - /** * RoomDownsyncFrame players. * @member {Object.} players @@ -3179,14 +3169,6 @@ $root.treasurehunterx = (function() { */ RoomDownsyncFrame.prototype.players = $util.emptyObject; - /** - * RoomDownsyncFrame sentAt. - * @member {number|Long} sentAt - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.sentAt = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - /** * RoomDownsyncFrame countdownNanos. * @member {number|Long} countdownNanos @@ -3229,20 +3211,16 @@ $root.treasurehunterx = (function() { writer = $Writer.create(); if (message.id != null && Object.hasOwnProperty.call(message, "id")) writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id); - if (message.refFrameId != null && Object.hasOwnProperty.call(message, "refFrameId")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.refFrameId); if (message.players != null && Object.hasOwnProperty.call(message, "players")) for (var keys = Object.keys(message.players), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 3, wireType 2 =*/26).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); + writer.uint32(/* id 2, wireType 2 =*/18).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); $root.treasurehunterx.Player.encode(message.players[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); } - if (message.sentAt != null && Object.hasOwnProperty.call(message, "sentAt")) - writer.uint32(/* id 4, wireType 0 =*/32).int64(message.sentAt); if (message.countdownNanos != null && Object.hasOwnProperty.call(message, "countdownNanos")) - writer.uint32(/* id 5, wireType 0 =*/40).int64(message.countdownNanos); + writer.uint32(/* id 3, wireType 0 =*/24).int64(message.countdownNanos); if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas")) for (var keys = Object.keys(message.playerMetas), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 6, wireType 2 =*/50).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); + writer.uint32(/* id 4, wireType 2 =*/34).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); $root.treasurehunterx.PlayerMeta.encode(message.playerMetas[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); } return writer; @@ -3284,10 +3262,6 @@ $root.treasurehunterx = (function() { break; } case 2: { - message.refFrameId = reader.int32(); - break; - } - case 3: { if (message.players === $util.emptyObject) message.players = {}; var end2 = reader.uint32() + reader.pos; @@ -3310,15 +3284,11 @@ $root.treasurehunterx = (function() { message.players[key] = value; break; } - case 4: { - message.sentAt = reader.int64(); - break; - } - case 5: { + case 3: { message.countdownNanos = reader.int64(); break; } - case 6: { + case 4: { if (message.playerMetas === $util.emptyObject) message.playerMetas = {}; var end2 = reader.uint32() + reader.pos; @@ -3379,9 +3349,6 @@ $root.treasurehunterx = (function() { if (message.id != null && message.hasOwnProperty("id")) if (!$util.isInteger(message.id)) return "id: integer expected"; - if (message.refFrameId != null && message.hasOwnProperty("refFrameId")) - if (!$util.isInteger(message.refFrameId)) - return "refFrameId: integer expected"; if (message.players != null && message.hasOwnProperty("players")) { if (!$util.isObject(message.players)) return "players: object expected"; @@ -3396,9 +3363,6 @@ $root.treasurehunterx = (function() { } } } - if (message.sentAt != null && message.hasOwnProperty("sentAt")) - if (!$util.isInteger(message.sentAt) && !(message.sentAt && $util.isInteger(message.sentAt.low) && $util.isInteger(message.sentAt.high))) - return "sentAt: integer|Long expected"; if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos")) if (!$util.isInteger(message.countdownNanos) && !(message.countdownNanos && $util.isInteger(message.countdownNanos.low) && $util.isInteger(message.countdownNanos.high))) return "countdownNanos: integer|Long expected"; @@ -3433,8 +3397,6 @@ $root.treasurehunterx = (function() { var message = new $root.treasurehunterx.RoomDownsyncFrame(); if (object.id != null) message.id = object.id | 0; - if (object.refFrameId != null) - message.refFrameId = object.refFrameId | 0; if (object.players) { if (typeof object.players !== "object") throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected"); @@ -3445,15 +3407,6 @@ $root.treasurehunterx = (function() { message.players[keys[i]] = $root.treasurehunterx.Player.fromObject(object.players[keys[i]]); } } - if (object.sentAt != null) - if ($util.Long) - (message.sentAt = $util.Long.fromValue(object.sentAt)).unsigned = false; - else if (typeof object.sentAt === "string") - message.sentAt = parseInt(object.sentAt, 10); - else if (typeof object.sentAt === "number") - message.sentAt = object.sentAt; - else if (typeof object.sentAt === "object") - message.sentAt = new $util.LongBits(object.sentAt.low >>> 0, object.sentAt.high >>> 0).toNumber(); if (object.countdownNanos != null) if ($util.Long) (message.countdownNanos = $util.Long.fromValue(object.countdownNanos)).unsigned = false; @@ -3495,12 +3448,6 @@ $root.treasurehunterx = (function() { } if (options.defaults) { object.id = 0; - object.refFrameId = 0; - if ($util.Long) { - var long = new $util.Long(0, 0, false); - object.sentAt = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.sentAt = options.longs === String ? "0" : 0; if ($util.Long) { var long = new $util.Long(0, 0, false); object.countdownNanos = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; @@ -3509,19 +3456,12 @@ $root.treasurehunterx = (function() { } if (message.id != null && message.hasOwnProperty("id")) object.id = message.id; - if (message.refFrameId != null && message.hasOwnProperty("refFrameId")) - object.refFrameId = message.refFrameId; var keys2; if (message.players && (keys2 = Object.keys(message.players)).length) { object.players = {}; for (var j = 0; j < keys2.length; ++j) object.players[keys2[j]] = $root.treasurehunterx.Player.toObject(message.players[keys2[j]], options); } - if (message.sentAt != null && message.hasOwnProperty("sentAt")) - if (typeof message.sentAt === "number") - object.sentAt = options.longs === String ? String(message.sentAt) : message.sentAt; - else - object.sentAt = options.longs === String ? $util.Long.prototype.toString.call(message.sentAt) : options.longs === Number ? new $util.LongBits(message.sentAt.low >>> 0, message.sentAt.high >>> 0).toNumber() : message.sentAt; if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos")) if (typeof message.countdownNanos === "number") object.countdownNanos = options.longs === String ? String(message.countdownNanos) : message.countdownNanos; From 4d1de44ee5e202fb679b9dd7a5a87f3c3b845a9c Mon Sep 17 00:00:00 2001 From: genxium Date: Sun, 2 Oct 2022 11:33:40 +0800 Subject: [PATCH 08/13] Updated config uniformity. --- battle_srv/models/pb_type_convert.go | 30 +- battle_srv/models/room.go | 130 +++--- battle_srv/models/room_heap_manager.go | 39 +- .../pb_output/room_downsync_frame.pb.go | 428 ++++++++++------- battle_srv/ws/serve.go | 24 +- frontend/assets/plugin_scripts/constants.js | 1 - .../pbfiles/room_downsync_frame.proto | 27 +- frontend/assets/scenes/login.fire | 2 +- frontend/assets/scripts/Map.js | 54 +-- ...om_downsync_frame_proto_bundle.forcemsg.js | 439 ++++++++++++------ 10 files changed, 702 insertions(+), 472 deletions(-) diff --git a/battle_srv/models/pb_type_convert.go b/battle_srv/models/pb_type_convert.go index 2b64bfd..562ff44 100644 --- a/battle_srv/models/pb_type_convert.go +++ b/battle_srv/models/pb_type_convert.go @@ -33,6 +33,14 @@ func toPbVec2DList(modelInstance *Vec2DList) *pb.Vec2DList { return toRet } +func ToPbVec2DListMap(modelInstances map[string]*Vec2DList) map[string]*pb.Vec2DList { + toRet := make(map[string]*pb.Vec2DList, len(modelInstances)) + for k, v := range modelInstances { + toRet[k] = toPbVec2DList(v) + } + return toRet +} + func toPbPolygon2DList(modelInstance *Polygon2DList) *pb.Polygon2DList { toRet := &pb.Polygon2DList{ Polygon2DList: make([]*pb.Polygon2D, len(*modelInstance)), @@ -43,24 +51,10 @@ func toPbPolygon2DList(modelInstance *Polygon2DList) *pb.Polygon2DList { return toRet } -func ToPbStrToBattleColliderInfo(intervalToPing int32, willKickIfInactiveFor int32, boundRoomId int32, stageName string, modelInstance1 StrToVec2DListMap, modelInstance2 StrToPolygon2DListMap, stageDiscreteW int32, stageDiscreteH int32, stageTileW int32, stageTileH int32) *pb.BattleColliderInfo { - toRet := &pb.BattleColliderInfo{ - IntervalToPing: intervalToPing, - WillKickIfInactiveFor: willKickIfInactiveFor, - BoundRoomId: boundRoomId, - StageName: stageName, - StrToVec2DListMap: make(map[string]*pb.Vec2DList, 0), - StrToPolygon2DListMap: make(map[string]*pb.Polygon2DList, 0), - StageDiscreteW: stageDiscreteW, - StageDiscreteH: stageDiscreteH, - StageTileW: stageTileW, - StageTileH: stageTileH, - } - for k, v := range modelInstance1 { - toRet.StrToVec2DListMap[k] = toPbVec2DList(v) - } - for k, v := range modelInstance2 { - toRet.StrToPolygon2DListMap[k] = toPbPolygon2DList(v) +func ToPbPolygon2DListMap(modelInstances map[string]*Polygon2DList) map[string]*pb.Polygon2DList { + toRet := make(map[string]*pb.Polygon2DList, len(modelInstances)) + for k, v := range modelInstances { + toRet[k] = toPbPolygon2DList(v) } return toRet } diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 0a972db..de158fa 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -152,8 +152,10 @@ type Room struct { Index int RenderFrameId int32 CurDynamicsRenderFrameId int32 // [WARNING] The dynamics of backend is ALWAYS MOVING FORWARD BY ALL-CONFIRMED INPUTFRAMES (either by upsync or forced), i.e. no rollback - ServerFPS int32 + ServerFps int32 BattleDurationNanos int64 + InputFrameUpsyncDelayTolerance int32 + MaxChasingRenderFramesPerUpdate int32 EffectivePlayerCount int32 DismissalWaitGroup sync.WaitGroup Barriers map[int32]*Barrier @@ -348,9 +350,10 @@ func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 { } func (pR *Room) AllPlayerInputsBufferString(allDetails bool) string { - s := make([]string, 0) - s = append(s, fmt.Sprintf("{renderFrameId: %v, stInputFrameId: %v, edInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v, lastAllConfirmedInputFrameId: %v}", pR.RenderFrameId, pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId)) if allDetails { + // Appending of the array of strings can be very SLOW due to on-demand heap allocation! Use this printing with caution. + s := make([]string, 0) + s = append(s, fmt.Sprintf("{renderFrameId: %v, stInputFrameId: %v, edInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v, lastAllConfirmedInputFrameId: %v}", pR.RenderFrameId, pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId)) for playerId, player := range pR.Players { s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId)) } @@ -362,9 +365,11 @@ func (pR *Room) AllPlayerInputsBufferString(allDetails bool) string { f := tmp.(*pb.InputFrameDownsync) s = append(s, fmt.Sprintf("{inputFrameId: %v, inputList: %v, confirmedList: %v}", f.InputFrameId, f.InputList, f.ConfirmedList)) } - } - return strings.Join(s, "\n") + return strings.Join(s, "\n") + } else { + return fmt.Sprintf("{renderFrameId: %d, stInputFrameId: %d, edInputFrameId: %d, lastAllConfirmedInputFrameIdWithChange: %d, lastAllConfirmedInputFrameId: %d}", pR.RenderFrameId, pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId) + } } func (pR *Room) StartBattle() { @@ -374,7 +379,7 @@ func (pR *Room) StartBattle() { } // Always instantiates a new channel and let the old one die out due to not being retained by any root reference. - nanosPerFrame := 1000000000 / int64(pR.ServerFPS) + nanosPerFrame := 1000000000 / int64(pR.ServerFps) pR.RenderFrameId = 0 // Initialize the "collisionSys" as well as "RenderFrameBuffer" @@ -397,6 +402,7 @@ func (pR *Room) StartBattle() { defer func() { if r := recover(); r != nil { Logger.Error("battleMainLoop, recovery spot#1, recovered from: ", zap.Any("roomId", pR.Id), zap.Any("panic", r)) + pR.StopBattleForSettlement() } Logger.Info("The `battleMainLoop` is stopped for:", zap.Any("roomId", pR.Id)) pR.onBattleStoppedForSettlement() @@ -432,10 +438,6 @@ func (pR *Room) StartBattle() { dynamicsStartedAt := utils.UnixtimeNano() // Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId" nextDynamicsRenderFrameId := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames) - if nextDynamicsRenderFrameId > pR.RenderFrameId { - // [WARNING] DON'T apply dynamics too fast, otherwise upon DOWNSYNC_MSG_ACT_FORCED_RESYNC the frontend would resync itself to a "too advanced frontend.renderFrameId", and then start upsyncing "too advanced inputFrameId". - nextDynamicsRenderFrameId = pR.RenderFrameId - } Logger.Debug(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, LastAllConfirmedInputFrameId=%v, InputDelayFrames=%v, nextDynamicsRenderFrameId=%v", pR.Id, pR.RenderFrameId, pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames, nextDynamicsRenderFrameId)) pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId) dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt @@ -480,7 +482,12 @@ func (pR *Room) StartBattle() { indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) if 0 < (unconfirmedMask & joinMask) { - refRenderFrame := pR.RenderFrameBuffer.GetByFrameId(pR.CurDynamicsRenderFrameId).(*pb.RoomDownsyncFrame) + refRenderFrameId := pR.CurDynamicsRenderFrameId + if refRenderFrameId > pR.RenderFrameId { + // [WARNING] To avoid that in good network condition the frontend resyncs itself to a "too advanced frontend.renderFrameId", and then starts upsyncing "too advanced inputFrameId". + refRenderFrameId = pR.RenderFrameId + } + refRenderFrame := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId).(*pb.RoomDownsyncFrame) pR.sendSafely(refRenderFrame, toSendInputFrames, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId) } else { if 0 >= len(toSendInputFrames) { @@ -488,9 +495,9 @@ func (pR *Room) StartBattle() { } pR.sendSafely(nil, toSendInputFrames, DOWNSYNC_MSG_ACT_INPUT_BATCH, playerId) } - if -1 != debugSendingInputFrameId { - Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) - } + if -1 != debugSendingInputFrameId { + Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) + } atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) } } @@ -504,7 +511,7 @@ func (pR *Room) StartBattle() { f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync) if pR.inputFrameIdDebuggable(f.InputFrameId) { // Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked - Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) + Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) } } @@ -681,7 +688,7 @@ func (pR *Room) onBattlePrepare(cb BattleStartCbType) { CountdownNanos: pR.BattleDurationNanos, } - Logger.Info("Sending out frame for RoomBattleState.PREPARE ", zap.Any("battleReadyToStartFrame", battleReadyToStartFrame)) + Logger.Info("Sending out frame for RoomBattleState.PREPARE:", zap.Any("battleReadyToStartFrame", battleReadyToStartFrame)) for _, player := range pR.Players { pR.sendSafely(battleReadyToStartFrame, nil, DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START, player.Id) } @@ -738,10 +745,10 @@ func (pR *Room) Dismiss() { } pR.DismissalWaitGroup.Wait() } - pR.onDismissed() + pR.OnDismissed() } -func (pR *Room) onDismissed() { +func (pR *Room) OnDismissed() { // Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference. pR.Players = make(map[int32]*Player) @@ -749,16 +756,25 @@ func (pR *Room) onDismissed() { pR.CollisionSysMap = make(map[int32]*resolv.Object) pR.PlayerDownsyncSessionDict = make(map[int32]*websocket.Conn) pR.PlayerSignalToCloseDict = make(map[int32]SignalToCloseConnCbType) + pR.JoinIndexBooleanArr = make([]bool, pR.Capacity) + pR.Barriers = make(map[int32]*Barrier) + pR.AllPlayerInputsBuffer = NewRingBuffer(1024) + pR.RenderFrameBuffer = NewRingBuffer(1024) pR.LastAllConfirmedInputFrameId = -1 pR.LastAllConfirmedInputFrameIdWithChange = -1 pR.LastAllConfirmedInputList = make([]uint64, pR.Capacity) - for indice, _ := range pR.JoinIndexBooleanArr { - pR.JoinIndexBooleanArr[indice] = false - } - pR.AllPlayerInputsBuffer = NewRingBuffer(1024) - pR.RenderFrameBuffer = NewRingBuffer(1024) + pR.RenderFrameId = 0 + pR.CurDynamicsRenderFrameId = 0 + pR.InputDelayFrames = 8 + pR.NstDelayFrames = 32 + pR.InputScaleFrames = uint32(2) + pR.ServerFps = 60 + pR.RollbackEstimatedDt = float64(1.0) / float64(pR.ServerFps) + pR.BattleDurationNanos = int64(30 * 1000 * 1000 * 1000) + pR.InputFrameUpsyncDelayTolerance = 2 + pR.MaxChasingRenderFramesPerUpdate = 10 pR.ChooseStage() pR.EffectivePlayerCount = 0 @@ -917,7 +933,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { } } - // Broadcast added or readded player info to all players in the same room + // Broadcast added or readded player info to all players in the same room for _, eachPlayer := range pR.Players { /* [WARNING] @@ -925,23 +941,23 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { By making use of the sequential nature of each ws session, all later "RoomDownsyncFrame"s generated after `pRoom.StartBattle()` will be put behind this `playerAckedFrame`. */ - switch targetPlayer.BattleState { - case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK: - playerAckedFrame := &pb.RoomDownsyncFrame{ - Id: pR.RenderFrameId, - Players: toPbPlayers(pR.Players), - PlayerMetas: playerMetas, - } - pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED, eachPlayer.Id) - case PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK: - playerAckedFrame := &pb.RoomDownsyncFrame{ - Id: pR.RenderFrameId, - Players: toPbPlayers(pR.Players), - PlayerMetas: playerMetas, - } - pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED, eachPlayer.Id) - default: - } + switch targetPlayer.BattleState { + case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK: + playerAckedFrame := &pb.RoomDownsyncFrame{ + Id: pR.RenderFrameId, + Players: toPbPlayers(pR.Players), + PlayerMetas: playerMetas, + } + pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED, eachPlayer.Id) + case PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK: + playerAckedFrame := &pb.RoomDownsyncFrame{ + Id: pR.RenderFrameId, + Players: toPbPlayers(pR.Players), + PlayerMetas: playerMetas, + } + pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED, eachPlayer.Id) + default: + } } targetPlayer.BattleState = PlayerBattleStateIns.ACTIVE @@ -1038,6 +1054,11 @@ func (pR *Room) forceConfirmationIfApplicable() uint64 { } inputFrameId2 := pR.ConvertToInputFrameId(renderFrameId1, 0) // The inputFrame to force confirmation (if necessary) + if inputFrameId2 < pR.LastAllConfirmedInputFrameId { + // No need to force confirmation, the inputFrames already arrived + Logger.Debug(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v[type#1], no need to force confirmation of it", inputFrameId2, pR.Id)) + return 0 + } tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId2) if nil == tmp { panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! AllPlayerInputsBuffer=%v", inputFrameId2, pR.Id, pR.AllPlayerInputsBufferString(false))) @@ -1047,7 +1068,8 @@ func (pR *Room) forceConfirmationIfApplicable() uint64 { totPlayerCnt := uint32(pR.Capacity) allConfirmedMask := uint64((1 << totPlayerCnt) - 1) if swapped := atomic.CompareAndSwapUint64(&(inputFrame2.ConfirmedList), allConfirmedMask, allConfirmedMask); swapped { - Logger.Info(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v, no need to force confirmation of it", inputFrameId2, pR.Id)) + // This could happen if the frontend upsync command arrived between type#1 and type#2 checks. + Logger.Debug(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v[type#2], no need to force confirmation of it", inputFrameId2, pR.Id)) return 0 } @@ -1093,18 +1115,18 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende dx := baseChange * float64(decodedInput[0]) dy := baseChange * float64(decodedInput[1]) /* - // The collision lib seems very slow at worst cases, omitting for now - collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex - playerCollider := pR.CollisionSysMap[collisionPlayerIndex] - if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { - changeWithCollision := collision.ContactWithObject(collision.Objects[0]) - dx = changeWithCollision.X() - dy = changeWithCollision.Y() - } - playerCollider.X += dx - playerCollider.Y += dy - // Update in "collision space" - playerCollider.Update() + // The collision lib seems very slow at worst cases, omitting for now + collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex + playerCollider := pR.CollisionSysMap[collisionPlayerIndex] + if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { + changeWithCollision := collision.ContactWithObject(collision.Objects[0]) + dx = changeWithCollision.X() + dy = changeWithCollision.Y() + } + playerCollider.X += dx + playerCollider.Y += dy + // Update in "collision space" + playerCollider.Update() */ player.Dir.Dx = decodedInput[0] diff --git a/battle_srv/models/room_heap_manager.go b/battle_srv/models/room_heap_manager.go index bd33264..d788f0a 100644 --- a/battle_srv/models/room_heap_manager.go +++ b/battle_srv/models/room_heap_manager.go @@ -3,8 +3,6 @@ package models import ( "container/heap" "fmt" - "github.com/gorilla/websocket" - "github.com/solarlune/resolv" "go.uber.org/zap" . "server/common" "sync" @@ -93,42 +91,13 @@ func InitRoomHeapManager() { for i := 0; i < initialCountOfRooms; i++ { roomCapacity := 2 - joinIndexBooleanArr := make([]bool, roomCapacity) - for index, _ := range joinIndexBooleanArr { - joinIndexBooleanArr[index] = false - } - currentRoomBattleState := RoomBattleStateIns.IDLE pq[i] = &Room{ - Id: int32(i + 1), - Players: make(map[int32]*Player), - PlayersArr: make([]*Player, roomCapacity), - CollisionSysMap: make(map[int32]*resolv.Object), - PlayerDownsyncSessionDict: make(map[int32]*websocket.Conn), - PlayerSignalToCloseDict: make(map[int32]SignalToCloseConnCbType), - Capacity: roomCapacity, - Score: calRoomScore(0, roomCapacity, currentRoomBattleState), - State: currentRoomBattleState, - Index: i, - RenderFrameId: 0, - CurDynamicsRenderFrameId: 0, - EffectivePlayerCount: 0, - //BattleDurationNanos: int64(5 * 1000 * 1000 * 1000), - BattleDurationNanos: int64(30 * 1000 * 1000 * 1000), - ServerFPS: 60, - Barriers: make(map[int32]*Barrier), - AllPlayerInputsBuffer: NewRingBuffer(1024), - RenderFrameBuffer: NewRingBuffer(1024), - LastAllConfirmedInputFrameId: -1, - LastAllConfirmedInputFrameIdWithChange: -1, - LastAllConfirmedInputList: make([]uint64, roomCapacity), - InputDelayFrames: 4, - NstDelayFrames: 8, - InputScaleFrames: 2, - JoinIndexBooleanArr: joinIndexBooleanArr, - RollbackEstimatedDt: float64(1.0) / 60, + Id: int32(i + 1), + Capacity: roomCapacity, + Index: i, } roomMap[pq[i].Id] = pq[i] - pq[i].ChooseStage() + pq[i].OnDismissed() } heap.Init(&pq) RoomHeapManagerIns = &pq diff --git a/battle_srv/pb_output/room_downsync_frame.pb.go b/battle_srv/pb_output/room_downsync_frame.pb.go index 808a826..794e483 100644 --- a/battle_srv/pb_output/room_downsync_frame.pb.go +++ b/battle_srv/pb_output/room_downsync_frame.pb.go @@ -284,16 +284,23 @@ type BattleColliderInfo struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - IntervalToPing int32 `protobuf:"varint,1,opt,name=intervalToPing,proto3" json:"intervalToPing,omitempty"` - WillKickIfInactiveFor int32 `protobuf:"varint,2,opt,name=willKickIfInactiveFor,proto3" json:"willKickIfInactiveFor,omitempty"` - BoundRoomId int32 `protobuf:"varint,3,opt,name=boundRoomId,proto3" json:"boundRoomId,omitempty"` - StageName string `protobuf:"bytes,4,opt,name=stageName,proto3" json:"stageName,omitempty"` - StrToVec2DListMap map[string]*Vec2DList `protobuf:"bytes,5,rep,name=strToVec2DListMap,proto3" json:"strToVec2DListMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - StrToPolygon2DListMap map[string]*Polygon2DList `protobuf:"bytes,6,rep,name=strToPolygon2DListMap,proto3" json:"strToPolygon2DListMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - StageDiscreteW int32 `protobuf:"varint,7,opt,name=StageDiscreteW,proto3" json:"StageDiscreteW,omitempty"` - StageDiscreteH int32 `protobuf:"varint,8,opt,name=StageDiscreteH,proto3" json:"StageDiscreteH,omitempty"` - StageTileW int32 `protobuf:"varint,9,opt,name=StageTileW,proto3" json:"StageTileW,omitempty"` - StageTileH int32 `protobuf:"varint,10,opt,name=StageTileH,proto3" json:"StageTileH,omitempty"` + StageName string `protobuf:"bytes,1,opt,name=stageName,proto3" json:"stageName,omitempty"` + StrToVec2DListMap map[string]*Vec2DList `protobuf:"bytes,2,rep,name=strToVec2DListMap,proto3" json:"strToVec2DListMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + StrToPolygon2DListMap map[string]*Polygon2DList `protobuf:"bytes,3,rep,name=strToPolygon2DListMap,proto3" json:"strToPolygon2DListMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + StageDiscreteW int32 `protobuf:"varint,4,opt,name=stageDiscreteW,proto3" json:"stageDiscreteW,omitempty"` + StageDiscreteH int32 `protobuf:"varint,5,opt,name=stageDiscreteH,proto3" json:"stageDiscreteH,omitempty"` + StageTileW int32 `protobuf:"varint,6,opt,name=stageTileW,proto3" json:"stageTileW,omitempty"` + StageTileH int32 `protobuf:"varint,7,opt,name=stageTileH,proto3" json:"stageTileH,omitempty"` + IntervalToPing int32 `protobuf:"varint,8,opt,name=intervalToPing,proto3" json:"intervalToPing,omitempty"` + WillKickIfInactiveFor int32 `protobuf:"varint,9,opt,name=willKickIfInactiveFor,proto3" json:"willKickIfInactiveFor,omitempty"` + BoundRoomId int32 `protobuf:"varint,10,opt,name=boundRoomId,proto3" json:"boundRoomId,omitempty"` + BattleDurationNanos int64 `protobuf:"varint,11,opt,name=battleDurationNanos,proto3" json:"battleDurationNanos,omitempty"` + ServerFps int32 `protobuf:"varint,12,opt,name=serverFps,proto3" json:"serverFps,omitempty"` + InputDelayFrames int32 `protobuf:"varint,13,opt,name=inputDelayFrames,proto3" json:"inputDelayFrames,omitempty"` + InputScaleFrames uint32 `protobuf:"varint,14,opt,name=inputScaleFrames,proto3" json:"inputScaleFrames,omitempty"` + NstDelayFrames int32 `protobuf:"varint,15,opt,name=nstDelayFrames,proto3" json:"nstDelayFrames,omitempty"` + InputFrameUpsyncDelayTolerance int32 `protobuf:"varint,16,opt,name=inputFrameUpsyncDelayTolerance,proto3" json:"inputFrameUpsyncDelayTolerance,omitempty"` + MaxChasingRenderFramesPerUpdate int32 `protobuf:"varint,17,opt,name=maxChasingRenderFramesPerUpdate,proto3" json:"maxChasingRenderFramesPerUpdate,omitempty"` } func (x *BattleColliderInfo) Reset() { @@ -328,27 +335,6 @@ func (*BattleColliderInfo) Descriptor() ([]byte, []int) { return file_room_downsync_frame_proto_rawDescGZIP(), []int{5} } -func (x *BattleColliderInfo) GetIntervalToPing() int32 { - if x != nil { - return x.IntervalToPing - } - return 0 -} - -func (x *BattleColliderInfo) GetWillKickIfInactiveFor() int32 { - if x != nil { - return x.WillKickIfInactiveFor - } - return 0 -} - -func (x *BattleColliderInfo) GetBoundRoomId() int32 { - if x != nil { - return x.BoundRoomId - } - return 0 -} - func (x *BattleColliderInfo) GetStageName() string { if x != nil { return x.StageName @@ -398,6 +384,76 @@ func (x *BattleColliderInfo) GetStageTileH() int32 { return 0 } +func (x *BattleColliderInfo) GetIntervalToPing() int32 { + if x != nil { + return x.IntervalToPing + } + return 0 +} + +func (x *BattleColliderInfo) GetWillKickIfInactiveFor() int32 { + if x != nil { + return x.WillKickIfInactiveFor + } + return 0 +} + +func (x *BattleColliderInfo) GetBoundRoomId() int32 { + if x != nil { + return x.BoundRoomId + } + return 0 +} + +func (x *BattleColliderInfo) GetBattleDurationNanos() int64 { + if x != nil { + return x.BattleDurationNanos + } + return 0 +} + +func (x *BattleColliderInfo) GetServerFps() int32 { + if x != nil { + return x.ServerFps + } + return 0 +} + +func (x *BattleColliderInfo) GetInputDelayFrames() int32 { + if x != nil { + return x.InputDelayFrames + } + return 0 +} + +func (x *BattleColliderInfo) GetInputScaleFrames() uint32 { + if x != nil { + return x.InputScaleFrames + } + return 0 +} + +func (x *BattleColliderInfo) GetNstDelayFrames() int32 { + if x != nil { + return x.NstDelayFrames + } + return 0 +} + +func (x *BattleColliderInfo) GetInputFrameUpsyncDelayTolerance() int32 { + if x != nil { + return x.InputFrameUpsyncDelayTolerance + } + return 0 +} + +func (x *BattleColliderInfo) GetMaxChasingRenderFramesPerUpdate() int32 { + if x != nil { + return x.MaxChasingRenderFramesPerUpdate + } + return 0 +} + type Player struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1049,163 +1105,185 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x52, 0x0d, 0x70, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, - 0x74, 0x22, 0xee, 0x05, 0x0a, 0x12, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, - 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x54, 0x6f, 0x50, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x54, 0x6f, 0x50, 0x69, 0x6e, 0x67, - 0x12, 0x34, 0x0a, 0x15, 0x77, 0x69, 0x6c, 0x6c, 0x4b, 0x69, 0x63, 0x6b, 0x49, 0x66, 0x49, 0x6e, - 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x15, 0x77, 0x69, 0x6c, 0x6c, 0x4b, 0x69, 0x63, 0x6b, 0x49, 0x66, 0x49, 0x6e, 0x61, 0x63, 0x74, - 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, - 0x6f, 0x6f, 0x6d, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x52, 0x6f, 0x6f, 0x6d, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x67, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x22, 0xd0, 0x08, 0x0a, 0x12, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, + 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x67, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x68, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x54, 0x6f, 0x56, - 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x72, 0x54, 0x6f, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x73, 0x74, 0x72, 0x54, 0x6f, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x12, 0x74, 0x0a, 0x15, 0x73, 0x74, 0x72, 0x54, 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, - 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x72, 0x54, 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x73, 0x74, 0x72, 0x54, 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, - 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x12, 0x26, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x44, - 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x57, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, - 0x53, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x57, 0x12, 0x26, - 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x48, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, - 0x63, 0x72, 0x65, 0x74, 0x65, 0x48, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x67, 0x65, 0x54, - 0x69, 0x6c, 0x65, 0x57, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x53, 0x74, 0x61, 0x67, - 0x65, 0x54, 0x69, 0x6c, 0x65, 0x57, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x67, 0x65, 0x54, - 0x69, 0x6c, 0x65, 0x48, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x53, 0x74, 0x61, 0x67, - 0x65, 0x54, 0x69, 0x6c, 0x65, 0x48, 0x1a, 0x60, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x54, 0x6f, 0x56, - 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x78, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x68, 0x0a, 0x1a, 0x53, 0x74, 0x72, 0x54, - 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, - 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, - 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, - 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x96, 0x02, 0x0a, 0x06, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x0c, 0x0a, - 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79, 0x12, 0x2c, 0x0a, 0x03, 0x64, 0x69, 0x72, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, - 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x12, 0x20, 0x0a, - 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x2c, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, 0x4d, 0x69, - 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, - 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x12, 0x14, 0x0a, - 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x63, - 0x6f, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x12, 0x1c, 0x0a, - 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x88, 0x01, 0x0a, 0x0a, - 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69, - 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, - 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, - 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x22, 0x7c, - 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, - 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, - 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f, - 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, - 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9f, 0x03, 0x0a, 0x11, 0x52, 0x6f, - 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, + 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, + 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x57, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, + 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x57, 0x12, 0x26, + 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x48, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, + 0x63, 0x72, 0x65, 0x74, 0x65, 0x48, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, + 0x69, 0x6c, 0x65, 0x57, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x67, + 0x65, 0x54, 0x69, 0x6c, 0x65, 0x57, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, + 0x69, 0x6c, 0x65, 0x48, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x67, + 0x65, 0x54, 0x69, 0x6c, 0x65, 0x48, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x54, 0x6f, 0x50, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x54, 0x6f, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x34, + 0x0a, 0x15, 0x77, 0x69, 0x6c, 0x6c, 0x4b, 0x69, 0x63, 0x6b, 0x49, 0x66, 0x49, 0x6e, 0x61, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x77, + 0x69, 0x6c, 0x6c, 0x4b, 0x69, 0x63, 0x6b, 0x49, 0x66, 0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x46, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x6f, 0x6f, + 0x6d, 0x49, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x52, 0x6f, 0x6f, 0x6d, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x13, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x46, 0x70, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x46, 0x70, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x44, + 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, + 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, + 0x0a, 0x0e, 0x6e, 0x73, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6e, 0x73, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, + 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x46, 0x0a, 0x1e, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x54, + 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1e, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, + 0x44, 0x65, 0x6c, 0x61, 0x79, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x48, + 0x0a, 0x1f, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1f, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x73, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, + 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x60, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x54, + 0x6f, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x68, 0x0a, 0x1a, 0x53, 0x74, + 0x72, 0x54, 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, + 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6f, 0x6c, 0x79, + 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x96, 0x02, 0x0a, 0x06, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, - 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, - 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, - 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, - 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, - 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, - 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xca, 0x02, 0x0a, 0x05, - 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, - 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, - 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, - 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, - 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x2e, 0x0a, - 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, - 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x57, 0x0a, - 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, - 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, - 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, - 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, - 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, - 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, - 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x06, 0x57, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, - 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, 0x63, 0x68, 0x6f, - 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x03, 0x72, 0x64, 0x66, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, - 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, - 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x72, 0x64, 0x66, 0x12, - 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, - 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, - 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3f, - 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, - 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x42, - 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, + 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79, 0x12, 0x2c, 0x0a, 0x03, 0x64, + 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, + 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x12, + 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, + 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6c, 0x61, + 0x73, 0x74, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x88, 0x01, + 0x0a, 0x0a, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, + 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, + 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, + 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, + 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, + 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, + 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, + 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9f, 0x03, 0x0a, 0x11, + 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, + 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0e, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, + 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, + 0x74, 0x61, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, + 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, + 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, + 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xca, 0x02, + 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, + 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6a, + 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, + 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x6b, + 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, + 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x63, 0x6b, + 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, + 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, + 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, + 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, + 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, + 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, + 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x06, 0x57, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, + 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, 0x63, + 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x03, 0x72, + 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, + 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x72, 0x64, + 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, + 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, + 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, + 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/battle_srv/ws/serve.go b/battle_srv/ws/serve.go index 0f3e9fb..a828025 100644 --- a/battle_srv/ws/serve.go +++ b/battle_srv/ws/serve.go @@ -240,13 +240,33 @@ func Serve(c *gin.Context) { }) }() - playerBattleColliderInfo := models.ToPbStrToBattleColliderInfo(int32(Constants.Ws.IntervalToPing), int32(Constants.Ws.WillKickIfInactiveFor), pRoom.Id, pRoom.StageName, pRoom.RawBattleStrToVec2DListMap, pRoom.RawBattleStrToPolygon2DListMap, pRoom.StageDiscreteW, pRoom.StageDiscreteH, pRoom.StageTileW, pRoom.StageTileH) + // Construct "battleColliderInfo" to downsync + bciFrame := &pb.BattleColliderInfo{ + BoundRoomId: pRoom.Id, + StageName: pRoom.StageName, + StrToVec2DListMap: models.ToPbVec2DListMap(pRoom.RawBattleStrToVec2DListMap), + StrToPolygon2DListMap: models.ToPbPolygon2DListMap(pRoom.RawBattleStrToPolygon2DListMap), + StageDiscreteW: pRoom.StageDiscreteW, + StageDiscreteH: pRoom.StageDiscreteH, + StageTileW: pRoom.StageTileW, + StageTileH: pRoom.StageTileH, + + IntervalToPing: int32(Constants.Ws.IntervalToPing), + WillKickIfInactiveFor: int32(Constants.Ws.WillKickIfInactiveFor), + BattleDurationNanos: pRoom.BattleDurationNanos, + ServerFps: pRoom.ServerFps, + InputDelayFrames: pRoom.InputDelayFrames, + InputScaleFrames: pRoom.InputScaleFrames, + NstDelayFrames: pRoom.NstDelayFrames, + InputFrameUpsyncDelayTolerance: pRoom.InputFrameUpsyncDelayTolerance, + MaxChasingRenderFramesPerUpdate: pRoom.MaxChasingRenderFramesPerUpdate, + } resp := &pb.WsResp{ Ret: int32(Constants.RetCode.Ok), EchoedMsgId: int32(0), Act: models.DOWNSYNC_MSG_ACT_HB_REQ, - BciFrame: playerBattleColliderInfo, + BciFrame: bciFrame, } // Logger.Info("Sending downsync HeartbeatRequirements:", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("resp", resp)) diff --git a/frontend/assets/plugin_scripts/constants.js b/frontend/assets/plugin_scripts/constants.js index fb3d975..c615bab 100644 --- a/frontend/assets/plugin_scripts/constants.js +++ b/frontend/assets/plugin_scripts/constants.js @@ -1,6 +1,5 @@ "use strict"; - var _ROUTE_PATH; function _defineProperty(obj, key, value) { diff --git a/frontend/assets/resources/pbfiles/room_downsync_frame.proto b/frontend/assets/resources/pbfiles/room_downsync_frame.proto index e54f010..d50ebe4 100644 --- a/frontend/assets/resources/pbfiles/room_downsync_frame.proto +++ b/frontend/assets/resources/pbfiles/room_downsync_frame.proto @@ -27,17 +27,24 @@ message Polygon2DList { } message BattleColliderInfo { - int32 intervalToPing = 1; - int32 willKickIfInactiveFor = 2; - int32 boundRoomId = 3; + string stageName = 1; + map strToVec2DListMap = 2; + map strToPolygon2DListMap = 3; + int32 stageDiscreteW = 4; + int32 stageDiscreteH = 5; + int32 stageTileW = 6; + int32 stageTileH = 7; - string stageName = 4; - map strToVec2DListMap = 5; - map strToPolygon2DListMap = 6; - int32 StageDiscreteW = 7; - int32 StageDiscreteH = 8; - int32 StageTileW = 9; - int32 StageTileH = 10; + int32 intervalToPing = 8; + int32 willKickIfInactiveFor = 9; + int32 boundRoomId = 10; + int64 battleDurationNanos = 11; + int32 serverFps = 12; + int32 inputDelayFrames = 13; + uint32 inputScaleFrames = 14; + int32 nstDelayFrames = 15; + int32 inputFrameUpsyncDelayTolerance = 16; + int32 maxChasingRenderFramesPerUpdate = 17; } message Player { diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index a8af76d..6324f99 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 210.43837950571782, + 209.57814771583418, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 35d0d5a..739b185 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -42,18 +42,6 @@ cc.Class({ type: cc.Prefab, default: null, }, - treasurePrefab: { - type: cc.Prefab, - default: null, - }, - trapPrefab: { - type: cc.Prefab, - default: null, - }, - speedShoePrefab: { - type: cc.Prefab, - default: null, - }, polygonBoundaryBarrierPrefab: { type: cc.Prefab, default: null, @@ -82,10 +70,6 @@ cc.Class({ type: cc.Label, default: null }, - trapBulletPrefab: { - type: cc.Prefab, - default: null - }, resultPanelPrefab: { type: cc.Prefab, default: null @@ -106,10 +90,6 @@ cc.Class({ type: cc.Prefab, default: null }, - guardTowerPrefab: { - type: cc.Prefab, - default: null - }, forceBigEndianFloatingNumDecoding: { default: false, }, @@ -117,22 +97,6 @@ cc.Class({ type: cc.TiledMap, default: null }, - rollbackEstimatedDt: { - type: cc.Float, - default: 1.0/60 - }, - rollbackEstimatedDtMillis: { - type: cc.Float, - default: 1000.0/60 - }, - rollbackEstimatedDtToleranceMillis: { - type: cc.Float, - default: 1.0/60 - }, - maxChasingRenderFramesPerUpdate: { - type: cc.Integer, - default: 10 - }, }, _inputFrameIdDebuggable(inputFrameId) { @@ -352,12 +316,8 @@ cc.Class({ self.renderFrameId = 0; // After battle started self.lastAllConfirmedRenderFrameId = -1; self.lastAllConfirmedInputFrameId = -1; - self.chaserRenderFrameId = -1; // at any moment, "lastAllConfirmedRenderFrameId <= chaserRenderFrameId <= renderFrameId", but "chaserRenderFrameId" would fluctuate according to "handleInputFrameDownsyncBatch" - - self.inputDelayFrames = 4; - self.inputScaleFrames = 2; self.lastUpsyncInputFrameId = -1; - self.inputFrameUpsyncDelayTolerance = 2; + self.chaserRenderFrameId = -1; // at any moment, "lastAllConfirmedRenderFrameId <= chaserRenderFrameId <= renderFrameId", but "chaserRenderFrameId" would fluctuate according to "handleInputFrameDownsyncBatch" self.recentRenderCache = new RingBuffer(1024); @@ -455,10 +415,16 @@ cc.Class({ /** Init required prefab ended. */ - self.clientUpsyncFps = 60; - window.handleBattleColliderInfo = function(parsedBattleColliderInfo) { - self.battleColliderInfo = parsedBattleColliderInfo; + self.inputDelayFrames = parsedBattleColliderInfo.inputDelayFrames; + self.inputScaleFrames = parsedBattleColliderInfo.inputScaleFrames; + self.inputFrameUpsyncDelayTolerance = parsedBattleColliderInfo.inputFrameUpsyncDelayTolerance; + + self.rollbackEstimatedDt = 1.0/parsedBattleColliderInfo.serverFps; + self.rollbackEstimatedDtMillis = 1000.0*self.rollbackEstimatedDt; + self.rollbackEstimatedDtToleranceMillis = self.rollbackEstimatedDtMillis/1000.0; + self.maxChasingRenderFramesPerUpdate = parsedBattleColliderInfo.maxChasingRenderFramesPerUpdate; + const tiledMapIns = self.node.getComponent(cc.TiledMap); const fullPathOfTmxFile = cc.js.formatStr("map/%s/map", parsedBattleColliderInfo.stageName); diff --git a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js index b79c89c..de4a071 100644 --- a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js +++ b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js @@ -1179,16 +1179,23 @@ $root.treasurehunterx = (function() { * Properties of a BattleColliderInfo. * @memberof treasurehunterx * @interface IBattleColliderInfo - * @property {number|null} [intervalToPing] BattleColliderInfo intervalToPing - * @property {number|null} [willKickIfInactiveFor] BattleColliderInfo willKickIfInactiveFor - * @property {number|null} [boundRoomId] BattleColliderInfo boundRoomId * @property {string|null} [stageName] BattleColliderInfo stageName * @property {Object.|null} [strToVec2DListMap] BattleColliderInfo strToVec2DListMap * @property {Object.|null} [strToPolygon2DListMap] BattleColliderInfo strToPolygon2DListMap - * @property {number|null} [StageDiscreteW] BattleColliderInfo StageDiscreteW - * @property {number|null} [StageDiscreteH] BattleColliderInfo StageDiscreteH - * @property {number|null} [StageTileW] BattleColliderInfo StageTileW - * @property {number|null} [StageTileH] BattleColliderInfo StageTileH + * @property {number|null} [stageDiscreteW] BattleColliderInfo stageDiscreteW + * @property {number|null} [stageDiscreteH] BattleColliderInfo stageDiscreteH + * @property {number|null} [stageTileW] BattleColliderInfo stageTileW + * @property {number|null} [stageTileH] BattleColliderInfo stageTileH + * @property {number|null} [intervalToPing] BattleColliderInfo intervalToPing + * @property {number|null} [willKickIfInactiveFor] BattleColliderInfo willKickIfInactiveFor + * @property {number|null} [boundRoomId] BattleColliderInfo boundRoomId + * @property {number|Long|null} [battleDurationNanos] BattleColliderInfo battleDurationNanos + * @property {number|null} [serverFps] BattleColliderInfo serverFps + * @property {number|null} [inputDelayFrames] BattleColliderInfo inputDelayFrames + * @property {number|null} [inputScaleFrames] BattleColliderInfo inputScaleFrames + * @property {number|null} [nstDelayFrames] BattleColliderInfo nstDelayFrames + * @property {number|null} [inputFrameUpsyncDelayTolerance] BattleColliderInfo inputFrameUpsyncDelayTolerance + * @property {number|null} [maxChasingRenderFramesPerUpdate] BattleColliderInfo maxChasingRenderFramesPerUpdate */ /** @@ -1208,30 +1215,6 @@ $root.treasurehunterx = (function() { this[keys[i]] = properties[keys[i]]; } - /** - * BattleColliderInfo intervalToPing. - * @member {number} intervalToPing - * @memberof treasurehunterx.BattleColliderInfo - * @instance - */ - BattleColliderInfo.prototype.intervalToPing = 0; - - /** - * BattleColliderInfo willKickIfInactiveFor. - * @member {number} willKickIfInactiveFor - * @memberof treasurehunterx.BattleColliderInfo - * @instance - */ - BattleColliderInfo.prototype.willKickIfInactiveFor = 0; - - /** - * BattleColliderInfo boundRoomId. - * @member {number} boundRoomId - * @memberof treasurehunterx.BattleColliderInfo - * @instance - */ - BattleColliderInfo.prototype.boundRoomId = 0; - /** * BattleColliderInfo stageName. * @member {string} stageName @@ -1257,36 +1240,116 @@ $root.treasurehunterx = (function() { BattleColliderInfo.prototype.strToPolygon2DListMap = $util.emptyObject; /** - * BattleColliderInfo StageDiscreteW. - * @member {number} StageDiscreteW + * BattleColliderInfo stageDiscreteW. + * @member {number} stageDiscreteW * @memberof treasurehunterx.BattleColliderInfo * @instance */ - BattleColliderInfo.prototype.StageDiscreteW = 0; + BattleColliderInfo.prototype.stageDiscreteW = 0; /** - * BattleColliderInfo StageDiscreteH. - * @member {number} StageDiscreteH + * BattleColliderInfo stageDiscreteH. + * @member {number} stageDiscreteH * @memberof treasurehunterx.BattleColliderInfo * @instance */ - BattleColliderInfo.prototype.StageDiscreteH = 0; + BattleColliderInfo.prototype.stageDiscreteH = 0; /** - * BattleColliderInfo StageTileW. - * @member {number} StageTileW + * BattleColliderInfo stageTileW. + * @member {number} stageTileW * @memberof treasurehunterx.BattleColliderInfo * @instance */ - BattleColliderInfo.prototype.StageTileW = 0; + BattleColliderInfo.prototype.stageTileW = 0; /** - * BattleColliderInfo StageTileH. - * @member {number} StageTileH + * BattleColliderInfo stageTileH. + * @member {number} stageTileH * @memberof treasurehunterx.BattleColliderInfo * @instance */ - BattleColliderInfo.prototype.StageTileH = 0; + BattleColliderInfo.prototype.stageTileH = 0; + + /** + * BattleColliderInfo intervalToPing. + * @member {number} intervalToPing + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.intervalToPing = 0; + + /** + * BattleColliderInfo willKickIfInactiveFor. + * @member {number} willKickIfInactiveFor + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.willKickIfInactiveFor = 0; + + /** + * BattleColliderInfo boundRoomId. + * @member {number} boundRoomId + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.boundRoomId = 0; + + /** + * BattleColliderInfo battleDurationNanos. + * @member {number|Long} battleDurationNanos + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.battleDurationNanos = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * BattleColliderInfo serverFps. + * @member {number} serverFps + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.serverFps = 0; + + /** + * BattleColliderInfo inputDelayFrames. + * @member {number} inputDelayFrames + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.inputDelayFrames = 0; + + /** + * BattleColliderInfo inputScaleFrames. + * @member {number} inputScaleFrames + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.inputScaleFrames = 0; + + /** + * BattleColliderInfo nstDelayFrames. + * @member {number} nstDelayFrames + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.nstDelayFrames = 0; + + /** + * BattleColliderInfo inputFrameUpsyncDelayTolerance. + * @member {number} inputFrameUpsyncDelayTolerance + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.inputFrameUpsyncDelayTolerance = 0; + + /** + * BattleColliderInfo maxChasingRenderFramesPerUpdate. + * @member {number} maxChasingRenderFramesPerUpdate + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.maxChasingRenderFramesPerUpdate = 0; /** * Creates a new BattleColliderInfo instance using the specified properties. @@ -1312,32 +1375,46 @@ $root.treasurehunterx = (function() { BattleColliderInfo.encode = function encode(message, writer) { if (!writer) writer = $Writer.create(); - if (message.intervalToPing != null && Object.hasOwnProperty.call(message, "intervalToPing")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.intervalToPing); - if (message.willKickIfInactiveFor != null && Object.hasOwnProperty.call(message, "willKickIfInactiveFor")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.willKickIfInactiveFor); - if (message.boundRoomId != null && Object.hasOwnProperty.call(message, "boundRoomId")) - writer.uint32(/* id 3, wireType 0 =*/24).int32(message.boundRoomId); if (message.stageName != null && Object.hasOwnProperty.call(message, "stageName")) - writer.uint32(/* id 4, wireType 2 =*/34).string(message.stageName); + writer.uint32(/* id 1, wireType 2 =*/10).string(message.stageName); if (message.strToVec2DListMap != null && Object.hasOwnProperty.call(message, "strToVec2DListMap")) for (var keys = Object.keys(message.strToVec2DListMap), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 5, wireType 2 =*/42).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]); + writer.uint32(/* id 2, wireType 2 =*/18).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]); $root.treasurehunterx.Vec2DList.encode(message.strToVec2DListMap[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); } if (message.strToPolygon2DListMap != null && Object.hasOwnProperty.call(message, "strToPolygon2DListMap")) for (var keys = Object.keys(message.strToPolygon2DListMap), i = 0; i < keys.length; ++i) { - writer.uint32(/* id 6, wireType 2 =*/50).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]); + writer.uint32(/* id 3, wireType 2 =*/26).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]); $root.treasurehunterx.Polygon2DList.encode(message.strToPolygon2DListMap[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); } - if (message.StageDiscreteW != null && Object.hasOwnProperty.call(message, "StageDiscreteW")) - writer.uint32(/* id 7, wireType 0 =*/56).int32(message.StageDiscreteW); - if (message.StageDiscreteH != null && Object.hasOwnProperty.call(message, "StageDiscreteH")) - writer.uint32(/* id 8, wireType 0 =*/64).int32(message.StageDiscreteH); - if (message.StageTileW != null && Object.hasOwnProperty.call(message, "StageTileW")) - writer.uint32(/* id 9, wireType 0 =*/72).int32(message.StageTileW); - if (message.StageTileH != null && Object.hasOwnProperty.call(message, "StageTileH")) - writer.uint32(/* id 10, wireType 0 =*/80).int32(message.StageTileH); + if (message.stageDiscreteW != null && Object.hasOwnProperty.call(message, "stageDiscreteW")) + writer.uint32(/* id 4, wireType 0 =*/32).int32(message.stageDiscreteW); + if (message.stageDiscreteH != null && Object.hasOwnProperty.call(message, "stageDiscreteH")) + writer.uint32(/* id 5, wireType 0 =*/40).int32(message.stageDiscreteH); + if (message.stageTileW != null && Object.hasOwnProperty.call(message, "stageTileW")) + writer.uint32(/* id 6, wireType 0 =*/48).int32(message.stageTileW); + if (message.stageTileH != null && Object.hasOwnProperty.call(message, "stageTileH")) + writer.uint32(/* id 7, wireType 0 =*/56).int32(message.stageTileH); + if (message.intervalToPing != null && Object.hasOwnProperty.call(message, "intervalToPing")) + writer.uint32(/* id 8, wireType 0 =*/64).int32(message.intervalToPing); + if (message.willKickIfInactiveFor != null && Object.hasOwnProperty.call(message, "willKickIfInactiveFor")) + writer.uint32(/* id 9, wireType 0 =*/72).int32(message.willKickIfInactiveFor); + if (message.boundRoomId != null && Object.hasOwnProperty.call(message, "boundRoomId")) + writer.uint32(/* id 10, wireType 0 =*/80).int32(message.boundRoomId); + if (message.battleDurationNanos != null && Object.hasOwnProperty.call(message, "battleDurationNanos")) + writer.uint32(/* id 11, wireType 0 =*/88).int64(message.battleDurationNanos); + if (message.serverFps != null && Object.hasOwnProperty.call(message, "serverFps")) + writer.uint32(/* id 12, wireType 0 =*/96).int32(message.serverFps); + if (message.inputDelayFrames != null && Object.hasOwnProperty.call(message, "inputDelayFrames")) + writer.uint32(/* id 13, wireType 0 =*/104).int32(message.inputDelayFrames); + if (message.inputScaleFrames != null && Object.hasOwnProperty.call(message, "inputScaleFrames")) + writer.uint32(/* id 14, wireType 0 =*/112).uint32(message.inputScaleFrames); + if (message.nstDelayFrames != null && Object.hasOwnProperty.call(message, "nstDelayFrames")) + writer.uint32(/* id 15, wireType 0 =*/120).int32(message.nstDelayFrames); + if (message.inputFrameUpsyncDelayTolerance != null && Object.hasOwnProperty.call(message, "inputFrameUpsyncDelayTolerance")) + writer.uint32(/* id 16, wireType 0 =*/128).int32(message.inputFrameUpsyncDelayTolerance); + if (message.maxChasingRenderFramesPerUpdate != null && Object.hasOwnProperty.call(message, "maxChasingRenderFramesPerUpdate")) + writer.uint32(/* id 17, wireType 0 =*/136).int32(message.maxChasingRenderFramesPerUpdate); return writer; }; @@ -1373,22 +1450,10 @@ $root.treasurehunterx = (function() { var tag = reader.uint32(); switch (tag >>> 3) { case 1: { - message.intervalToPing = reader.int32(); - break; - } - case 2: { - message.willKickIfInactiveFor = reader.int32(); - break; - } - case 3: { - message.boundRoomId = reader.int32(); - break; - } - case 4: { message.stageName = reader.string(); break; } - case 5: { + case 2: { if (message.strToVec2DListMap === $util.emptyObject) message.strToVec2DListMap = {}; var end2 = reader.uint32() + reader.pos; @@ -1411,7 +1476,7 @@ $root.treasurehunterx = (function() { message.strToVec2DListMap[key] = value; break; } - case 6: { + case 3: { if (message.strToPolygon2DListMap === $util.emptyObject) message.strToPolygon2DListMap = {}; var end2 = reader.uint32() + reader.pos; @@ -1434,20 +1499,60 @@ $root.treasurehunterx = (function() { message.strToPolygon2DListMap[key] = value; break; } + case 4: { + message.stageDiscreteW = reader.int32(); + break; + } + case 5: { + message.stageDiscreteH = reader.int32(); + break; + } + case 6: { + message.stageTileW = reader.int32(); + break; + } case 7: { - message.StageDiscreteW = reader.int32(); + message.stageTileH = reader.int32(); break; } case 8: { - message.StageDiscreteH = reader.int32(); + message.intervalToPing = reader.int32(); break; } case 9: { - message.StageTileW = reader.int32(); + message.willKickIfInactiveFor = reader.int32(); break; } case 10: { - message.StageTileH = reader.int32(); + message.boundRoomId = reader.int32(); + break; + } + case 11: { + message.battleDurationNanos = reader.int64(); + break; + } + case 12: { + message.serverFps = reader.int32(); + break; + } + case 13: { + message.inputDelayFrames = reader.int32(); + break; + } + case 14: { + message.inputScaleFrames = reader.uint32(); + break; + } + case 15: { + message.nstDelayFrames = reader.int32(); + break; + } + case 16: { + message.inputFrameUpsyncDelayTolerance = reader.int32(); + break; + } + case 17: { + message.maxChasingRenderFramesPerUpdate = reader.int32(); break; } default: @@ -1485,15 +1590,6 @@ $root.treasurehunterx = (function() { BattleColliderInfo.verify = function verify(message) { if (typeof message !== "object" || message === null) return "object expected"; - if (message.intervalToPing != null && message.hasOwnProperty("intervalToPing")) - if (!$util.isInteger(message.intervalToPing)) - return "intervalToPing: integer expected"; - if (message.willKickIfInactiveFor != null && message.hasOwnProperty("willKickIfInactiveFor")) - if (!$util.isInteger(message.willKickIfInactiveFor)) - return "willKickIfInactiveFor: integer expected"; - if (message.boundRoomId != null && message.hasOwnProperty("boundRoomId")) - if (!$util.isInteger(message.boundRoomId)) - return "boundRoomId: integer expected"; if (message.stageName != null && message.hasOwnProperty("stageName")) if (!$util.isString(message.stageName)) return "stageName: string expected"; @@ -1517,18 +1613,48 @@ $root.treasurehunterx = (function() { return "strToPolygon2DListMap." + error; } } - if (message.StageDiscreteW != null && message.hasOwnProperty("StageDiscreteW")) - if (!$util.isInteger(message.StageDiscreteW)) - return "StageDiscreteW: integer expected"; - if (message.StageDiscreteH != null && message.hasOwnProperty("StageDiscreteH")) - if (!$util.isInteger(message.StageDiscreteH)) - return "StageDiscreteH: integer expected"; - if (message.StageTileW != null && message.hasOwnProperty("StageTileW")) - if (!$util.isInteger(message.StageTileW)) - return "StageTileW: integer expected"; - if (message.StageTileH != null && message.hasOwnProperty("StageTileH")) - if (!$util.isInteger(message.StageTileH)) - return "StageTileH: integer expected"; + if (message.stageDiscreteW != null && message.hasOwnProperty("stageDiscreteW")) + if (!$util.isInteger(message.stageDiscreteW)) + return "stageDiscreteW: integer expected"; + if (message.stageDiscreteH != null && message.hasOwnProperty("stageDiscreteH")) + if (!$util.isInteger(message.stageDiscreteH)) + return "stageDiscreteH: integer expected"; + if (message.stageTileW != null && message.hasOwnProperty("stageTileW")) + if (!$util.isInteger(message.stageTileW)) + return "stageTileW: integer expected"; + if (message.stageTileH != null && message.hasOwnProperty("stageTileH")) + if (!$util.isInteger(message.stageTileH)) + return "stageTileH: integer expected"; + if (message.intervalToPing != null && message.hasOwnProperty("intervalToPing")) + if (!$util.isInteger(message.intervalToPing)) + return "intervalToPing: integer expected"; + if (message.willKickIfInactiveFor != null && message.hasOwnProperty("willKickIfInactiveFor")) + if (!$util.isInteger(message.willKickIfInactiveFor)) + return "willKickIfInactiveFor: integer expected"; + if (message.boundRoomId != null && message.hasOwnProperty("boundRoomId")) + if (!$util.isInteger(message.boundRoomId)) + return "boundRoomId: integer expected"; + if (message.battleDurationNanos != null && message.hasOwnProperty("battleDurationNanos")) + if (!$util.isInteger(message.battleDurationNanos) && !(message.battleDurationNanos && $util.isInteger(message.battleDurationNanos.low) && $util.isInteger(message.battleDurationNanos.high))) + return "battleDurationNanos: integer|Long expected"; + if (message.serverFps != null && message.hasOwnProperty("serverFps")) + if (!$util.isInteger(message.serverFps)) + return "serverFps: integer expected"; + if (message.inputDelayFrames != null && message.hasOwnProperty("inputDelayFrames")) + if (!$util.isInteger(message.inputDelayFrames)) + return "inputDelayFrames: integer expected"; + if (message.inputScaleFrames != null && message.hasOwnProperty("inputScaleFrames")) + if (!$util.isInteger(message.inputScaleFrames)) + return "inputScaleFrames: integer expected"; + if (message.nstDelayFrames != null && message.hasOwnProperty("nstDelayFrames")) + if (!$util.isInteger(message.nstDelayFrames)) + return "nstDelayFrames: integer expected"; + if (message.inputFrameUpsyncDelayTolerance != null && message.hasOwnProperty("inputFrameUpsyncDelayTolerance")) + if (!$util.isInteger(message.inputFrameUpsyncDelayTolerance)) + return "inputFrameUpsyncDelayTolerance: integer expected"; + if (message.maxChasingRenderFramesPerUpdate != null && message.hasOwnProperty("maxChasingRenderFramesPerUpdate")) + if (!$util.isInteger(message.maxChasingRenderFramesPerUpdate)) + return "maxChasingRenderFramesPerUpdate: integer expected"; return null; }; @@ -1544,12 +1670,6 @@ $root.treasurehunterx = (function() { if (object instanceof $root.treasurehunterx.BattleColliderInfo) return object; var message = new $root.treasurehunterx.BattleColliderInfo(); - if (object.intervalToPing != null) - message.intervalToPing = object.intervalToPing | 0; - if (object.willKickIfInactiveFor != null) - message.willKickIfInactiveFor = object.willKickIfInactiveFor | 0; - if (object.boundRoomId != null) - message.boundRoomId = object.boundRoomId | 0; if (object.stageName != null) message.stageName = String(object.stageName); if (object.strToVec2DListMap) { @@ -1572,14 +1692,41 @@ $root.treasurehunterx = (function() { message.strToPolygon2DListMap[keys[i]] = $root.treasurehunterx.Polygon2DList.fromObject(object.strToPolygon2DListMap[keys[i]]); } } - if (object.StageDiscreteW != null) - message.StageDiscreteW = object.StageDiscreteW | 0; - if (object.StageDiscreteH != null) - message.StageDiscreteH = object.StageDiscreteH | 0; - if (object.StageTileW != null) - message.StageTileW = object.StageTileW | 0; - if (object.StageTileH != null) - message.StageTileH = object.StageTileH | 0; + if (object.stageDiscreteW != null) + message.stageDiscreteW = object.stageDiscreteW | 0; + if (object.stageDiscreteH != null) + message.stageDiscreteH = object.stageDiscreteH | 0; + if (object.stageTileW != null) + message.stageTileW = object.stageTileW | 0; + if (object.stageTileH != null) + message.stageTileH = object.stageTileH | 0; + if (object.intervalToPing != null) + message.intervalToPing = object.intervalToPing | 0; + if (object.willKickIfInactiveFor != null) + message.willKickIfInactiveFor = object.willKickIfInactiveFor | 0; + if (object.boundRoomId != null) + message.boundRoomId = object.boundRoomId | 0; + if (object.battleDurationNanos != null) + if ($util.Long) + (message.battleDurationNanos = $util.Long.fromValue(object.battleDurationNanos)).unsigned = false; + else if (typeof object.battleDurationNanos === "string") + message.battleDurationNanos = parseInt(object.battleDurationNanos, 10); + else if (typeof object.battleDurationNanos === "number") + message.battleDurationNanos = object.battleDurationNanos; + else if (typeof object.battleDurationNanos === "object") + message.battleDurationNanos = new $util.LongBits(object.battleDurationNanos.low >>> 0, object.battleDurationNanos.high >>> 0).toNumber(); + if (object.serverFps != null) + message.serverFps = object.serverFps | 0; + if (object.inputDelayFrames != null) + message.inputDelayFrames = object.inputDelayFrames | 0; + if (object.inputScaleFrames != null) + message.inputScaleFrames = object.inputScaleFrames >>> 0; + if (object.nstDelayFrames != null) + message.nstDelayFrames = object.nstDelayFrames | 0; + if (object.inputFrameUpsyncDelayTolerance != null) + message.inputFrameUpsyncDelayTolerance = object.inputFrameUpsyncDelayTolerance | 0; + if (object.maxChasingRenderFramesPerUpdate != null) + message.maxChasingRenderFramesPerUpdate = object.maxChasingRenderFramesPerUpdate | 0; return message; }; @@ -1601,21 +1748,26 @@ $root.treasurehunterx = (function() { object.strToPolygon2DListMap = {}; } if (options.defaults) { + object.stageName = ""; + object.stageDiscreteW = 0; + object.stageDiscreteH = 0; + object.stageTileW = 0; + object.stageTileH = 0; object.intervalToPing = 0; object.willKickIfInactiveFor = 0; object.boundRoomId = 0; - object.stageName = ""; - object.StageDiscreteW = 0; - object.StageDiscreteH = 0; - object.StageTileW = 0; - object.StageTileH = 0; + if ($util.Long) { + var long = new $util.Long(0, 0, false); + object.battleDurationNanos = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.battleDurationNanos = options.longs === String ? "0" : 0; + object.serverFps = 0; + object.inputDelayFrames = 0; + object.inputScaleFrames = 0; + object.nstDelayFrames = 0; + object.inputFrameUpsyncDelayTolerance = 0; + object.maxChasingRenderFramesPerUpdate = 0; } - if (message.intervalToPing != null && message.hasOwnProperty("intervalToPing")) - object.intervalToPing = message.intervalToPing; - if (message.willKickIfInactiveFor != null && message.hasOwnProperty("willKickIfInactiveFor")) - object.willKickIfInactiveFor = message.willKickIfInactiveFor; - if (message.boundRoomId != null && message.hasOwnProperty("boundRoomId")) - object.boundRoomId = message.boundRoomId; if (message.stageName != null && message.hasOwnProperty("stageName")) object.stageName = message.stageName; var keys2; @@ -1629,14 +1781,37 @@ $root.treasurehunterx = (function() { for (var j = 0; j < keys2.length; ++j) object.strToPolygon2DListMap[keys2[j]] = $root.treasurehunterx.Polygon2DList.toObject(message.strToPolygon2DListMap[keys2[j]], options); } - if (message.StageDiscreteW != null && message.hasOwnProperty("StageDiscreteW")) - object.StageDiscreteW = message.StageDiscreteW; - if (message.StageDiscreteH != null && message.hasOwnProperty("StageDiscreteH")) - object.StageDiscreteH = message.StageDiscreteH; - if (message.StageTileW != null && message.hasOwnProperty("StageTileW")) - object.StageTileW = message.StageTileW; - if (message.StageTileH != null && message.hasOwnProperty("StageTileH")) - object.StageTileH = message.StageTileH; + if (message.stageDiscreteW != null && message.hasOwnProperty("stageDiscreteW")) + object.stageDiscreteW = message.stageDiscreteW; + if (message.stageDiscreteH != null && message.hasOwnProperty("stageDiscreteH")) + object.stageDiscreteH = message.stageDiscreteH; + if (message.stageTileW != null && message.hasOwnProperty("stageTileW")) + object.stageTileW = message.stageTileW; + if (message.stageTileH != null && message.hasOwnProperty("stageTileH")) + object.stageTileH = message.stageTileH; + if (message.intervalToPing != null && message.hasOwnProperty("intervalToPing")) + object.intervalToPing = message.intervalToPing; + if (message.willKickIfInactiveFor != null && message.hasOwnProperty("willKickIfInactiveFor")) + object.willKickIfInactiveFor = message.willKickIfInactiveFor; + if (message.boundRoomId != null && message.hasOwnProperty("boundRoomId")) + object.boundRoomId = message.boundRoomId; + if (message.battleDurationNanos != null && message.hasOwnProperty("battleDurationNanos")) + if (typeof message.battleDurationNanos === "number") + object.battleDurationNanos = options.longs === String ? String(message.battleDurationNanos) : message.battleDurationNanos; + else + object.battleDurationNanos = options.longs === String ? $util.Long.prototype.toString.call(message.battleDurationNanos) : options.longs === Number ? new $util.LongBits(message.battleDurationNanos.low >>> 0, message.battleDurationNanos.high >>> 0).toNumber() : message.battleDurationNanos; + if (message.serverFps != null && message.hasOwnProperty("serverFps")) + object.serverFps = message.serverFps; + if (message.inputDelayFrames != null && message.hasOwnProperty("inputDelayFrames")) + object.inputDelayFrames = message.inputDelayFrames; + if (message.inputScaleFrames != null && message.hasOwnProperty("inputScaleFrames")) + object.inputScaleFrames = message.inputScaleFrames; + if (message.nstDelayFrames != null && message.hasOwnProperty("nstDelayFrames")) + object.nstDelayFrames = message.nstDelayFrames; + if (message.inputFrameUpsyncDelayTolerance != null && message.hasOwnProperty("inputFrameUpsyncDelayTolerance")) + object.inputFrameUpsyncDelayTolerance = message.inputFrameUpsyncDelayTolerance; + if (message.maxChasingRenderFramesPerUpdate != null && message.hasOwnProperty("maxChasingRenderFramesPerUpdate")) + object.maxChasingRenderFramesPerUpdate = message.maxChasingRenderFramesPerUpdate; return object; }; From 54d6e5249883a20afddb9b56ac902c695020bf64 Mon Sep 17 00:00:00 2001 From: genxium Date: Sun, 2 Oct 2022 16:19:54 +0800 Subject: [PATCH 09/13] Minor fix for frontend chaserRenderFrameId positioning and logging. --- battle_srv/models/room.go | 13 ++++++------- frontend/assets/scenes/login.fire | 2 +- frontend/assets/scripts/Map.js | 3 ++- frontend/assets/scripts/WsSessionMgr.js | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index de158fa..ea71837 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -473,7 +473,7 @@ func (pR *Room) StartBattle() { f := tmp.(*pb.InputFrameDownsync) if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) { debugSendingInputFrameId = candidateToSendInputFrameId - Logger.Info("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false)), zap.Any("ConfirmedList", f.ConfirmedList)) + Logger.Debug("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false)), zap.Any("ConfirmedList", f.ConfirmedList)) } toSendInputFrames = append(toSendInputFrames, f) candidateToSendInputFrameId++ @@ -511,7 +511,7 @@ func (pR *Room) StartBattle() { f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync) if pR.inputFrameIdDebuggable(f.InputFrameId) { // Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked - Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) + Logger.Debug("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) } } @@ -579,15 +579,14 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { inputFrameDownsync := tmp2.(*pb.InputFrameDownsync) oldConfirmedList := atomic.LoadUint64(&(inputFrameDownsync.ConfirmedList)) if (oldConfirmedList & joinMask) > 0 { - Logger.Warn(fmt.Sprintf("Cmd already confirmed but getting set attempt, omitting this upsync cmd: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) + Logger.Debug(fmt.Sprintf("Cmd already confirmed but getting set attempt, omitting this upsync cmd: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) return } // In Golang 1.12, there's no "compare-and-swap primitive" on a custom struct (or it's pointer, unless it's an unsafe pointer https://pkg.go.dev/sync/atomic@go1.12#CompareAndSwapPointer). Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. - if swapped := atomic.CompareAndSwapUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], uint64(0), encodedInput); !swapped { - Logger.Warn(fmt.Sprintf("Failed input CAS: roomId=%v, playerId=%v, clientInputFrameId=%v", pR.Id, playerId, clientInputFrameId)) - return - } + + // [WARNING] No need to use CAS for updating "inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr]", the upsync from frontend takes top priority. + atomic.StoreUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], encodedInput); newConfirmedList := (oldConfirmedList | joinMask) if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped { diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index 6324f99..cee1e6c 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 209.57814771583418, + 209.63606633899616, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 739b185..d719ab1 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -947,6 +947,7 @@ cc.Class({ rdf.id > self.lastAllConfirmedRenderFrameId ) { self.lastAllConfirmedRenderFrameId = rdf.id; + self.chaserRenderFrameId = rdf.id; // it must be true that "chaserRenderFrameId >= lastAllConfirmedRenderFrameId" } self._dumpToRenderCache(rdf); return rdf; @@ -978,7 +979,7 @@ cc.Class({ }, rollbackAndChase(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap) { - if (renderFrameSt >= renderFrameIdEd) { + if (renderFrameIdSt >= renderFrameIdEd) { return; } diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index da2abec..38bd561 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -179,7 +179,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { break; case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC: if (window.handleInputFrameDownsyncBatch && window.handleRoomDownsyncFrame) { - console.warn("Got forced resync:", resp.toString(), " @localRenderFrameId=", mapIns.renderFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false)); + console.warn("Got forced resync:", JSON.stringify(resp), " @localRenderFrameId=", mapIns.renderFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false)); // The following order of execution is important, because "handleInputFrameDownsyncBatch" is only available when state is IN_BATTLE window.handleRoomDownsyncFrame(resp.rdf); window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); From 9d9bea21ef61de0bc86b92a0847c595ef47c8820 Mon Sep 17 00:00:00 2001 From: genxium Date: Mon, 3 Oct 2022 00:22:05 +0800 Subject: [PATCH 10/13] Minor updates. --- battle_srv/models/room.go | 112 ++++-- frontend/assets/scenes/login.fire | 2 +- frontend/assets/scripts/Map.js | 501 ++++++++++++------------ frontend/assets/scripts/RingBuffer.js | 47 ++- frontend/assets/scripts/WsSessionMgr.js | 30 +- 5 files changed, 377 insertions(+), 315 deletions(-) diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index ea71837..6a8a718 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -26,10 +26,10 @@ const ( UPSYNC_MSG_ACT_PLAYER_CMD = int32(2) UPSYNC_MSG_ACT_PLAYER_COLLIDER_ACK = int32(3) - DOWNSYNC_MSG_ACT_HB_REQ = int32(1) - DOWNSYNC_MSG_ACT_INPUT_BATCH = int32(2) - DOWNSYNC_MSG_ACT_ROOM_FRAME = int32(3) - DOWNSYNC_MSG_ACT_FORCED_RESYNC = int32(4) + DOWNSYNC_MSG_ACT_HB_REQ = int32(1) + DOWNSYNC_MSG_ACT_INPUT_BATCH = int32(2) + DOWNSYNC_MSG_ACT_BATTLE_STOPPED = int32(3) + DOWNSYNC_MSG_ACT_FORCED_RESYNC = int32(4) DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START = int32(-1) DOWNSYNC_MSG_ACT_BATTLE_START = int32(0) @@ -334,6 +334,10 @@ func (pR *Room) ConvertToInputFrameId(renderFrameId int32, inputDelayFrames int3 return ((renderFrameId - inputDelayFrames) >> pR.InputScaleFrames) } +func (pR *Room) ConvertToGeneratingRenderFrameId(inputFrameId int32) int32 { + return (inputFrameId << pR.InputScaleFrames) +} + func (pR *Room) ConvertToFirstUsedRenderFrameId(inputFrameId int32, inputDelayFrames int32) int32 { return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames) } @@ -349,6 +353,10 @@ func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 { return ret } +func (pR *Room) RenderFrameBufferString() string { + return fmt.Sprintf("{renderFrameId: %d, stRenderFrameId: %d, edRenderFrameId: %d, lastAllConfirmedRenderFrameId: %d}", pR.RenderFrameId, pR.RenderFrameBuffer.StFrameId, pR.RenderFrameBuffer.EdFrameId, pR.CurDynamicsRenderFrameId) +} + func (pR *Room) AllPlayerInputsBufferString(allDetails bool) string { if allDetails { // Appending of the array of strings can be very SLOW due to on-demand heap allocation! Use this printing with caution. @@ -366,7 +374,7 @@ func (pR *Room) AllPlayerInputsBufferString(allDetails bool) string { s = append(s, fmt.Sprintf("{inputFrameId: %v, inputList: %v, confirmedList: %v}", f.InputFrameId, f.InputList, f.ConfirmedList)) } - return strings.Join(s, "\n") + return strings.Join(s, "; ") } else { return fmt.Sprintf("{renderFrameId: %d, stInputFrameId: %d, edInputFrameId: %d, lastAllConfirmedInputFrameIdWithChange: %d, lastAllConfirmedInputFrameId: %d}", pR.RenderFrameId, pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId) } @@ -443,7 +451,7 @@ func (pR *Room) StartBattle() { dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt } - lastAllConfirmedInputFrameIdWithChange := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameIdWithChange)) + upperToSendInputFrameId := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameId)) for playerId, player := range pR.Players { if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped { // [WARNING] DON'T send anything if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player". @@ -463,46 +471,65 @@ func (pR *Room) StartBattle() { } // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! - debugSendingInputFrameId := int32(-1) - for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange { + for candidateToSendInputFrameId <= upperToSendInputFrameId { tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId) if nil == tmp { panic(fmt.Sprintf("Required inputFrameId=%v for roomId=%v, playerId=%v doesn't exist! AllPlayerInputsBuffer=%v", candidateToSendInputFrameId, pR.Id, playerId, pR.AllPlayerInputsBufferString(false))) } f := tmp.(*pb.InputFrameDownsync) if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) { - debugSendingInputFrameId = candidateToSendInputFrameId Logger.Debug("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false)), zap.Any("ConfirmedList", f.ConfirmedList)) } toSendInputFrames = append(toSendInputFrames, f) candidateToSendInputFrameId++ } + if 0 >= len(toSendInputFrames) { + // [WARNING] When sending DOWNSYNC_MSG_ACT_FORCED_RESYNC, there MUST BE accompanying "toSendInputFrames" for calculating "refRenderFrameId"! + continue + } + indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) if 0 < (unconfirmedMask & joinMask) { - refRenderFrameId := pR.CurDynamicsRenderFrameId + /* + [WARNING] + "refRenderFrameId" MUST BE CAPPED somehow by "candidateToSendInputFrameId-1", if frontend resyncs itself to a more advanced value than given below, upon the next renderFrame tick on the frontend it might generate non-consecutive "nextInputFrameId > frontend.recentInputCache.edFrameId+1". + + If "NstDelayFrames" becomes larger, "pR.RenderFrameId - refRenderFrameId" possibly becomes larger because the force confirmation is delayed more. + + Hence even upon resync, it's still possible that "refRenderFrameId < frontend.chaserRenderFrameId". + */ + refRenderFrameId := pR.ConvertToGeneratingRenderFrameId(upperToSendInputFrameId) + (1 << pR.InputScaleFrames) - 1 + // [WARNING] The following inequalities are seldom true, but just to avoid that in good network condition the frontend resyncs itself to a "too advanced frontend.renderFrameId", and then starts upsyncing "too advanced inputFrameId". if refRenderFrameId > pR.RenderFrameId { - // [WARNING] To avoid that in good network condition the frontend resyncs itself to a "too advanced frontend.renderFrameId", and then starts upsyncing "too advanced inputFrameId". refRenderFrameId = pR.RenderFrameId } - refRenderFrame := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId).(*pb.RoomDownsyncFrame) + if refRenderFrameId > pR.CurDynamicsRenderFrameId { + refRenderFrameId = pR.CurDynamicsRenderFrameId + } + tmp := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId) + if nil == tmp { + panic(fmt.Sprintf("Required refRenderFrameId=%v for roomId=%v, playerId=%v, candidateToSendInputFrameId=%v doesn't exist! AllPlayerInputsBuffer=%v, RenderFrameBuffer=%v", refRenderFrameId, pR.Id, playerId, candidateToSendInputFrameId, pR.AllPlayerInputsBufferString(false), pR.RenderFrameBufferString())) + } + refRenderFrame := tmp.(*pb.RoomDownsyncFrame) pR.sendSafely(refRenderFrame, toSendInputFrames, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId) } else { - if 0 >= len(toSendInputFrames) { - continue - } pR.sendSafely(nil, toSendInputFrames, DOWNSYNC_MSG_ACT_INPUT_BATCH, playerId) } - if -1 != debugSendingInputFrameId { - Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) - } atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) } } - for 0 < pR.RenderFrameBuffer.Cnt && pR.RenderFrameBuffer.StFrameId < pR.CurDynamicsRenderFrameId { + renderFrameBuffLowerBound := pR.ConvertToGeneratingRenderFrameId(upperToSendInputFrameId) + (1 << pR.InputScaleFrames) - 1 + if renderFrameBuffLowerBound > pR.RenderFrameId { + renderFrameBuffLowerBound = pR.RenderFrameId + } + if renderFrameBuffLowerBound > pR.CurDynamicsRenderFrameId { + renderFrameBuffLowerBound = pR.CurDynamicsRenderFrameId + } + for 0 < pR.RenderFrameBuffer.Cnt && pR.RenderFrameBuffer.StFrameId < renderFrameBuffLowerBound { _ = pR.RenderFrameBuffer.Pop() } @@ -511,7 +538,7 @@ func (pR *Room) StartBattle() { f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync) if pR.inputFrameIdDebuggable(f.InputFrameId) { // Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked - Logger.Debug("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) + Logger.Debug("inputFrame lifecycle#4[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) } } @@ -585,13 +612,13 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { // In Golang 1.12, there's no "compare-and-swap primitive" on a custom struct (or it's pointer, unless it's an unsafe pointer https://pkg.go.dev/sync/atomic@go1.12#CompareAndSwapPointer). Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. - // [WARNING] No need to use CAS for updating "inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr]", the upsync from frontend takes top priority. - atomic.StoreUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], encodedInput); + // [WARNING] No need to use CAS for updating "inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr]", the upsync from frontend takes top priority. + atomic.StoreUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], encodedInput) newConfirmedList := (oldConfirmedList | joinMask) if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped { // [WARNING] Upon this error, the actual input has already been updated, which is an expected result if it caused by the force confirmation from "battleMainLoop". - Logger.Warn(fmt.Sprintf("Failed confirm CAS: roomId=%v, playerId=%v, clientInputFrameId=%v", pR.Id, playerId, clientInputFrameId)) + Logger.Warn(fmt.Sprintf("Failed confirm CAS, might've been forced to all-confirmed: roomId=%v, playerId=%v, clientInputFrameId=%v", pR.Id, playerId, clientInputFrameId)) return } @@ -606,7 +633,11 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFrameDownsync, playerId int32) { inputFrameId := inputFrameDownsync.InputFrameId if -1 == pR.LastAllConfirmedInputFrameIdWithChange || false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { - Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, newInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString(false))) + if -1 == playerId { + Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, newInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString(false))) + } else { + Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, newInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString(false))) + } atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), inputFrameId) } atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), inputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! @@ -614,12 +645,10 @@ func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFra // To avoid potential misuse of pointers pR.LastAllConfirmedInputList[i] = v } - if pR.inputFrameIdDebuggable(inputFrameId) { - if -1 == playerId { - Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[forced-allconfirmed]: roomId=%v, inputFrameId=%v, lastAllConfirmedInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, inputFrameId, pR.LastAllConfirmedInputFrameId, pR.AllPlayerInputsBufferString(false))) - } else { - Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[allconfirmed]: roomId=%v, playerId=%v, inputFrameId=%v, lastAllConfirmedInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, pR.AllPlayerInputsBufferString(false))) - } + if -1 == playerId { + Logger.Debug(fmt.Sprintf("inputFrame lifecycle#2[forced-allconfirmed]: roomId=%v, AllPlayerInputsBuffer=%v", pR.Id, pR.AllPlayerInputsBufferString(false))) + } else { + Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[allconfirmed]: roomId=%v, playerId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, pR.AllPlayerInputsBufferString(false))) } } @@ -648,7 +677,7 @@ func (pR *Room) StopBattleForSettlement() { Players: toPbPlayers(pR.Players), CountdownNanos: -1, // TODO: Replace this magic constant! } - pR.sendSafely(&assembledFrame, nil, DOWNSYNC_MSG_ACT_ROOM_FRAME, playerId) + pR.sendSafely(&assembledFrame, nil, DOWNSYNC_MSG_ACT_BATTLE_STOPPED, playerId) } // Note that `pR.onBattleStoppedForSettlement` will be called by `battleMainLoop`. } @@ -1000,7 +1029,7 @@ func (pR *Room) sendSafely(roomDownsyncFrame *pb.RoomDownsyncFrame, toSendFrames } if err := pR.PlayerDownsyncSessionDict[playerId].WriteMessage(websocket.BinaryMessage, theBytes); nil != err { - panic(fmt.Sprintf("Error sending downsync message: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount)) + panic(fmt.Sprintf("Error sending downsync message: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v, err=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount, err)) } } @@ -1053,11 +1082,11 @@ func (pR *Room) forceConfirmationIfApplicable() uint64 { } inputFrameId2 := pR.ConvertToInputFrameId(renderFrameId1, 0) // The inputFrame to force confirmation (if necessary) - if inputFrameId2 < pR.LastAllConfirmedInputFrameId { - // No need to force confirmation, the inputFrames already arrived + if inputFrameId2 < pR.LastAllConfirmedInputFrameId { + // No need to force confirmation, the inputFrames already arrived Logger.Debug(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v[type#1], no need to force confirmation of it", inputFrameId2, pR.Id)) - return 0 - } + return 0 + } tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId2) if nil == tmp { panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! AllPlayerInputsBuffer=%v", inputFrameId2, pR.Id, pR.AllPlayerInputsBufferString(false))) @@ -1067,7 +1096,7 @@ func (pR *Room) forceConfirmationIfApplicable() uint64 { totPlayerCnt := uint32(pR.Capacity) allConfirmedMask := uint64((1 << totPlayerCnt) - 1) if swapped := atomic.CompareAndSwapUint64(&(inputFrame2.ConfirmedList), allConfirmedMask, allConfirmedMask); swapped { - // This could happen if the frontend upsync command arrived between type#1 and type#2 checks. + // This could happen if the frontend upsync command arrived between type#1 and type#2 checks. Logger.Debug(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v[type#2], no need to force confirmation of it", inputFrameId2, pR.Id)) return 0 } @@ -1087,21 +1116,22 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende } Logger.Debug(fmt.Sprintf("Applying inputFrame dynamics: roomId=%v, room.RenderFrameId=%v, fromRenderFrameId=%v, toRenderFrameId=%v", pR.Id, pR.RenderFrameId, fromRenderFrameId, toRenderFrameId)) - totPlayerCnt := uint32(pR.Capacity) allConfirmedMask := uint64((1 << totPlayerCnt) - 1) for collisionSysRenderFrameId := fromRenderFrameId; collisionSysRenderFrameId < toRenderFrameId; collisionSysRenderFrameId++ { delayedInputFrameId := pR.ConvertToInputFrameId(collisionSysRenderFrameId, pR.InputDelayFrames) if 0 <= delayedInputFrameId { + if delayedInputFrameId > pR.LastAllConfirmedInputFrameId { + panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString(false))) + } tmp := pR.AllPlayerInputsBuffer.GetByFrameId(delayedInputFrameId) if nil == tmp { panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString(false))) } delayedInputFrame := tmp.(*pb.InputFrameDownsync) - if swapped := atomic.CompareAndSwapUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask, allConfirmedMask); !swapped { - panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString(false))) - } + // [WARNING] It's possible that by now "allConfirmedMask != delayedInputFrame.ConfirmedList && delayedInputFrameId <= pR.LastAllConfirmedInputFrameId", we trust "pR.LastAllConfirmedInputFrameId" as the TOP AUTHORITY. + atomic.StoreUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask) inputList := delayedInputFrame.InputList // Ordered by joinIndex to guarantee determinism diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index cee1e6c..15d9cdf 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 209.63606633899616, + 216.6425019058577, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index d719ab1..84ba905 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -98,52 +98,35 @@ cc.Class({ default: null }, }, - + _inputFrameIdDebuggable(inputFrameId) { - return (0 == inputFrameId%10); + return (0 == inputFrameId % 10); }, - _dumpToRenderCache: function(roomDownsyncFrame) { + dumpToRenderCache: function(roomDownsyncFrame) { const self = this; - const minToKeepRenderFrameId = self.lastAllConfirmedRenderFrameId; + const minToKeepRenderFrameId = self.lastAllConfirmedRenderFrameId; while (0 < self.recentRenderCache.cnt && self.recentRenderCache.stFrameId < minToKeepRenderFrameId) { self.recentRenderCache.pop(); } - if (self.recentRenderCache.stFrameId < minToKeepRenderFrameId) { - console.warn("Weird dumping of RENDER frame: self.renderFrame=", self.renderFrame, ", self.recentInputCache=", self._stringifyRecentInputCache(false), ", self.recentRenderCache=", self._stringifyRecentRenderCache(false), ", self.lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", self.lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId); - } - const existing = self.recentRenderCache.getByFrameId(roomDownsyncFrame.id); - if (null != existing) { - existing.players = roomDownsyncFrame.players; - existing.sentAt = roomDownsyncFrame.sentAt; - existing.countdownNanos = roomDownsyncFrame.countdownNanos; - existing.treasures = roomDownsyncFrame.treasures; - existing.bullets = roomDownsyncFrame.bullets; - existing.speedShoes = roomDownsyncFrame.speedShoes; - existing.guardTowers = roomDownsyncFrame.guardTowers; - existing.playerMetas = roomDownsyncFrame.playerMetas; - } else { - self.recentRenderCache.put(roomDownsyncFrame); - } + const ret = self.recentRenderCache.setByFrameId(roomDownsyncFrame, roomDownsyncFrame.id); + return ret; }, - _dumpToInputCache: function(inputFrameDownsync) { + dumpToInputCache: function(inputFrameDownsync) { const self = this; let minToKeepInputFrameId = self._convertToInputFrameId(self.lastAllConfirmedRenderFrameId, self.inputDelayFrames); // [WARNING] This could be different from "self.lastAllConfirmedInputFrameId". We'd like to keep the corresponding inputFrame for "self.lastAllConfirmedRenderFrameId" such that a rollback could place "self.chaserRenderFrameId = self.lastAllConfirmedRenderFrameId" for the worst case incorrect prediction. - if (minToKeepInputFrameId > self.lastAllConfirmedInputFrameId) minToKeepInputFrameId = self.lastAllConfirmedInputFrameId; + if (minToKeepInputFrameId > self.lastAllConfirmedInputFrameId) { + minToKeepInputFrameId = self.lastAllConfirmedInputFrameId; + } while (0 < self.recentInputCache.cnt && self.recentInputCache.stFrameId < minToKeepInputFrameId) { self.recentInputCache.pop(); } - if (self.recentInputCache.stFrameId < minToKeepInputFrameId) { - console.warn("Weird dumping of INPUT frame: self.renderFrame=", self.renderFrame, ", self.recentInputCache=", self._stringifyRecentInputCache(false), ", self.recentRenderCache=", self._stringifyRecentRenderCache(false), ", self.lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", self.lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId); - } - const existing = self.recentInputCache.getByFrameId(inputFrameDownsync.inputFrameId); - if (null != existing) { - existing.inputList = inputFrameDownsync.inputList; - existing.confirmedList = inputFrameDownsync.confirmedList; - } else { - self.recentInputCache.put(inputFrameDownsync); + const ret = self.recentInputCache.setByFrameId(inputFrameDownsync, inputFrameDownsync.inputFrameId); + if (-1 < self.lastAllConfirmedInputFrameId && self.recentInputCache.stFrameId > self.lastAllConfirmedInputFrameId) { + console.error("Invalid input cache dumped! lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false)); } + return ret; }, _convertToInputFrameId(renderFrameId, inputDelayFrames) { @@ -156,12 +139,12 @@ cc.Class({ }, shouldGenerateInputFrameUpsync(renderFrameId) { - return ((renderFrameId & ((1 << this.inputScaleFrames)-1)) == 0); + return ((renderFrameId & ((1 << this.inputScaleFrames) - 1)) == 0); }, _allConfirmed(confirmedList) { - return (confirmedList+1) == (1 << this.playerRichInfoDict.size); - }, + return (confirmedList + 1) == (1 << this.playerRichInfoDict.size); + }, _generateInputFrameUpsync(inputFrameId) { const self = this; @@ -174,21 +157,21 @@ cc.Class({ const joinIndex = self.selfPlayerInfo.joinIndex; const discreteDir = self.ctrl.getDiscretizedDirection(); - const previousInputFrameDownsyncWithPrediction = self.getCachedInputFrameDownsyncWithPrediction(inputFrameId); - const prefabbedInputList = (null == previousInputFrameDownsyncWithPrediction ? new Array(self.playerRichInfoDict.size).fill(0) : previousInputFrameDownsyncWithPrediction.inputList.slice()); - prefabbedInputList[(joinIndex-1)] = discreteDir.encodedIdx; - const prefabbedInputFrameDownsync = { + const previousInputFrameDownsyncWithPrediction = self.getCachedInputFrameDownsyncWithPrediction(inputFrameId); + const prefabbedInputList = (null == previousInputFrameDownsyncWithPrediction ? new Array(self.playerRichInfoDict.size).fill(0) : previousInputFrameDownsyncWithPrediction.inputList.slice()); + prefabbedInputList[(joinIndex - 1)] = discreteDir.encodedIdx; + const prefabbedInputFrameDownsync = { inputFrameId: inputFrameId, - inputList: prefabbedInputList, - confirmedList: (1 << (self.selfPlayerInfo.joinIndex-1)) + inputList: prefabbedInputList, + confirmedList: (1 << (self.selfPlayerInfo.joinIndex - 1)) }; - self._dumpToInputCache(prefabbedInputFrameDownsync); // A prefabbed inputFrame, would certainly be adding a new inputFrame to the cache, because server only downsyncs "all-confirmed inputFrames" + self.dumpToInputCache(prefabbedInputFrameDownsync); // A prefabbed inputFrame, would certainly be adding a new inputFrame to the cache, because server only downsyncs "all-confirmed inputFrames" - const previousSelfInput = (null == previousInputFrameDownsyncWithPrediction ? null : previousInputFrameDownsyncWithPrediction.inputList[joinIndex-1]); + const previousSelfInput = (null == previousInputFrameDownsyncWithPrediction ? null : previousInputFrameDownsyncWithPrediction.inputList[joinIndex - 1]); return [previousSelfInput, discreteDir.encodedIdx]; }, - + shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, lastUpsyncInputFrameId, currInputFrameId) { /* For a 2-player-battle, this "shouldUpsyncForEarlyAllConfirmedOnBackend" can be omitted, however for more players in a same battle, to avoid a "long time non-moving player" jamming the downsync of other moving players, we should use this flag. @@ -197,26 +180,31 @@ cc.Class({ */ if (null == currSelfInput) return false; - const shouldUpsyncForEarlyAllConfirmedOnBackend = (currInputFrameId - lastUpsyncInputFrameId >= this.inputFrameUpsyncDelayTolerance); + const shouldUpsyncForEarlyAllConfirmedOnBackend = (currInputFrameId - lastUpsyncInputFrameId >= this.inputFrameUpsyncDelayTolerance); return shouldUpsyncForEarlyAllConfirmedOnBackend || (prevSelfInput != currSelfInput); - }, + }, - sendInputFrameUpsyncBatch(inputFrameId) { - // [WARNING] Why not just send the latest input? Because different player would have a different "inputFrameId" of changing its last input, and that could make the server not recognizing any "all-confirmed inputFrame"! + sendInputFrameUpsyncBatch(latestLocalInputFrameId) { + // [WARNING] Why not just send the latest input? Because different player would have a different "latestLocalInputFrameId" of changing its last input, and that could make the server not recognizing any "all-confirmed inputFrame"! const self = this; let inputFrameUpsyncBatch = []; - for (let i = self.lastUpsyncInputFrameId+1; i <= inputFrameId; ++i) { + let batchInputFrameIdSt = self.lastUpsyncInputFrameId + 1; + if (batchInputFrameIdSt < self.recentInputCache.stFrameId) { + // Upon resync, "self.lastUpsyncInputFrameId" might not have been updated properly. + batchInputFrameIdSt = self.recentInputCache.stFrameId; + } + for (let i = batchInputFrameIdSt; i <= latestLocalInputFrameId; ++i) { const inputFrameDownsync = self.recentInputCache.getByFrameId(i); if (null == inputFrameDownsync) { - console.warn("sendInputFrameUpsyncBatch: recentInputCache is NOT having inputFrameId=", i, "; recentInputCache=", self._stringifyRecentInputCache(false)); + console.error("sendInputFrameUpsyncBatch: recentInputCache is NOT having inputFrameId=", i, ": latestLocalInputFrameId=", latestLocalInputFrameId, ", recentInputCache=", self._stringifyRecentInputCache(false)); } else { const inputFrameUpsync = { inputFrameId: i, - encodedDir: inputFrameDownsync.inputList[self.selfPlayerInfo.joinIndex-1], + encodedDir: inputFrameDownsync.inputList[self.selfPlayerInfo.joinIndex - 1], }; inputFrameUpsyncBatch.push(inputFrameUpsync); } - } + } const reqData = window.WsReq.encode({ msgId: Date.now(), playerId: self.selfPlayerInfo.id, @@ -227,7 +215,7 @@ cc.Class({ inputFrameUpsyncBatch: inputFrameUpsyncBatch, }).finish(); window.sendSafely(reqData); - self.lastUpsyncInputFrameId = inputFrameId; + self.lastUpsyncInputFrameId = latestLocalInputFrameId; }, onEnable() { @@ -244,12 +232,6 @@ cc.Class({ if (null == self.battleState || ALL_BATTLE_STATES.WAITING == self.battleState) { window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); } - if (null != window.handleRoomDownsyncFrame) { - window.handleRoomDownsyncFrame = null; - } - if (null != window.handleInputFrameDownsyncBatch) { - window.handleInputFrameDownsyncBatch = null; - } if (null != window.handleBattleColliderInfo) { window.handleBattleColliderInfo = null; } @@ -309,7 +291,7 @@ cc.Class({ playerRichInfo.node.parent.removeChild(playerRichInfo.node); } }); - } + } self.playerRichInfoDict = new Map(); // Clearing previous info of all players. [ENDS] @@ -317,12 +299,12 @@ cc.Class({ self.lastAllConfirmedRenderFrameId = -1; self.lastAllConfirmedInputFrameId = -1; self.lastUpsyncInputFrameId = -1; - self.chaserRenderFrameId = -1; // at any moment, "lastAllConfirmedRenderFrameId <= chaserRenderFrameId <= renderFrameId", but "chaserRenderFrameId" would fluctuate according to "handleInputFrameDownsyncBatch" + self.chaserRenderFrameId = -1; // at any moment, "lastAllConfirmedRenderFrameId <= chaserRenderFrameId <= renderFrameId", but "chaserRenderFrameId" would fluctuate according to "onInputFrameDownsyncBatch" self.recentRenderCache = new RingBuffer(1024); self.selfPlayerInfo = null; // This field is kept for distinguishing "self" and "others". - self.recentInputCache = new RingBuffer(1024); + self.recentInputCache = new RingBuffer(1024); self.latestCollisionSys = new collisions.Collisions(); self.chaserCollisionSys = new collisions.Collisions(); @@ -330,7 +312,7 @@ cc.Class({ self.collisionBarrierIndexPrefix = (1 << 16); // For tracking the movements of barriers, though not yet actually used self.latestCollisionSysMap = new Map(); self.chaserCollisionSysMap = new Map(); - + self.transitToState(ALL_MAP_STATES.VISUAL); self.battleState = ALL_BATTLE_STATES.WAITING; @@ -378,13 +360,11 @@ cc.Class({ const resultPanelScriptIns = self.resultPanelNode.getComponent("ResultPanel"); resultPanelScriptIns.mapScriptIns = self; resultPanelScriptIns.onAgainClicked = () => { - self.battleState = ALL_BATTLE_STATES.WAITING; + self.battleState = ALL_BATTLE_STATES.WAITING; window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); window.initPersistentSessionClient(self.initAfterWSConnected, null /* Deliberately NOT passing in any `expectedRoomId`. -- YFLu */ ); }; - resultPanelScriptIns.onCloseDelegate = () => { - - }; + resultPanelScriptIns.onCloseDelegate = () => {}; self.gameRuleNode = cc.instantiate(self.gameRulePrefab); self.gameRuleNode.width = self.canvasNode.width; @@ -416,13 +396,13 @@ cc.Class({ /** Init required prefab ended. */ window.handleBattleColliderInfo = function(parsedBattleColliderInfo) { - self.inputDelayFrames = parsedBattleColliderInfo.inputDelayFrames; + self.inputDelayFrames = parsedBattleColliderInfo.inputDelayFrames; self.inputScaleFrames = parsedBattleColliderInfo.inputScaleFrames; self.inputFrameUpsyncDelayTolerance = parsedBattleColliderInfo.inputFrameUpsyncDelayTolerance; - self.rollbackEstimatedDt = 1.0/parsedBattleColliderInfo.serverFps; - self.rollbackEstimatedDtMillis = 1000.0*self.rollbackEstimatedDt; - self.rollbackEstimatedDtToleranceMillis = self.rollbackEstimatedDtMillis/1000.0; + self.rollbackEstimatedDt = 1.0 / parsedBattleColliderInfo.serverFps; + self.rollbackEstimatedDtMillis = 1000.0 * self.rollbackEstimatedDt; + self.rollbackEstimatedDtToleranceMillis = self.rollbackEstimatedDtMillis / 1000.0; self.maxChasingRenderFramesPerUpdate = parsedBattleColliderInfo.maxChasingRenderFramesPerUpdate; const tiledMapIns = self.node.getComponent(cc.TiledMap); @@ -433,7 +413,7 @@ cc.Class({ console.error(err); return; } - + /* [WARNING] @@ -444,12 +424,12 @@ cc.Class({ tiledMapIns.tmxAsset = null; mapNode.removeAllChildren(); - self._resetCurrentMatch(); + self._resetCurrentMatch(); tiledMapIns.tmxAsset = tmxAsset; const newMapSize = tiledMapIns.getMapSize(); const newTileSize = tiledMapIns.getTileSize(); - self.node.setContentSize(newMapSize.width*newTileSize.width, newMapSize.height*newTileSize.height); + self.node.setContentSize(newMapSize.width * newTileSize.width, newMapSize.height * newTileSize.height); self.node.setPosition(cc.v2(0, 0)); /* * Deliberately hiding "ImageLayer"s. This dirty fix is specific to "CocosCreator v2.2.1", where it got back the rendering capability of "ImageLayer of Tiled", yet made incorrectly. In this game our "markers of ImageLayers" are rendered by dedicated prefabs with associated colliders. @@ -458,24 +438,25 @@ cc.Class({ */ const existingImageLayers = tiledMapIns.getObjectGroups(); for (let singleImageLayer of existingImageLayers) { - singleImageLayer.node.opacity = 0; + singleImageLayer.node.opacity = 0; } let barrierIdCounter = 0; const boundaryObjs = tileCollisionManager.extractBoundaryObjects(self.node); for (let boundaryObj of boundaryObjs.barriers) { - const x0 = boundaryObj[0].x, y0 = boundaryObj[0].y; + const x0 = boundaryObj[0].x, + y0 = boundaryObj[0].y; let pts = []; // TODO: Simplify this redundant coordinate conversion within "extractBoundaryObjects", but since this routine is only called once per battle, not urgent. for (let i = 0; i < boundaryObj.length; ++i) { - pts.push([boundaryObj[i].x-x0, boundaryObj[i].y-y0]); - } + pts.push([boundaryObj[i].x - x0, boundaryObj[i].y - y0]); + } const newBarrierLatest = self.latestCollisionSys.createPolygon(x0, y0, pts); const newBarrierChaser = self.chaserCollisionSys.createPolygon(x0, y0, pts); ++barrierIdCounter; - const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter); - self.latestCollisionSysMap.set(collisionBarrierIndex, newBarrierLatest); - self.chaserCollisionSysMap.set(collisionBarrierIndex, newBarrierChaser); + const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter); + self.latestCollisionSysMap.set(collisionBarrierIndex, newBarrierLatest); + self.chaserCollisionSysMap.set(collisionBarrierIndex, newBarrierChaser); } self.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')); @@ -495,7 +476,7 @@ cc.Class({ self.backgroundMapTiledIns.tmxAsset = backgroundMapTmxAsset; const newBackgroundMapSize = self.backgroundMapTiledIns.getMapSize(); const newBackgroundMapTileSize = self.backgroundMapTiledIns.getTileSize(); - self.backgroundMapTiledIns.node.setContentSize(newBackgroundMapSize.width*newBackgroundMapTileSize.width, newBackgroundMapSize.height*newBackgroundMapTileSize.height); + self.backgroundMapTiledIns.node.setContentSize(newBackgroundMapSize.width * newBackgroundMapTileSize.width, newBackgroundMapSize.height * newBackgroundMapTileSize.height); self.backgroundMapTiledIns.node.setPosition(cc.v2(0, 0)); const reqData = window.WsReq.encode({ @@ -512,90 +493,6 @@ cc.Class({ self.hideGameRuleNode(); self.transitToState(ALL_MAP_STATES.WAITING); self._inputControlEnabled = false; - - let findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer"); - window.handlePlayerAdded = function(rdf) { - // Update the "finding player" GUI and show it if not previously present - if (!self.findingPlayerNode.parent) { - self.showPopupInCanvas(self.findingPlayerNode); - } - findingPlayerScriptIns.updatePlayersInfo(rdf.playerMetas); - }; - - window.handleRoomDownsyncFrame = function(rdf) { - const frameId = rdf.id; - // Right upon establishment of the "PersistentSessionClient", we should receive an initial signal "BattleColliderInfo" earlier than any "RoomDownsyncFrame" containing "PlayerMeta" data. - switch (frameId) { - case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_READY_TO_START: - self.onBattleReadyToStart(rdf.playerMetas, false); - return; - case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START: - self.onBattleStartedOrResynced(rdf); - return; - } - - // TODO: Inject a NetworkDoctor as introduced in https://app.yinxiang.com/shard/s61/nl/13267014/5c575124-01db-419b-9c02-ec81f78c6ddc/. - }; - - window.handleInputFrameDownsyncBatch = function(batch) { - if (ALL_BATTLE_STATES.IN_BATTLE != self.battleState - && ALL_BATTLE_STATES.IN_SETTLEMENT != self.battleState) { - return; - } - - // console.log("Received inputFrameDownsyncBatch=", batch, ", now correspondingLastLocalInputFrame=", self.recentInputCache.getByFrameId(batch[batch.length-1].inputFrameId)); - let firstPredictedYetIncorrectInputFrameId = null; - let firstPredictedYetIncorrectInputFrameJoinIndex = null; - for (let k in batch) { - const inputFrameDownsync = batch[k]; - const inputFrameDownsyncId = inputFrameDownsync.inputFrameId; - const localInputFrame = self.recentInputCache.getByFrameId(inputFrameDownsyncId); - if (null == localInputFrame) { - console.warn("handleInputFrameDownsyncBatch: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false)); - } else { - if (null == firstPredictedYetIncorrectInputFrameId) { - for (let i in localInputFrame.inputList) { - if (localInputFrame.inputList[i] != inputFrameDownsync.inputList[i]) { - firstPredictedYetIncorrectInputFrameId = inputFrameDownsyncId; - firstPredictedYetIncorrectInputFrameJoinIndex = (parseInt(i)+1); - break; - } - } - } - } - self.lastAllConfirmedInputFrameId = inputFrameDownsyncId; - self._dumpToInputCache(inputFrameDownsync); - } - - if (null != firstPredictedYetIncorrectInputFrameId) { - const inputFrameId1 = firstPredictedYetIncorrectInputFrameId; - const renderFrameId1 = self._convertToFirstUsedRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" - if (renderFrameId1 < self.renderFrameId) { - /* - A typical case is as follows. - -------------------------------------------------------- - [self.lastAllConfirmedRenderFrameId] : 22 - - : 36 - - - : 62 - - [self.renderFrameId] : 64 - -------------------------------------------------------- - */ - if (renderFrameId1 < self.chaserRenderFrameId) { - // The actual rollback-and-chase would later be executed in update(dt). - console.warn("Mismatched input detected, resetting chaserRenderFrameId: inputFrameId1:", inputFrameId1, ", renderFrameId1:", renderFrameId1, ", chaserRenderFrameId before reset: ", self.chaserRenderFrameId); - self.chaserRenderFrameId = renderFrameId1; - } else { - // Deliberately left blank, chasing is ongoing. - } - } else { - // No need to rollback when "renderFrameId1 == self.renderFrameId", because the "corresponding delayedInputFrame for renderFrameId2" is NOT YET EXECUTED BY NOW, it just went through "++self.renderFrameId" in "update(dt)" and javascript-runtime is mostly single-threaded in our programmable range. - } - } - }; } // The player is now viewing "self.gameRuleNode" with button(s) to start an actual battle. -- YFLu @@ -608,15 +505,15 @@ cc.Class({ self.disableGameRuleNode(); // The player is now possibly viewing "self.gameRuleNode" with no button, and should wait for `self.initAfterWSConnected` to be called. - self.battleState = ALL_BATTLE_STATES.WAITING; + self.battleState = ALL_BATTLE_STATES.WAITING; window.initPersistentSessionClient(self.initAfterWSConnected, expectedRoomId); } else if (null != boundRoomId) { self.disableGameRuleNode(); - self.battleState = ALL_BATTLE_STATES.WAITING; + self.battleState = ALL_BATTLE_STATES.WAITING; window.initPersistentSessionClient(self.initAfterWSConnected, expectedRoomId); } else { self.showPopupInCanvas(self.gameRuleNode); - // Deliberately left blank. -- YFLu + // Deliberately left blank. -- YFLu } }, @@ -652,10 +549,27 @@ cc.Class({ onBattleStartedOrResynced(rdf) { // This function is also applicable to "re-joining". - console.log('On battle started or resynced! renderFrameId=', rdf.id); const self = window.mapIns; + const dumpRenderCacheRet = self.dumpToRenderCache(rdf); + if (window.RING_BUFF_FAILED_TO_SET == dumpRenderCacheRet) { + console.error("Something is wrong while setting the RingBuffer by frameId!"); + return dumpRenderCacheRet; + } + if (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START < rdf.id && window.RING_BUFF_CONSECUTIVE_SET == dumpRenderCacheRet) { + if (rdf.id < self.chaserRenderFrameId) { + // This "rdf.id = backend.refRenderFrameId" could be small, see comments around "room.go". + self.chaserRenderFrameId = rdf.id; + } + // In this case, we'll also got proper all-confirmed inputFrames for advancing the renderFrames in the coming "update(dt)" + return dumpRenderCacheRet; + } + + // The logic below applies to (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START == rdf.id || window.RING_BUFF_NON_CONSECUTIVE_SET == dumpRenderCacheRet) + console.log('On battle started or resynced! renderFrameId=', rdf.id); + self.renderFrameId = rdf.id; - self.lastAllConfirmedRenderFrameId = rdf.id; + self.lastRenderFrameIdTriggeredAt = performance.now(); + self.lastAllConfirmedRenderFrameId = rdf.id; self.chaserRenderFrameId = rdf.id; const players = rdf.players; @@ -682,30 +596,105 @@ cc.Class({ self.countdownToBeginGameNode.parent.removeChild(self.countdownToBeginGameNode); } self.transitToState(ALL_MAP_STATES.VISUAL); + self.battleState = ALL_BATTLE_STATES.IN_BATTLE; self.applyRoomDownsyncFrameDynamics(rdf); - self._dumpToRenderCache(rdf); - self.battleState = ALL_BATTLE_STATES.IN_BATTLE; // Starts the increment of "self.renderFrameId" in "self.update(dt)" - self.lastRenderFrameIdTriggeredAt = performance.now(); - if (null != window.boundRoomId) { - self.boundRoomIdLabel.string = window.boundRoomId; + + return dumpRenderCacheRet; + }, + + equalInputLists(lhs, rhs) { + if (null == lhs || null == rhs) return false; + if (lhs.length != rhs.length) return false; + for (let i in lhs) { + if (lhs[i] == rhs[i]) continue; + return false; } + return true; + }, + + onInputFrameDownsyncBatch(batch, dumpRenderCacheRet /* second param is default to null */ ) { + const self = this; + if (ALL_BATTLE_STATES.IN_BATTLE != self.battleState + && ALL_BATTLE_STATES.IN_SETTLEMENT != self.battleState) { + return; + } + + let firstPredictedYetIncorrectInputFrameId = null; + for (let k in batch) { + const inputFrameDownsync = batch[k]; + const inputFrameDownsyncId = inputFrameDownsync.inputFrameId; + if (window.RING_BUFF_NON_CONSECUTIVE_SET == dumpRenderCacheRet) { + // Deliberately left blank, in this case "chaserRenderFrameId" is already reset to proper value. + } else { + const localInputFrame = self.recentInputCache.getByFrameId(inputFrameDownsyncId); + if (null == localInputFrame) { + console.warn("localInputFrame not existing: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false)); + } else if (null == firstPredictedYetIncorrectInputFrameId && !self.equalInputLists(localInputFrame.inputList, inputFrameDownsync.inputList)) { + firstPredictedYetIncorrectInputFrameId = inputFrameDownsyncId; + } + } + self.lastAllConfirmedInputFrameId = inputFrameDownsyncId; + self.dumpToInputCache(inputFrameDownsync); + } + + if (null != firstPredictedYetIncorrectInputFrameId) { + const inputFrameId1 = firstPredictedYetIncorrectInputFrameId; + const renderFrameId1 = self._convertToFirstUsedRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" + if (renderFrameId1 < self.renderFrameId) { + /* + A typical case is as follows. + -------------------------------------------------------- + [self.lastAllConfirmedRenderFrameId] : 22 + + : 36 + + + : 62 + + [self.renderFrameId] : 64 + -------------------------------------------------------- + */ + if (renderFrameId1 < self.chaserRenderFrameId) { + // The actual rollback-and-chase would later be executed in update(dt). + console.warn("Mismatched input detected, resetting chaserRenderFrameId: inputFrameId1:", inputFrameId1, ", renderFrameId1:", renderFrameId1, ", chaserRenderFrameId before reset: ", self.chaserRenderFrameId); + self.chaserRenderFrameId = renderFrameId1; + } else { + // Deliberately left blank, chasing is ongoing. + } + } else { + // No need to rollback when "renderFrameId1 == self.renderFrameId", because the "corresponding delayedInputFrame for renderFrameId2" is NOT YET EXECUTED BY NOW, it just went through "++self.renderFrameId" in "update(dt)" and javascript-runtime is mostly single-threaded in our programmable range. + } + } + }, + + onPlayerAdded(rdf) { + const self = this; + // Update the "finding player" GUI and show it if not previously present + if (!self.findingPlayerNode.parent) { + self.showPopupInCanvas(self.findingPlayerNode); + } + let findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer"); + findingPlayerScriptIns.updatePlayersInfo(rdf.playerMetas); }, logBattleStats() { const self = this; let s = []; s.push("Battle stats: lastUpsyncInputFrameId=" + self.lastUpsyncInputFrameId + ", lastAllConfirmedInputFrameId=" + self.lastAllConfirmedInputFrameId); - + for (let i = self.recentInputCache.stFrameId; i < self.recentInputCache.edFrameId; ++i) { - const inputFrameDownsync = self.recentInputCache.getByFrameId(i); + const inputFrameDownsync = self.recentInputCache.getByFrameId(i); s.push(JSON.stringify(inputFrameDownsync)); - } + } console.log(s.join('\n')); }, onBattleStopped() { const self = this; + if (ALL_BATTLE_STATES.IN_BATTLE != self.battleState) { + return; + } self.countdownNanos = null; self.logBattleStats(); if (self.musicEffectManagerScriptIns) { @@ -741,7 +730,10 @@ cc.Class({ newPlayerNode.active = true; const playerScriptIns = newPlayerNode.getComponent("SelfPlayer"); - playerScriptIns.scheduleNewDirection({dx: 0, dy: 0}, true); + playerScriptIns.scheduleNewDirection({ + dx: 0, + dy: 0 + }, true); return [newPlayerNode, playerScriptIns]; }, @@ -750,45 +742,47 @@ cc.Class({ const self = this; if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { const elapsedMillisSinceLastFrameIdTriggered = performance.now() - self.lastRenderFrameIdTriggeredAt; - if (elapsedMillisSinceLastFrameIdTriggered < (self.rollbackEstimatedDtMillis)) { + if (elapsedMillisSinceLastFrameIdTriggered < (self.rollbackEstimatedDtMillis)) { // console.debug("Avoiding too fast frame@renderFrameId=", self.renderFrameId, ": elapsedMillisSinceLastFrameIdTriggered=", elapsedMillisSinceLastFrameIdTriggered); return; } try { - let st = performance.now(); - let prevSelfInput = null, currSelfInput = null; - const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here - if (self.shouldGenerateInputFrameUpsync(self.renderFrameId)) { - const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId); - prevSelfInput = prevAndCurrInputs[0]; - currSelfInput = prevAndCurrInputs[1]; - } + let st = performance.now(); + let prevSelfInput = null, + currSelfInput = null; + const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here + if (self.shouldGenerateInputFrameUpsync(self.renderFrameId)) { + const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId); + prevSelfInput = prevAndCurrInputs[0]; + currSelfInput = prevAndCurrInputs[1]; + } - let t0 = performance.now(); - if (self.shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, self.lastUpsyncInputFrameId, noDelayInputFrameId)) { - // TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously? - self.sendInputFrameUpsyncBatch(noDelayInputFrameId); - } + let t0 = performance.now(); + if (self.shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, self.lastUpsyncInputFrameId, noDelayInputFrameId)) { + // TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously? + self.sendInputFrameUpsyncBatch(noDelayInputFrameId); + } - let t1 = performance.now(); - // Use "fractional-frame-chasing" to guarantee that "self.update(dt)" is not jammed by a "large range of frame-chasing". See `/ConcerningEdgeCases.md` for the motivation. - const prevChaserRenderFrameId = self.chaserRenderFrameId; - let nextChaserRenderFrameId = (prevChaserRenderFrameId + self.maxChasingRenderFramesPerUpdate); - if (nextChaserRenderFrameId > self.renderFrameId) nextChaserRenderFrameId = self.renderFrameId; - self.rollbackAndChase(prevChaserRenderFrameId, nextChaserRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap); - self.chaserRenderFrameId = nextChaserRenderFrameId; // Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic! - let t2 = performance.now(); + let t1 = performance.now(); + // Use "fractional-frame-chasing" to guarantee that "self.update(dt)" is not jammed by a "large range of frame-chasing". See `/ConcerningEdgeCases.md` for the motivation. + const prevChaserRenderFrameId = self.chaserRenderFrameId; + let nextChaserRenderFrameId = (prevChaserRenderFrameId + self.maxChasingRenderFramesPerUpdate); + if (nextChaserRenderFrameId > self.renderFrameId) + nextChaserRenderFrameId = self.renderFrameId; + self.rollbackAndChase(prevChaserRenderFrameId, nextChaserRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap); + self.chaserRenderFrameId = nextChaserRenderFrameId; // Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic! + let t2 = performance.now(); - // Inside "self.rollbackAndChase", the "self.latestCollisionSys" is ALWAYS ROLLED BACK to "self.recentRenderCache.get(self.renderFrameId)" before being applied dynamics from corresponding inputFrameDownsync, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now. - const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId+1, self.latestCollisionSys, self.latestCollisionSysMap); - self.applyRoomDownsyncFrameDynamics(rdf); - let t3 = performance.now(); + // Inside "self.rollbackAndChase", the "self.latestCollisionSys" is ALWAYS ROLLED BACK to "self.recentRenderCache.get(self.renderFrameId)" before being applied dynamics from corresponding inputFrameDownsync, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now. + const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.latestCollisionSys, self.latestCollisionSysMap); + self.applyRoomDownsyncFrameDynamics(rdf); + let t3 = performance.now(); } catch (err) { console.error("Error during Map.update", err); } finally { // Update countdown if (null != self.countdownNanos) { - self.countdownNanos -= self.rollbackEstimatedDt*1000000000; + self.countdownNanos -= self.rollbackEstimatedDt * 1000000000; if (self.countdownNanos <= 0) { self.onBattleStopped(self.playerRichInfoDict); return; @@ -820,14 +814,16 @@ cc.Class({ const selfPlayerStr = cc.sys.localStorage.getItem("selfPlayer"); if (null == selfPlayerStr) { localClearance(); - return; + return; } const selfPlayerInfo = JSON.parse(selfPlayerStr); try { NetworkUtils.ajax({ url: backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER + constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.INT_AUTH_TOKEN + constants.ROUTE_PATH.LOGOUT, type: "POST", - data: { intAuthToken: selfPlayerInfo.intAuthToken }, + data: { + intAuthToken: selfPlayerInfo.intAuthToken + }, success: function(res) { if (res.ret != constants.RET_CODE.OK) { console.log("Logout failed: ", res); @@ -862,7 +858,7 @@ cc.Class({ onGameRule1v1ModeClicked(evt, cb) { const self = this; - self.battleState = ALL_BATTLE_STATES.WAITING; + self.battleState = ALL_BATTLE_STATES.WAITING; window.initPersistentSessionClient(self.initAfterWSConnected, null /* Deliberately NOT passing in any `expectedRoomId`. -- YFLu */ ); self.hideGameRuleNode(); }, @@ -888,8 +884,8 @@ cc.Class({ }; if (true == isSelfRejoining) { - hideFindingPlayersGUI(); - } else { + hideFindingPlayersGUI(); + } else { // Delay to hide the "finding player" GUI, then show a countdown clock window.setTimeout(() => { hideFindingPlayersGUI(); @@ -902,10 +898,10 @@ cc.Class({ _createRoomDownsyncFrameLocally(renderFrameId, collisionSys, collisionSysMap) { const self = this; - const prevRenderFrameId = renderFrameId-1; + const prevRenderFrameId = renderFrameId - 1; const inputFrameAppliedOnPrevRenderFrame = ( - 0 > prevRenderFrameId - ? + 0 > prevRenderFrameId + ? null : self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(prevRenderFrameId, self.inputDelayFrames)) @@ -914,8 +910,8 @@ cc.Class({ // TODO: Find a better way to assign speeds instead of using "speedRefRenderFrameId". const speedRefRenderFrameId = prevRenderFrameId; const speedRefRenderFrame = ( - 0 > speedRefRenderFrameId - ? + 0 > speedRefRenderFrameId + ? null : self.recentRenderCache.getByFrameId(speedRefRenderFrameId) @@ -924,32 +920,32 @@ cc.Class({ const rdf = { id: renderFrameId, refFrameId: renderFrameId, - players: {} + players: {} }; self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { - const joinIndex = playerRichInfo.joinIndex; + const joinIndex = playerRichInfo.joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const playerCollider = collisionSysMap.get(collisionPlayerIndex); rdf.players[playerRichInfo.id] = { id: playerRichInfo.id, x: playerCollider.x, y: playerCollider.y, - dir: self.ctrl.decodeDirection(null == inputFrameAppliedOnPrevRenderFrame ? 0 : inputFrameAppliedOnPrevRenderFrame.inputList[joinIndex-1]), + dir: self.ctrl.decodeDirection(null == inputFrameAppliedOnPrevRenderFrame ? 0 : inputFrameAppliedOnPrevRenderFrame.inputList[joinIndex - 1]), speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed), - joinIndex: joinIndex + joinIndex: joinIndex }; }); if ( - null != inputFrameAppliedOnPrevRenderFrame && self._allConfirmed(inputFrameAppliedOnPrevRenderFrame.confirmedList) - && + null != inputFrameAppliedOnPrevRenderFrame && self._allConfirmed(inputFrameAppliedOnPrevRenderFrame.confirmedList) + && self.lastAllConfirmedRenderFrameId >= prevRenderFrameId && rdf.id > self.lastAllConfirmedRenderFrameId ) { - self.lastAllConfirmedRenderFrameId = rdf.id; + self.lastAllConfirmedRenderFrameId = rdf.id; self.chaserRenderFrameId = rdf.id; // it must be true that "chaserRenderFrameId >= lastAllConfirmedRenderFrameId" } - self._dumpToRenderCache(rdf); + self.dumpToRenderCache(rdf); return rdf; }, @@ -960,20 +956,20 @@ cc.Class({ const immediatePlayerInfo = rdf.players[playerId]; playerRichInfo.node.setPosition(immediatePlayerInfo.x, immediatePlayerInfo.y); playerRichInfo.scriptIns.scheduleNewDirection(immediatePlayerInfo.dir, true); - playerRichInfo.scriptIns.updateSpeed(immediatePlayerInfo.speed); + playerRichInfo.scriptIns.updateSpeed(immediatePlayerInfo.speed); }); - }, + }, getCachedInputFrameDownsyncWithPrediction(inputFrameId) { const self = this; let inputFrameDownsync = self.recentInputCache.getByFrameId(inputFrameId); if (null != inputFrameDownsync && -1 != self.lastAllConfirmedInputFrameId && inputFrameId > self.lastAllConfirmedInputFrameId) { - const lastAllConfirmedInputFrame = self.recentInputCache.getByFrameId(self.lastAllConfirmedInputFrameId); + const lastAllConfirmedInputFrame = self.recentInputCache.getByFrameId(self.lastAllConfirmedInputFrameId); for (let i = 0; i < inputFrameDownsync.inputList.length; ++i) { - if (i == self.selfPlayerInfo.joinIndex-1) continue; - inputFrameDownsync.inputList[i] = lastAllConfirmedInputFrame.inputList[i]; + if (i == self.selfPlayerInfo.joinIndex - 1) continue; + inputFrameDownsync.inputList[i] = lastAllConfirmedInputFrame.inputList[i]; } - } + } return inputFrameDownsync; }, @@ -986,13 +982,13 @@ cc.Class({ const self = this; const renderFrameSt = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame" if (null == renderFrameSt) { - console.error("Couldn't find renderFrameId=", renderFrameIdSt, " to rollback, recentRenderCache=", self._stringifyRecentRenderCache(false)); + console.error("Couldn't find renderFrameId=", renderFrameIdSt, " to rollback, lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false)); } /* Reset "position" of players in "collisionSys" according to "renderFrameSt". The easy part is that we don't have path-dependent-integrals to worry about like that of thermal dynamics. */ self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { - const joinIndex = playerRichInfo.joinIndex; + const joinIndex = playerRichInfo.joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const playerCollider = collisionSysMap.get(collisionPlayerIndex); const player = renderFrameSt.players[playerId]; @@ -1008,30 +1004,29 @@ cc.Class({ for (let i = renderFrameIdSt; i < renderFrameIdEd; ++i) { const renderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame" const j = self._convertToInputFrameId(i, self.inputDelayFrames); - const inputList = self.getCachedInputFrameDownsyncWithPrediction(j).inputList; + const inputFrameDownsync = self.getCachedInputFrameDownsyncWithPrediction(j); + if (null == inputFrameDownsync) { + console.error("Failed to get cached inputFrameDownsync for renderFrameId=", i, ", inputFrameId=", j, "lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false)); + } + const inputList = inputFrameDownsync.inputList; for (let j in self.playerRichInfoArr) { - const joinIndex = parseInt(j) + 1; - const playerId = self.playerRichInfoArr[j].id; + const joinIndex = parseInt(j) + 1; + const playerId = self.playerRichInfoArr[j].id; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const playerCollider = collisionSysMap.get(collisionPlayerIndex); const player = renderFrame.players[playerId]; - const encodedInput = inputList[joinIndex-1]; + const encodedInput = inputList[joinIndex - 1]; const decodedInput = self.ctrl.decodeDirection(encodedInput); - const baseChange = player.speed*self.rollbackEstimatedDt*decodedInput.speedFactor; - playerCollider.x += baseChange*decodedInput.dx; - playerCollider.y += baseChange*decodedInput.dy; - /* - if (0 < encodedInput) { - console.log("playerId=", playerId, "@renderFrameId=", i, ", delayedInputFrameId=", j, ", baseChange=", baseChange, ": x=", playerCollider.x, ", y=", playerCollider.y); - } - */ + const baseChange = player.speed * self.rollbackEstimatedDt * decodedInput.speedFactor; + playerCollider.x += baseChange * decodedInput.dx; + playerCollider.y += baseChange * decodedInput.dy; } collisionSys.update(); const result = collisionSys.createResult(); // Can I reuse a "self.latestCollisionSysResult" object throughout the whole battle? - + for (let i in self.playerRichInfoArr) { - const joinIndex = parseInt(i) + 1; + const joinIndex = parseInt(i) + 1; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const playerCollider = collisionSysMap.get(collisionPlayerIndex); const potentials = playerCollider.potentials(); @@ -1044,10 +1039,10 @@ cc.Class({ } } } - + return self._createRoomDownsyncFrameLocally(renderFrameIdEd, collisionSys, collisionSysMap); - }, - + }, + _initPlayerRichInfoDict(players, playerMetas) { const self = this; for (let k in players) { @@ -1056,7 +1051,7 @@ cc.Class({ const immediatePlayerInfo = players[playerId]; const immediatePlayerMeta = playerMetas[playerId]; const nodeAndScriptIns = self.spawnPlayerNode(immediatePlayerInfo.joinIndex, immediatePlayerInfo.x, immediatePlayerInfo.y); - self.playerRichInfoDict.set(playerId, immediatePlayerInfo); + self.playerRichInfoDict.set(playerId, immediatePlayerInfo); Object.assign(self.playerRichInfoDict.get(playerId), { node: nodeAndScriptIns[0], @@ -1064,14 +1059,14 @@ cc.Class({ }); if (self.selfPlayerInfo.id == playerId) { - self.selfPlayerInfo = Object.assign(self.selfPlayerInfo, immediatePlayerInfo); + self.selfPlayerInfo = Object.assign(self.selfPlayerInfo, immediatePlayerInfo); nodeAndScriptIns[1].showArrowTipNode(); } } self.playerRichInfoArr = new Array(self.playerRichInfoDict.size); self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { - self.playerRichInfoArr[playerRichInfo.joinIndex-1] = playerRichInfo; - }); + self.playerRichInfoArr[playerRichInfo.joinIndex - 1] = playerRichInfo; + }); }, _stringifyRecentInputCache(usefullOutput) { diff --git a/frontend/assets/scripts/RingBuffer.js b/frontend/assets/scripts/RingBuffer.js index 6262c6d..57310fe 100644 --- a/frontend/assets/scripts/RingBuffer.js +++ b/frontend/assets/scripts/RingBuffer.js @@ -1,3 +1,7 @@ +window.RING_BUFF_CONSECUTIVE_SET = 0; +window.RING_BUFF_NON_CONSECUTIVE_SET = 1; +window.RING_BUFF_FAILED_TO_SET = 2; + var RingBuffer = function(capacity) { this.ed = 0; // write index, open index this.st = 0; // read index, closed index @@ -32,15 +36,15 @@ RingBuffer.prototype.pop = function() { return item; }; -RingBuffer.prototype.getByOffset = function(offsetFromSt) { - if (0 == this.cnt) { +RingBuffer.prototype.getArrIdxByOffset = function(offsetFromSt) { + if (0 > offsetFromSt || 0 == this.cnt) { return null; } let arrIdx = this.st + offsetFromSt; if (this.st < this.ed) { // case#1: 0...st...ed...n-1 if (this.st <= arrIdx && arrIdx < this.ed) { - return this.eles[arrIdx]; + return arrIdx; } } else { // if this.st >= this.sd @@ -49,7 +53,7 @@ RingBuffer.prototype.getByOffset = function(offsetFromSt) { arrIdx -= this.n } if (arrIdx >= this.st || arrIdx < this.ed) { - return this.eles[arrIdx]; + return arrIdx; } } @@ -57,7 +61,40 @@ RingBuffer.prototype.getByOffset = function(offsetFromSt) { }; RingBuffer.prototype.getByFrameId = function(frameId) { - return this.getByOffset(frameId - this.stFrameId); + const arrIdx = this.getArrIdxByOffset(frameId - this.stFrameId); + return (null == arrIdx ? null : this.eles[arrIdx]); +}; + +// [WARNING] During a battle, frontend could receive non-consecutive frames (either renderFrame or inputFrame) due to resync, the buffer should handle these frames properly. +RingBuffer.prototype.setByFrameId = function(item, frameId) { + if (frameId < this.stFrameId) { + console.error("Invalid putByFrameId#1: stFrameId=", stFrameId, ", edFrameId=", edFrameId, ", incoming item=", item); + return window.RING_BUFF_FAILED_TO_SET; + } + const arrIdx = this.getArrIdxByOffset(frameId - this.stFrameId); + if (null != arrIdx) { + this.eles[arrIdx] = item; + return window.RING_BUFF_CONSECUTIVE_SET; + } + + // When "null == arrIdx", should it still be deemed consecutive if "frameId == edFrameId" prior to the reset? + let ret = window.RING_BUFF_CONSECUTIVE_SET; + if (this.edFrameId < frameId) { + this.st = this.ed = 0; + this.stFrameId = this.edFrameId = frameId; + this.cnt = 0; + ret = window.RING_BUFF_NON_CONSECUTIVE_SET; + } + + this.eles[this.ed] = item + this.edFrameId++; + this.cnt++; + this.ed++; + if (this.ed >= this.n) { + this.ed -= this.n; // Deliberately not using "%" operator for performance concern + } + + return ret; }; module.exports = RingBuffer; diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index 38bd561..bf4efb8 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -1,3 +1,5 @@ +const RingBuffer = require('./RingBuffer'); + window.UPSYNC_MSG_ACT_HB_PING = 1; window.UPSYNC_MSG_ACT_PLAYER_CMD = 2; window.UPSYNC_MSG_ACT_PLAYER_COLLIDER_ACK = 3; @@ -8,7 +10,7 @@ window.DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START = -1; window.DOWNSYNC_MSG_ACT_BATTLE_START = 0; window.DOWNSYNC_MSG_ACT_HB_REQ = 1; window.DOWNSYNC_MSG_ACT_INPUT_BATCH = 2; -window.DOWNSYNC_MSG_ACT_ROOM_FRAME = 3; +window.DOWNSYNC_MSG_ACT_BATTLE_STOPPED = 3; window.DOWNSYNC_MSG_ACT_FORCED_RESYNC = 4; @@ -160,30 +162,28 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { window.handleHbRequirements(resp); // 获取boundRoomId并存储到localStorage break; case window.DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED: - window.handlePlayerAdded(resp.rdf); + mapIns.onPlayerAdded(resp.rdf); break; case window.DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED: // Deliberately left blank for now break; case window.DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START: + mapIns.onBattleReadyToStart(resp.rdf.playerMetas, false); + break; case window.DOWNSYNC_MSG_ACT_BATTLE_START: - case window.DOWNSYNC_MSG_ACT_ROOM_FRAME: - if (window.handleRoomDownsyncFrame) { - window.handleRoomDownsyncFrame(resp.rdf); - } + mapIns.onBattleStartedOrResynced(resp.rdf); + break; + case window.DOWNSYNC_MSG_ACT_BATTLE_STOPPED: + mapIns.onBattleStopped(); break; case window.DOWNSYNC_MSG_ACT_INPUT_BATCH: - if (window.handleInputFrameDownsyncBatch) { - window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); - } + mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); break; case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC: - if (window.handleInputFrameDownsyncBatch && window.handleRoomDownsyncFrame) { - console.warn("Got forced resync:", JSON.stringify(resp), " @localRenderFrameId=", mapIns.renderFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false)); - // The following order of execution is important, because "handleInputFrameDownsyncBatch" is only available when state is IN_BATTLE - window.handleRoomDownsyncFrame(resp.rdf); - window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); - } + console.warn("Got forced resync:", JSON.stringify(resp), " @localRenderFrameId=", mapIns.renderFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false)); + // The following order of execution is important, because "onInputFrameDownsyncBatch" is only available when state is IN_BATTLE + const dumpRenderCacheRet = mapIns.onBattleStartedOrResynced(resp.rdf); + mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch, dumpRenderCacheRet); break; default: break; From 09b12c5b16767e8a519c89cfa06d8e6920a1172d Mon Sep 17 00:00:00 2001 From: genxium Date: Mon, 3 Oct 2022 11:42:19 +0800 Subject: [PATCH 11/13] Updated documentation. --- README.md | 30 +-- battle_srv/common/utils/wechat.go | 16 +- battle_srv/env_tools/load_pre_conf.go | 2 +- battle_srv/go.mod | 34 +-- battle_srv/go.sum | 6 + .../models/in_range_player_collection.go | 2 +- battle_srv/models/room.go | 212 ++++++++---------- frontend/assets/scenes/login.fire | 2 +- frontend/assets/scripts/Map.js | 45 ++-- frontend/assets/scripts/RingBuffer.js | 2 +- frontend/assets/scripts/WsSessionMgr.js | 6 +- 11 files changed, 180 insertions(+), 177 deletions(-) diff --git a/README.md b/README.md index 47073f9..a1ad3a5 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,15 @@ If you'd like to play with the backend code seriously, please read the detailed There could be some left over wechat-game related code pieces, but they're neither meant to work nor supported anymore. -# 1. Database Server +# 1. Building & running -The database product to be used for this project is MySQL 5.7. +## 1.1 Golang1.19.1 +Documentation TBD. -We use [skeema](https://github.com/skeema/skeema) for schematic synchronization under `/database/skeema-repo-root/` which intentionally doesn't contain a `.skeema` file. Please read [this tutorial](https://shimo.im/doc/wQ0LvB0rlZcbHF5V) for more information. +## 1.2 MySQL +The database product to be used for this project is MySQL 5.7, you can install and manage `MySQL` server by [these scripts](https://github.com/genxium/Ubuntu14InitScripts/tree/master/database/mysql). -You can use [this node module (still under development)](https://github.com/genxium/node-mysqldiff-bridge) instead under `Windows10`, other versions of Windows are not yet tested for compatibility. +We use [skeema](https://github.com/skeema/skeema) for schematic synchronization under `/database/skeema-repo-root/` which intentionally doesn't contain a `.skeema` file. Please read [this tutorial](https://shimo.im/doc/wQ0LvB0rlZcbHF5V) for more information. For `Windows 10/11`, you can compile `skeema` from source and config the host to be `127.0.0.1` instead of `localhost` to use it, i.e. circumventing the pitfall for MySQL unix socket connection on Windows. The following command(s) ``` @@ -21,33 +23,25 @@ user@proj-root/database/skeema-repo-root> skeema diff ``` is recommended to be used for checking difference from your "live MySQL server" to the latest expected schema tracked in git. -# 2. Building & running +## 1.3 Required Config Files -## 2.1 Golang1.11 -See https://github.com/genxium/Go111ModulePrac for details. - -## 2.2 MySQL -On a product machine, you can install and manage `MySQL` server by [these scripts](https://github.com/genxium/Ubuntu14InitScripts/tree/master/database/mysql). - -## 2.3 Required Config Files - -### 2.3.1 Backend +### 1.3.1 Backend - It needs `/battle_srv/configs/*` which is generated by `cd /battle_srv && cp -r ./configs.template ./configs` and necessary customization. -### 2.3.2 Frontend +### 1.3.2 Frontend - It needs CocosCreator v2.2.1 to build. - A required "CocosCreator plugin `i18n`" is already enclosed in the project, if you have a globally installed "CocosCreator plugin `i18n`"(often located at `$HOME/.CocosCreator/packages/`) they should be OK to co-exist. - It needs `/frontend/assets/plugin_scripts/conf.js` which is generated by `cd /frontend/assets/plugin_scripts && cp conf.js.template conf.js`. -## 2.4 Troubleshooting +## 1.4 Troubleshooting -### 2.4.1 Redis snapshot writing failure +### 1.4.1 Redis snapshot writing failure ``` ErrFatal {"err": "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error."} ``` Just restart your `redis-server` process. -# 3. Git configs cautions +# 2. Git configs cautions Please make sure that you've set `ignorecase = false` in your `[core] section of /.git/config`. diff --git a/battle_srv/common/utils/wechat.go b/battle_srv/common/utils/wechat.go index 55ed8ea..b77f5ab 100644 --- a/battle_srv/common/utils/wechat.go +++ b/battle_srv/common/utils/wechat.go @@ -94,7 +94,7 @@ func (w *wechat) GetJsConfig(uri string) (config *JsConfig, err error) { return } -//TODO add cache, getTicket 获取jsapi_ticket +// TODO add cache, getTicket 获取jsapi_ticket func (w *wechat) getTicket() (ticketStr string, err error) { var ticket resTicket ticket, err = w.getTicketFromServer() @@ -131,7 +131,7 @@ func (w *wechat) GetOauth2Basic(authcode string) (result resAccessToken, err err return } -//UserInfo 用户授权获取到用户信息 +// UserInfo 用户授权获取到用户信息 type UserInfo struct { CommonError OpenID string `json:"openid"` @@ -164,7 +164,7 @@ func (w *wechat) GetMoreInfo(accessToken string, openId string) (result UserInfo return } -//HTTPGet get 请求 +// HTTPGet get 请求 func get(uri string) ([]byte, error) { response, err := http.Get(uri) if err != nil { @@ -182,7 +182,7 @@ func get(uri string) ([]byte, error) { return body, err } -//PostJSON post json 数据请求 +// PostJSON post json 数据请求 func post(uri string, obj interface{}) ([]byte, error) { jsonData, err := json.Marshal(obj) if err != nil { @@ -206,7 +206,7 @@ func post(uri string, obj interface{}) ([]byte, error) { return ioutil.ReadAll(response.Body) } -//Signature sha1签名 +// Signature sha1签名 func signature(params ...string) string { sort.Strings(params) h := sha1.New() @@ -216,7 +216,7 @@ func signature(params ...string) string { return fmt.Sprintf("%x", h.Sum(nil)) } -//RandomStr 随机生成字符串 +// RandomStr 随机生成字符串 func randomStr(length int) string { str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" bytes := []byte(str) @@ -228,7 +228,7 @@ func randomStr(length int) string { return string(result) } -//getTicketFromServer 强制从服务器中获取ticket +// getTicketFromServer 强制从服务器中获取ticket func (w *wechat) getTicketFromServer() (ticket resTicket, err error) { var accessToken string accessToken, err = w.getAccessTokenFromServer() @@ -256,7 +256,7 @@ func (w *wechat) getTicketFromServer() (ticket resTicket, err error) { return } -//GetAccessTokenFromServer 强制从微信服务器获取token +// GetAccessTokenFromServer 强制从微信服务器获取token func (w *wechat) getAccessTokenFromServer() (accessToken string, err error) { AccessTokenURL := w.config.ApiProtocol + "://" + w.config.ApiGateway + "/cgi-bin/token" url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s", AccessTokenURL, w.config.AppID, w.config.AppSecret) diff --git a/battle_srv/env_tools/load_pre_conf.go b/battle_srv/env_tools/load_pre_conf.go index d797942..55c6a97 100644 --- a/battle_srv/env_tools/load_pre_conf.go +++ b/battle_srv/env_tools/load_pre_conf.go @@ -66,7 +66,7 @@ func createMysqlData(rows *sqlx.Rows, v string) { } } -//加上tableName参数, 用于pre_conf_data.sqlite里bot_player表的复用 --kobako +// 加上tableName参数, 用于pre_conf_data.sqlite里bot_player表的复用 --kobako func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) { var ls []*dbBotPlayer err := db.Select(&ls, "SELECT name, magic_phone_country_code, magic_phone_num, display_name FROM "+tableName) diff --git a/battle_srv/go.mod b/battle_srv/go.mod index 555734c..816c005 100644 --- a/battle_srv/go.mod +++ b/battle_srv/go.mod @@ -1,38 +1,46 @@ module server +go 1.19 + require ( github.com/ByteArena/box2d v1.0.2 - github.com/ChimeraCoder/gojson v1.0.0 // indirect github.com/Masterminds/squirrel v0.0.0-20180815162352-8a7e65843414 github.com/davecgh/go-spew v1.1.1 - github.com/fatih/color v1.7.0 // indirect github.com/gin-contrib/cors v0.0.0-20180514151808-6f0a820f94be - github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect github.com/gin-gonic/gin v1.3.0 - github.com/githubnemo/CompileDaemon v1.0.0 // indirect github.com/go-redis/redis v6.13.2+incompatible github.com/go-sql-driver/mysql v1.4.0 github.com/golang/protobuf v1.5.2 - github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.2.0 github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186 - github.com/howeyc/fsnotify v0.9.0 // indirect github.com/imdario/mergo v0.3.6 github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 - github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect - github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e - github.com/mattn/go-colorable v0.0.9 // indirect - github.com/mattn/go-isatty v0.0.3 // indirect - github.com/mattn/go-sqlite3 v1.9.0 github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 github.com/solarlune/resolv v0.5.1 github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713 + go.uber.org/zap v1.9.1 + google.golang.org/protobuf v1.28.1 +) + +require ( + github.com/ChimeraCoder/gojson v1.0.0 // indirect + github.com/fatih/color v1.7.0 // indirect + github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect + github.com/githubnemo/CompileDaemon v1.0.0 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/howeyc/fsnotify v0.9.0 // indirect + github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-sqlite3 v1.14.15 // indirect github.com/ugorji/go v1.1.1 // indirect go.uber.org/atomic v1.3.2 // indirect go.uber.org/multierr v1.1.0 // indirect - go.uber.org/zap v1.9.1 - google.golang.org/protobuf v1.28.1 + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 // indirect gopkg.in/go-playground/validator.v8 v8.18.2 // indirect gopkg.in/yaml.v2 v2.2.1 // indirect ) diff --git a/battle_srv/go.sum b/battle_srv/go.sum index 984ae1f..9150fe4 100644 --- a/battle_srv/go.sum +++ b/battle_srv/go.sum @@ -57,8 +57,12 @@ github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRU github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE= @@ -75,6 +79,8 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/battle_srv/models/in_range_player_collection.go b/battle_srv/models/in_range_player_collection.go index 8d53202..253f008 100644 --- a/battle_srv/models/in_range_player_collection.go +++ b/battle_srv/models/in_range_player_collection.go @@ -86,7 +86,7 @@ func (p *InRangePlayerCollection) NextPlayerToAttack() *InRangePlayerNode { //TODO: 完成重构 -/// Doubly circular linked list Implement +// / Doubly circular linked list Implement type InRangePlayerNode struct { Prev *InRangePlayerNode Next *InRangePlayerNode diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 6a8a718..0677452 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -159,7 +159,8 @@ type Room struct { EffectivePlayerCount int32 DismissalWaitGroup sync.WaitGroup Barriers map[int32]*Barrier - AllPlayerInputsBuffer *RingBuffer + InputsBuffer *RingBuffer // Indices are STRICTLY consecutive + DiscreteInputsBuffer sync.Map // Indices are NOT NECESSARILY consecutive RenderFrameBuffer *RingBuffer LastAllConfirmedInputFrameId int32 LastAllConfirmedInputFrameIdWithChange int32 @@ -357,16 +358,16 @@ func (pR *Room) RenderFrameBufferString() string { return fmt.Sprintf("{renderFrameId: %d, stRenderFrameId: %d, edRenderFrameId: %d, lastAllConfirmedRenderFrameId: %d}", pR.RenderFrameId, pR.RenderFrameBuffer.StFrameId, pR.RenderFrameBuffer.EdFrameId, pR.CurDynamicsRenderFrameId) } -func (pR *Room) AllPlayerInputsBufferString(allDetails bool) string { +func (pR *Room) InputsBufferString(allDetails bool) string { if allDetails { // Appending of the array of strings can be very SLOW due to on-demand heap allocation! Use this printing with caution. s := make([]string, 0) - s = append(s, fmt.Sprintf("{renderFrameId: %v, stInputFrameId: %v, edInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v, lastAllConfirmedInputFrameId: %v}", pR.RenderFrameId, pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId)) + s = append(s, fmt.Sprintf("{renderFrameId: %v, stInputFrameId: %v, edInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v, lastAllConfirmedInputFrameId: %v}", pR.RenderFrameId, pR.InputsBuffer.StFrameId, pR.InputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId)) for playerId, player := range pR.Players { s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId)) } - for i := pR.AllPlayerInputsBuffer.StFrameId; i < pR.AllPlayerInputsBuffer.EdFrameId; i++ { - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(i) + for i := pR.InputsBuffer.StFrameId; i < pR.InputsBuffer.EdFrameId; i++ { + tmp := pR.InputsBuffer.GetByFrameId(i) if nil == tmp { break } @@ -376,7 +377,7 @@ func (pR *Room) AllPlayerInputsBufferString(allDetails bool) string { return strings.Join(s, "; ") } else { - return fmt.Sprintf("{renderFrameId: %d, stInputFrameId: %d, edInputFrameId: %d, lastAllConfirmedInputFrameIdWithChange: %d, lastAllConfirmedInputFrameId: %d}", pR.RenderFrameId, pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId) + return fmt.Sprintf("{renderFrameId: %d, stInputFrameId: %d, edInputFrameId: %d, lastAllConfirmedInputFrameIdWithChange: %d, lastAllConfirmedInputFrameId: %d}", pR.RenderFrameId, pR.InputsBuffer.StFrameId, pR.InputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId) } } @@ -424,7 +425,7 @@ func (pR *Room) StartBattle() { stCalculation := utils.UnixtimeNano() if totalElapsedNanos > pR.BattleDurationNanos { - Logger.Info(fmt.Sprintf("The `battleMainLoop` for roomId=%v is stopped:\n%v", pR.Id, pR.AllPlayerInputsBufferString(true))) + Logger.Info(fmt.Sprintf("The `battleMainLoop` for roomId=%v is stopped:\n%v", pR.Id, pR.InputsBufferString(true))) pR.StopBattleForSettlement() } @@ -452,6 +453,22 @@ func (pR *Room) StartBattle() { } upperToSendInputFrameId := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameId)) + /* + [WARNING] + Upon resynced on frontend, "refRenderFrameId" MUST BE CAPPED somehow by "upperToSendInputFrameId", if frontend resyncs itself to a more advanced value than given below, upon the next renderFrame tick on the frontend it might generate non-consecutive "nextInputFrameId > frontend.recentInputCache.edFrameId+1". + + If "NstDelayFrames" becomes larger, "pR.RenderFrameId - refRenderFrameId" possibly becomes larger because the force confirmation is delayed more. + + Hence even upon resync, it's still possible that "refRenderFrameId < frontend.chaserRenderFrameId". + */ + refRenderFrameId := pR.ConvertToGeneratingRenderFrameId(upperToSendInputFrameId) + (1 << pR.InputScaleFrames) - 1 + // [WARNING] The following inequalities are seldom true, but just to avoid that in good network condition the frontend resyncs itself to a "too advanced frontend.renderFrameId", and then starts upsyncing "too advanced inputFrameId". + if refRenderFrameId > pR.RenderFrameId { + refRenderFrameId = pR.RenderFrameId + } + if refRenderFrameId > pR.CurDynamicsRenderFrameId { + refRenderFrameId = pR.CurDynamicsRenderFrameId + } for playerId, player := range pR.Players { if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped { // [WARNING] DON'T send anything if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player". @@ -462,24 +479,24 @@ func (pR *Room) StartBattle() { pR.sendSafely(kickoffFrame, nil, DOWNSYNC_MSG_ACT_BATTLE_START, playerId) } else { // [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player! - toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt) + toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.InputsBuffer.Cnt) candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1 - if candidateToSendInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { - // [WARNING] As "player.LastSentInputFrameId <= lastAllConfirmedInputFrameIdWithChange" for each iteration, and "lastAllConfirmedInputFrameIdWithChange <= lastAllConfirmedInputFrameId" where the latter is used to "applyInputFrameDownsyncDynamics" and then evict "pR.AllPlayerInputsBuffer", thus there's a very high possibility that "player.LastSentInputFrameId" is already evicted. - // Logger.Debug(fmt.Sprintf("LastSentInputFrameId already popped: roomId=%v, playerId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, candidateToSendInputFrameId-1, player.AckingInputFrameId, pR.AllPlayerInputsBufferString(false))) - candidateToSendInputFrameId = pR.AllPlayerInputsBuffer.StFrameId + if candidateToSendInputFrameId < pR.InputsBuffer.StFrameId { + // [WARNING] As "player.LastSentInputFrameId <= lastAllConfirmedInputFrameIdWithChange" for each iteration, and "lastAllConfirmedInputFrameIdWithChange <= lastAllConfirmedInputFrameId" where the latter is used to "applyInputFrameDownsyncDynamics" and then evict "pR.InputsBuffer", thus there's a very high possibility that "player.LastSentInputFrameId" is already evicted. + // Logger.Debug(fmt.Sprintf("LastSentInputFrameId already popped: roomId=%v, playerId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v, InputsBuffer=%v", pR.Id, playerId, candidateToSendInputFrameId-1, player.AckingInputFrameId, pR.InputsBufferString(false))) + candidateToSendInputFrameId = pR.InputsBuffer.StFrameId } // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! for candidateToSendInputFrameId <= upperToSendInputFrameId { - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId) + tmp := pR.InputsBuffer.GetByFrameId(candidateToSendInputFrameId) if nil == tmp { - panic(fmt.Sprintf("Required inputFrameId=%v for roomId=%v, playerId=%v doesn't exist! AllPlayerInputsBuffer=%v", candidateToSendInputFrameId, pR.Id, playerId, pR.AllPlayerInputsBufferString(false))) + panic(fmt.Sprintf("Required inputFrameId=%v for roomId=%v, playerId=%v doesn't exist! InputsBuffer=%v", candidateToSendInputFrameId, pR.Id, playerId, pR.InputsBufferString(false))) } f := tmp.(*pb.InputFrameDownsync) if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) { - Logger.Debug("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false)), zap.Any("ConfirmedList", f.ConfirmedList)) + Logger.Debug("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("InputsBuffer", pR.InputsBufferString(false)), zap.Any("ConfirmedList", f.ConfirmedList)) } toSendInputFrames = append(toSendInputFrames, f) candidateToSendInputFrameId++ @@ -493,25 +510,9 @@ func (pR *Room) StartBattle() { indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) if 0 < (unconfirmedMask & joinMask) { - /* - [WARNING] - "refRenderFrameId" MUST BE CAPPED somehow by "candidateToSendInputFrameId-1", if frontend resyncs itself to a more advanced value than given below, upon the next renderFrame tick on the frontend it might generate non-consecutive "nextInputFrameId > frontend.recentInputCache.edFrameId+1". - - If "NstDelayFrames" becomes larger, "pR.RenderFrameId - refRenderFrameId" possibly becomes larger because the force confirmation is delayed more. - - Hence even upon resync, it's still possible that "refRenderFrameId < frontend.chaserRenderFrameId". - */ - refRenderFrameId := pR.ConvertToGeneratingRenderFrameId(upperToSendInputFrameId) + (1 << pR.InputScaleFrames) - 1 - // [WARNING] The following inequalities are seldom true, but just to avoid that in good network condition the frontend resyncs itself to a "too advanced frontend.renderFrameId", and then starts upsyncing "too advanced inputFrameId". - if refRenderFrameId > pR.RenderFrameId { - refRenderFrameId = pR.RenderFrameId - } - if refRenderFrameId > pR.CurDynamicsRenderFrameId { - refRenderFrameId = pR.CurDynamicsRenderFrameId - } tmp := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId) if nil == tmp { - panic(fmt.Sprintf("Required refRenderFrameId=%v for roomId=%v, playerId=%v, candidateToSendInputFrameId=%v doesn't exist! AllPlayerInputsBuffer=%v, RenderFrameBuffer=%v", refRenderFrameId, pR.Id, playerId, candidateToSendInputFrameId, pR.AllPlayerInputsBufferString(false), pR.RenderFrameBufferString())) + panic(fmt.Sprintf("Required refRenderFrameId=%v for roomId=%v, playerId=%v, candidateToSendInputFrameId=%v doesn't exist! InputsBuffer=%v, RenderFrameBuffer=%v", refRenderFrameId, pR.Id, playerId, candidateToSendInputFrameId, pR.InputsBufferString(false), pR.RenderFrameBufferString())) } refRenderFrame := tmp.(*pb.RoomDownsyncFrame) pR.sendSafely(refRenderFrame, toSendInputFrames, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId) @@ -522,23 +523,17 @@ func (pR *Room) StartBattle() { } } - renderFrameBuffLowerBound := pR.ConvertToGeneratingRenderFrameId(upperToSendInputFrameId) + (1 << pR.InputScaleFrames) - 1 - if renderFrameBuffLowerBound > pR.RenderFrameId { - renderFrameBuffLowerBound = pR.RenderFrameId - } - if renderFrameBuffLowerBound > pR.CurDynamicsRenderFrameId { - renderFrameBuffLowerBound = pR.CurDynamicsRenderFrameId - } - for 0 < pR.RenderFrameBuffer.Cnt && pR.RenderFrameBuffer.StFrameId < renderFrameBuffLowerBound { + // Evict no longer required "RenderFrameBuffer" + for pR.RenderFrameBuffer.N < pR.RenderFrameBuffer.Cnt || (0 < pR.RenderFrameBuffer.Cnt && pR.RenderFrameBuffer.StFrameId < refRenderFrameId) { _ = pR.RenderFrameBuffer.Pop() } - toApplyInputFrameId := pR.ConvertToInputFrameId(pR.CurDynamicsRenderFrameId, pR.InputDelayFrames) - for 0 < pR.AllPlayerInputsBuffer.Cnt && pR.AllPlayerInputsBuffer.StFrameId < toApplyInputFrameId { - f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync) + toApplyInputFrameId := pR.ConvertToInputFrameId(refRenderFrameId, pR.InputDelayFrames) + for pR.InputsBuffer.N < pR.InputsBuffer.Cnt || (0 < pR.InputsBuffer.Cnt && pR.InputsBuffer.StFrameId < toApplyInputFrameId) { + f := pR.InputsBuffer.Pop().(*pb.InputFrameDownsync) if pR.inputFrameIdDebuggable(f.InputFrameId) { // Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked - Logger.Debug("inputFrame lifecycle#4[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString(false))) + Logger.Debug("inputFrame lifecycle#4[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("InputsBuffer", pR.InputsBufferString(false))) } } @@ -558,13 +553,16 @@ func (pR *Room) StartBattle() { }) } +func (pR *Room) toDiscreteInputsBufferIndex(inputFrameId int32, joinIndex int32) int32 { + return (inputFrameId << 2) + joinIndex // allowing joinIndex upto 15 +} + func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { if swapped := atomic.CompareAndSwapInt32(&pR.State, RoomBattleStateIns.IN_BATTLE, RoomBattleStateIns.IN_BATTLE); !swapped { return } playerId := pReq.PlayerId - indiceInJoinIndexBooleanArr := uint32(pReq.JoinIndex - 1) inputFrameUpsyncBatch := pReq.InputFrameUpsyncBatch ackingFrameId := pReq.AckingFrameId ackingInputFrameId := pReq.AckingInputFrameId @@ -584,49 +582,16 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) { for _, inputFrameUpsync := range inputFrameUpsyncBatch { clientInputFrameId := inputFrameUpsync.InputFrameId - if clientInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { - // Obsolete is actually not as concerned as advanced inputFrame. - Logger.Debug(fmt.Sprintf("Obsolete inputFrameUpsync: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) - return + if clientInputFrameId < pR.InputsBuffer.StFrameId { + // The updates to "pR.InputsBuffer.StFrameId" is monotonically increasing, thus if "clientInputFrameId < pR.InputsBuffer.StFrameId" at any moment of time, it is obsolete in the future. + Logger.Debug(fmt.Sprintf("Omitting obsolete inputFrameUpsync: roomId=%v, playerId=%v, clientInputFrameId=%v, InputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.InputsBufferString(false))) + continue } - var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) - encodedInput := pR.EncodeUpsyncCmd(inputFrameUpsync) + bufIndex := pR.toDiscreteInputsBufferIndex(clientInputFrameId, pReq.JoinIndex) + pR.DiscreteInputsBuffer.Store(bufIndex, inputFrameUpsync) - if clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId { - Logger.Warn(fmt.Sprintf("inputFrame too advanced! is the player cheating? roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) - return - } - tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId) - if nil == tmp2 { - // This shouldn't happen due to the previous 2 checks - Logger.Warn(fmt.Sprintf("Mysterious error getting an input frame: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) - return - } - inputFrameDownsync := tmp2.(*pb.InputFrameDownsync) - oldConfirmedList := atomic.LoadUint64(&(inputFrameDownsync.ConfirmedList)) - if (oldConfirmedList & joinMask) > 0 { - Logger.Debug(fmt.Sprintf("Cmd already confirmed but getting set attempt, omitting this upsync cmd: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString(false))) - return - } - - // In Golang 1.12, there's no "compare-and-swap primitive" on a custom struct (or it's pointer, unless it's an unsafe pointer https://pkg.go.dev/sync/atomic@go1.12#CompareAndSwapPointer). Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible. - - // [WARNING] No need to use CAS for updating "inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr]", the upsync from frontend takes top priority. - atomic.StoreUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], encodedInput) - - newConfirmedList := (oldConfirmedList | joinMask) - if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped { - // [WARNING] Upon this error, the actual input has already been updated, which is an expected result if it caused by the force confirmation from "battleMainLoop". - Logger.Warn(fmt.Sprintf("Failed confirm CAS, might've been forced to all-confirmed: roomId=%v, playerId=%v, clientInputFrameId=%v", pR.Id, playerId, clientInputFrameId)) - return - } - - totPlayerCnt := uint32(pR.Capacity) - allConfirmedMask := uint64((1 << totPlayerCnt) - 1) - if allConfirmedMask == newConfirmedList { - pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, playerId) - } + // TODO: "pR.DiscreteInputsBuffer" might become too large with outdated "inputFrameUpsync" items, maintain another queue orderd by timestamp to evict them } } @@ -634,9 +599,9 @@ func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFra inputFrameId := inputFrameDownsync.InputFrameId if -1 == pR.LastAllConfirmedInputFrameIdWithChange || false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { if -1 == playerId { - Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, newInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString(false))) + Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, newInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, InputsBuffer=%v", pR.Id, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.InputsBufferString(false))) } else { - Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, newInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString(false))) + Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, newInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, InputsBuffer=%v", pR.Id, playerId, inputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.InputsBufferString(false))) } atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), inputFrameId) } @@ -646,9 +611,9 @@ func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFra pR.LastAllConfirmedInputList[i] = v } if -1 == playerId { - Logger.Debug(fmt.Sprintf("inputFrame lifecycle#2[forced-allconfirmed]: roomId=%v, AllPlayerInputsBuffer=%v", pR.Id, pR.AllPlayerInputsBufferString(false))) + Logger.Debug(fmt.Sprintf("inputFrame lifecycle#2[forced-allconfirmed]: roomId=%v, InputsBuffer=%v", pR.Id, pR.InputsBufferString(false))) } else { - Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[allconfirmed]: roomId=%v, playerId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, pR.AllPlayerInputsBufferString(false))) + Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[allconfirmed]: roomId=%v, playerId=%v, InputsBuffer=%v", pR.Id, playerId, pR.InputsBufferString(false))) } } @@ -786,7 +751,8 @@ func (pR *Room) OnDismissed() { pR.PlayerSignalToCloseDict = make(map[int32]SignalToCloseConnCbType) pR.JoinIndexBooleanArr = make([]bool, pR.Capacity) pR.Barriers = make(map[int32]*Barrier) - pR.AllPlayerInputsBuffer = NewRingBuffer(1024) + pR.InputsBuffer = NewRingBuffer(1024) + pR.DiscreteInputsBuffer = sync.Map{} pR.RenderFrameBuffer = NewRingBuffer(1024) pR.LastAllConfirmedInputFrameId = -1 @@ -1045,16 +1011,16 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDowns */ var currInputFrameDownsync *pb.InputFrameDownsync = nil - if 0 == inputFrameId && 0 == pR.AllPlayerInputsBuffer.Cnt { + if 0 == inputFrameId && 0 == pR.InputsBuffer.Cnt { currInputFrameDownsync = &pb.InputFrameDownsync{ InputFrameId: 0, InputList: make([]uint64, pR.Capacity), ConfirmedList: uint64(0), } } else { - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId - 1) + tmp := pR.InputsBuffer.GetByFrameId(inputFrameId - 1) if nil == tmp { - panic(fmt.Sprintf("Error prefabbing inputFrameDownsync: roomId=%v, AllPlayerInputsBuffer=%v", pR.Id, pR.AllPlayerInputsBufferString(false))) + panic(fmt.Sprintf("Error prefabbing inputFrameDownsync: roomId=%v, InputsBuffer=%v", pR.Id, pR.InputsBufferString(false))) } prevInputFrameDownsync := tmp.(*pb.InputFrameDownsync) currInputList := prevInputFrameDownsync.InputList // Would be a clone of the values @@ -1065,7 +1031,26 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDowns } } - pR.AllPlayerInputsBuffer.Put(currInputFrameDownsync) + for _, player := range pR.Players { + // Enrich by already arrived player upsync commands + bufIndex := pR.toDiscreteInputsBufferIndex(currInputFrameDownsync.InputFrameId, player.JoinIndex) + tmp, loaded := pR.DiscreteInputsBuffer.LoadAndDelete(bufIndex) + if !loaded { + continue + } + inputFrameUpsync := tmp.(*pb.InputFrameUpsync) + indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) + currInputFrameDownsync.InputList[indiceInJoinIndexBooleanArr] = pR.EncodeUpsyncCmd(inputFrameUpsync) + currInputFrameDownsync.ConfirmedList |= (1 << indiceInJoinIndexBooleanArr) + } + + totPlayerCnt := uint32(pR.Capacity) + allConfirmedMask := uint64((1 << totPlayerCnt) - 1) + if currInputFrameDownsync.ConfirmedList == allConfirmedMask { + pR.onInputFrameDownsyncAllConfirmed(currInputFrameDownsync, -1) + } + + pR.InputsBuffer.Put(currInputFrameDownsync) return currInputFrameDownsync } @@ -1087,9 +1072,9 @@ func (pR *Room) forceConfirmationIfApplicable() uint64 { Logger.Debug(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v[type#1], no need to force confirmation of it", inputFrameId2, pR.Id)) return 0 } - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId2) + tmp := pR.InputsBuffer.GetByFrameId(inputFrameId2) if nil == tmp { - panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! AllPlayerInputsBuffer=%v", inputFrameId2, pR.Id, pR.AllPlayerInputsBufferString(false))) + panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! InputsBuffer=%v", inputFrameId2, pR.Id, pR.InputsBufferString(false))) } inputFrame2 := tmp.(*pb.InputFrameDownsync) @@ -1123,11 +1108,11 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende delayedInputFrameId := pR.ConvertToInputFrameId(collisionSysRenderFrameId, pR.InputDelayFrames) if 0 <= delayedInputFrameId { if delayedInputFrameId > pR.LastAllConfirmedInputFrameId { - panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString(false))) + panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! InputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.InputsBufferString(false))) } - tmp := pR.AllPlayerInputsBuffer.GetByFrameId(delayedInputFrameId) + tmp := pR.InputsBuffer.GetByFrameId(delayedInputFrameId) if nil == tmp { - panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString(false))) + panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! InputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.InputsBufferString(false))) } delayedInputFrame := tmp.(*pb.InputFrameDownsync) // [WARNING] It's possible that by now "allConfirmedMask != delayedInputFrame.ConfirmedList && delayedInputFrameId <= pR.LastAllConfirmedInputFrameId", we trust "pR.LastAllConfirmedInputFrameId" as the TOP AUTHORITY. @@ -1143,20 +1128,21 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor dx := baseChange * float64(decodedInput[0]) dy := baseChange * float64(decodedInput[1]) - /* - // The collision lib seems very slow at worst cases, omitting for now - collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex - playerCollider := pR.CollisionSysMap[collisionPlayerIndex] - if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { - changeWithCollision := collision.ContactWithObject(collision.Objects[0]) - dx = changeWithCollision.X() - dy = changeWithCollision.Y() - } - playerCollider.X += dx - playerCollider.Y += dy - // Update in "collision space" - playerCollider.Update() - */ + + /* + // The collision lib seems very slow at worst cases, omitting for now + collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex + playerCollider := pR.CollisionSysMap[collisionPlayerIndex] + if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { + changeWithCollision := collision.ContactWithObject(collision.Objects[0]) + dx = changeWithCollision.X() + dy = changeWithCollision.Y() + } + playerCollider.X += dx + playerCollider.Y += dy + // Update in "collision space" + playerCollider.Update() + */ player.Dir.Dx = decodedInput[0] player.Dir.Dy = decodedInput[1] diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index 15d9cdf..c4b57d0 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 216.6425019058577, + 210.74654679407692, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 84ba905..e831bbc 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -547,20 +547,23 @@ cc.Class({ this._inputControlEnabled = false; }, - onBattleStartedOrResynced(rdf) { + onRoomDownsyncFrame(rdf) { // This function is also applicable to "re-joining". const self = window.mapIns; + if (rdf.id < self.lastAllConfirmedRenderFrameId) { + return window.RING_BUFF_FAILED_TO_SET; + } const dumpRenderCacheRet = self.dumpToRenderCache(rdf); if (window.RING_BUFF_FAILED_TO_SET == dumpRenderCacheRet) { console.error("Something is wrong while setting the RingBuffer by frameId!"); return dumpRenderCacheRet; } if (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START < rdf.id && window.RING_BUFF_CONSECUTIVE_SET == dumpRenderCacheRet) { - if (rdf.id < self.chaserRenderFrameId) { - // This "rdf.id = backend.refRenderFrameId" could be small, see comments around "room.go". - self.chaserRenderFrameId = rdf.id; - } - // In this case, we'll also got proper all-confirmed inputFrames for advancing the renderFrames in the coming "update(dt)" + /* + Don't change + - lastAllConfirmedRenderFrameId, it's updated only in "rollbackAndChase > _createRoomDownsyncFrameLocally" (except for when RING_BUFF_NON_CONSECUTIVE_SET) + - chaserRenderFrameId, it's updated only in "onInputFrameDownsyncBatch" (except for when RING_BUFF_NON_CONSECUTIVE_SET) + */ return dumpRenderCacheRet; } @@ -569,6 +572,7 @@ cc.Class({ self.renderFrameId = rdf.id; self.lastRenderFrameIdTriggeredAt = performance.now(); + // In this case it must be true that "rdf.id > chaserRenderFrameId >= lastAllConfirmedRenderFrameId". self.lastAllConfirmedRenderFrameId = rdf.id; self.chaserRenderFrameId = rdf.id; @@ -623,6 +627,9 @@ cc.Class({ for (let k in batch) { const inputFrameDownsync = batch[k]; const inputFrameDownsyncId = inputFrameDownsync.inputFrameId; + if (inputFrameDownsyncId < self.lastAllConfirmedInputFrameId) { + continue; + } if (window.RING_BUFF_NON_CONSECUTIVE_SET == dumpRenderCacheRet) { // Deliberately left blank, in this case "chaserRenderFrameId" is already reset to proper value. } else { @@ -767,8 +774,9 @@ cc.Class({ // Use "fractional-frame-chasing" to guarantee that "self.update(dt)" is not jammed by a "large range of frame-chasing". See `/ConcerningEdgeCases.md` for the motivation. const prevChaserRenderFrameId = self.chaserRenderFrameId; let nextChaserRenderFrameId = (prevChaserRenderFrameId + self.maxChasingRenderFramesPerUpdate); - if (nextChaserRenderFrameId > self.renderFrameId) + if (nextChaserRenderFrameId > self.renderFrameId) { nextChaserRenderFrameId = self.renderFrameId; + } self.rollbackAndChase(prevChaserRenderFrameId, nextChaserRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap); self.chaserRenderFrameId = nextChaserRenderFrameId; // Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic! let t2 = performance.now(); @@ -975,29 +983,27 @@ cc.Class({ }, rollbackAndChase(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap) { - if (renderFrameIdSt >= renderFrameIdEd) { - return; - } - const self = this; - const renderFrameSt = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame" - if (null == renderFrameSt) { + let latestRdf = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame" + if (null == latestRdf) { console.error("Couldn't find renderFrameId=", renderFrameIdSt, " to rollback, lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false)); } + + if (renderFrameIdSt >= renderFrameIdEd) { + return latestRdf; + } /* - Reset "position" of players in "collisionSys" according to "renderFrameSt". The easy part is that we don't have path-dependent-integrals to worry about like that of thermal dynamics. + Reset "position" of players in "collisionSys" according to "renderFrameIdSt". The easy part is that we don't have path-dependent-integrals to worry about like that of thermal dynamics. */ self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { const joinIndex = playerRichInfo.joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const playerCollider = collisionSysMap.get(collisionPlayerIndex); - const player = renderFrameSt.players[playerId]; + const player = latestRdf.players[playerId]; playerCollider.x = player.x; playerCollider.y = player.y; }); - // [WARNING] Traverse in the order of joinIndices to guarantee determinism. - /* This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd". */ @@ -1009,6 +1015,7 @@ cc.Class({ console.error("Failed to get cached inputFrameDownsync for renderFrameId=", i, ", inputFrameId=", j, "lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false)); } const inputList = inputFrameDownsync.inputList; + // [WARNING] Traverse in the order of joinIndices to guarantee determinism. for (let j in self.playerRichInfoArr) { const joinIndex = parseInt(j) + 1; const playerId = self.playerRichInfoArr[j].id; @@ -1038,9 +1045,11 @@ cc.Class({ playerCollider.y -= result.overlap * result.overlap_y; } } + + latestRdf = self._createRoomDownsyncFrameLocally(i+1, collisionSys, collisionSysMap); } - return self._createRoomDownsyncFrameLocally(renderFrameIdEd, collisionSys, collisionSysMap); + return latestRdf; }, _initPlayerRichInfoDict(players, playerMetas) { diff --git a/frontend/assets/scripts/RingBuffer.js b/frontend/assets/scripts/RingBuffer.js index 57310fe..52c1ea0 100644 --- a/frontend/assets/scripts/RingBuffer.js +++ b/frontend/assets/scripts/RingBuffer.js @@ -68,7 +68,7 @@ RingBuffer.prototype.getByFrameId = function(frameId) { // [WARNING] During a battle, frontend could receive non-consecutive frames (either renderFrame or inputFrame) due to resync, the buffer should handle these frames properly. RingBuffer.prototype.setByFrameId = function(item, frameId) { if (frameId < this.stFrameId) { - console.error("Invalid putByFrameId#1: stFrameId=", stFrameId, ", edFrameId=", edFrameId, ", incoming item=", item); + console.error("Invalid putByFrameId#1: stFrameId=", this.stFrameId, ", edFrameId=", this.edFrameId, ", incoming item=", item); return window.RING_BUFF_FAILED_TO_SET; } const arrIdx = this.getArrIdxByOffset(frameId - this.stFrameId); diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index bf4efb8..6c74ec8 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -171,7 +171,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { mapIns.onBattleReadyToStart(resp.rdf.playerMetas, false); break; case window.DOWNSYNC_MSG_ACT_BATTLE_START: - mapIns.onBattleStartedOrResynced(resp.rdf); + mapIns.onRoomDownsyncFrame(resp.rdf); break; case window.DOWNSYNC_MSG_ACT_BATTLE_STOPPED: mapIns.onBattleStopped(); @@ -180,9 +180,9 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); break; case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC: - console.warn("Got forced resync:", JSON.stringify(resp), " @localRenderFrameId=", mapIns.renderFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false)); + console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp)); // The following order of execution is important, because "onInputFrameDownsyncBatch" is only available when state is IN_BATTLE - const dumpRenderCacheRet = mapIns.onBattleStartedOrResynced(resp.rdf); + const dumpRenderCacheRet = mapIns.onRoomDownsyncFrame(resp.rdf); mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch, dumpRenderCacheRet); break; default: From 1004fd45dbf06f14118c8bee1d0b34ab6c5e4d32 Mon Sep 17 00:00:00 2001 From: genxium Date: Mon, 3 Oct 2022 23:54:38 +0800 Subject: [PATCH 12/13] Fixed timing for upsync confirmation merge. --- battle_srv/models/room.go | 44 +++++++++++-------------------- frontend/assets/scenes/login.fire | 2 +- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 0677452..9ade191 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -1029,26 +1029,7 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDowns InputList: currInputList, ConfirmedList: uint64(0), } - } - - for _, player := range pR.Players { - // Enrich by already arrived player upsync commands - bufIndex := pR.toDiscreteInputsBufferIndex(currInputFrameDownsync.InputFrameId, player.JoinIndex) - tmp, loaded := pR.DiscreteInputsBuffer.LoadAndDelete(bufIndex) - if !loaded { - continue - } - inputFrameUpsync := tmp.(*pb.InputFrameUpsync) - indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) - currInputFrameDownsync.InputList[indiceInJoinIndexBooleanArr] = pR.EncodeUpsyncCmd(inputFrameUpsync) - currInputFrameDownsync.ConfirmedList |= (1 << indiceInJoinIndexBooleanArr) - } - - totPlayerCnt := uint32(pR.Capacity) - allConfirmedMask := uint64((1 << totPlayerCnt) - 1) - if currInputFrameDownsync.ConfirmedList == allConfirmedMask { - pR.onInputFrameDownsyncAllConfirmed(currInputFrameDownsync, -1) - } + } pR.InputsBuffer.Put(currInputFrameDownsync) return currInputFrameDownsync @@ -1077,21 +1058,28 @@ func (pR *Room) forceConfirmationIfApplicable() uint64 { panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! InputsBuffer=%v", inputFrameId2, pR.Id, pR.InputsBufferString(false))) } inputFrame2 := tmp.(*pb.InputFrameDownsync) + for _, player := range pR.Players { + // Enrich by already arrived player upsync commands + bufIndex := pR.toDiscreteInputsBufferIndex(inputFrame2.InputFrameId, player.JoinIndex) + tmp, loaded := pR.DiscreteInputsBuffer.LoadAndDelete(bufIndex) + if !loaded { + continue + } + inputFrameUpsync := tmp.(*pb.InputFrameUpsync) + indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) + inputFrame2.InputList[indiceInJoinIndexBooleanArr] = pR.EncodeUpsyncCmd(inputFrameUpsync) + inputFrame2.ConfirmedList |= (1 << indiceInJoinIndexBooleanArr) + } totPlayerCnt := uint32(pR.Capacity) allConfirmedMask := uint64((1 << totPlayerCnt) - 1) - if swapped := atomic.CompareAndSwapUint64(&(inputFrame2.ConfirmedList), allConfirmedMask, allConfirmedMask); swapped { - // This could happen if the frontend upsync command arrived between type#1 and type#2 checks. - Logger.Debug(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v[type#2], no need to force confirmation of it", inputFrameId2, pR.Id)) - return 0 - } // Force confirmation of "inputFrame2" - oldConfirmedList := atomic.LoadUint64(&(inputFrame2.ConfirmedList)) - atomic.StoreUint64(&(inputFrame2.ConfirmedList), allConfirmedMask) + oldConfirmedList := inputFrame2.ConfirmedList + unconfirmedMask := (oldConfirmedList ^ allConfirmedMask) + inputFrame2.ConfirmedList = allConfirmedMask pR.onInputFrameDownsyncAllConfirmed(inputFrame2, -1) - unconfirmedMask := (oldConfirmedList ^ allConfirmedMask) return unconfirmedMask } diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index c4b57d0..6324f99 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 210.74654679407692, + 209.57814771583418, 0, 0, 0, From a6731dc7d654442f938ab298ebd60789a700e36a Mon Sep 17 00:00:00 2001 From: genxium Date: Tue, 4 Oct 2022 11:24:47 +0800 Subject: [PATCH 13/13] Minor fixes for rejoining signals. --- battle_srv/models/room.go | 85 +++--- .../pb_output/room_downsync_frame.pb.go | 257 +++++++++--------- battle_srv/ws/serve.go | 3 +- .../pbfiles/room_downsync_frame.proto | 1 + frontend/assets/scenes/login.fire | 2 +- frontend/assets/scripts/Map.js | 66 +++-- frontend/assets/scripts/WsSessionMgr.js | 23 +- ...om_downsync_frame_proto_bundle.forcemsg.js | 23 ++ 8 files changed, 269 insertions(+), 191 deletions(-) diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 9ade191..3778ada 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -54,6 +54,11 @@ const ( COLLISION_BARRIER_INDEX_PREFIX = (1 << 16) ) +const ( + MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED = -1 + MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED = -2 +) + var DIRECTION_DECODER = [][]int32{ {0, 0}, {0, +1}, @@ -202,9 +207,9 @@ func (pR *Room) AddPlayerIfPossible(pPlayerFromDbInit *Player, session *websocke } defer pR.onPlayerAdded(playerId) - pPlayerFromDbInit.AckingFrameId = 0 + pPlayerFromDbInit.AckingFrameId = -1 pPlayerFromDbInit.AckingInputFrameId = -1 - pPlayerFromDbInit.LastSentInputFrameId = -1 + pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED pPlayerFromDbInit.BattleState = PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK pPlayerFromDbInit.FrozenAtGmtMillis = -1 // Hardcoded temporarily. pPlayerFromDbInit.Speed = PLAYER_DEFAULT_SPEED // Hardcoded temporarily. @@ -236,12 +241,12 @@ func (pR *Room) ReAddPlayerIfPossible(pTmpPlayerInstance *Player, session *webso pR.PlayerDownsyncSessionDict[playerId] = session pR.PlayerSignalToCloseDict[playerId] = signalToCloseConnOfThisPlayer pEffectiveInRoomPlayerInstance := pR.Players[playerId] - pEffectiveInRoomPlayerInstance.AckingFrameId = 0 + pEffectiveInRoomPlayerInstance.AckingFrameId = -1 pEffectiveInRoomPlayerInstance.AckingInputFrameId = -1 - pEffectiveInRoomPlayerInstance.LastSentInputFrameId = -1 + pEffectiveInRoomPlayerInstance.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED pEffectiveInRoomPlayerInstance.BattleState = PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK - Logger.Warn("ReAddPlayerIfPossible finished.", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("roomState", pR.State), zap.Any("roomEffectivePlayerCount", pR.EffectivePlayerCount), zap.Any("player AckingFrameId", pEffectiveInRoomPlayerInstance.AckingFrameId), zap.Any("player AckingInputFrameId", pEffectiveInRoomPlayerInstance.AckingInputFrameId)) + Logger.Warn("ReAddPlayerIfPossible finished.", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("joinIndex", pEffectiveInRoomPlayerInstance.JoinIndex), zap.Any("playerBattleState", pEffectiveInRoomPlayerInstance.BattleState), zap.Any("roomState", pR.State), zap.Any("roomEffectivePlayerCount", pR.EffectivePlayerCount), zap.Any("AckingFrameId", pEffectiveInRoomPlayerInstance.AckingFrameId), zap.Any("AckingInputFrameId", pEffectiveInRoomPlayerInstance.AckingInputFrameId), zap.Any("LastSentInputFrameId", pEffectiveInRoomPlayerInstance.LastSentInputFrameId)) return true } @@ -383,7 +388,7 @@ func (pR *Room) InputsBufferString(allDetails bool) string { func (pR *Room) StartBattle() { if RoomBattleStateIns.WAITING != pR.State { - Logger.Warn("[StartBattle] Battle not started after all players' battle state checked!", zap.Any("roomId", pR.Id), zap.Any("roomState", pR.State)) + Logger.Warn("[StartBattle] Battle not started due to not being WAITING!", zap.Any("roomId", pR.Id), zap.Any("roomState", pR.State)) return } @@ -480,15 +485,20 @@ func (pR *Room) StartBattle() { } else { // [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player! toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.InputsBuffer.Cnt) - candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1 + candidateToSendInputFrameId := pR.Players[playerId].LastSentInputFrameId + 1 if candidateToSendInputFrameId < pR.InputsBuffer.StFrameId { // [WARNING] As "player.LastSentInputFrameId <= lastAllConfirmedInputFrameIdWithChange" for each iteration, and "lastAllConfirmedInputFrameIdWithChange <= lastAllConfirmedInputFrameId" where the latter is used to "applyInputFrameDownsyncDynamics" and then evict "pR.InputsBuffer", thus there's a very high possibility that "player.LastSentInputFrameId" is already evicted. // Logger.Debug(fmt.Sprintf("LastSentInputFrameId already popped: roomId=%v, playerId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v, InputsBuffer=%v", pR.Id, playerId, candidateToSendInputFrameId-1, player.AckingInputFrameId, pR.InputsBufferString(false))) candidateToSendInputFrameId = pR.InputsBuffer.StFrameId } - // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! + if MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId { + // A rejoined player, should guarantee that when it resyncs to "refRenderFrameId" a matching inputFrame to apply exists + candidateToSendInputFrameId = pR.ConvertToInputFrameId(refRenderFrameId, pR.InputDelayFrames) + Logger.Warn(fmt.Sprintf("Resetting refRenderFrame for rejoined player: roomId=%v, playerId=%v, refRenderFrameId=%v, candidateToSendInputFrameId=%v, upperToSendInputFrameId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v", pR.Id, playerId, refRenderFrameId, candidateToSendInputFrameId, upperToSendInputFrameId, player.LastSentInputFrameId, player.AckingInputFrameId)) + } + // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! for candidateToSendInputFrameId <= upperToSendInputFrameId { tmp := pR.InputsBuffer.GetByFrameId(candidateToSendInputFrameId) if nil == tmp { @@ -504,12 +514,17 @@ func (pR *Room) StartBattle() { if 0 >= len(toSendInputFrames) { // [WARNING] When sending DOWNSYNC_MSG_ACT_FORCED_RESYNC, there MUST BE accompanying "toSendInputFrames" for calculating "refRenderFrameId"! + + if MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId { + Logger.Warn(fmt.Sprintf("Not sending due to empty toSendInputFrames: roomId=%v, playerId=%v, refRenderFrameId=%v, candidateToSendInputFrameId=%v, upperToSendInputFrameId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v", pR.Id, playerId, refRenderFrameId, candidateToSendInputFrameId, upperToSendInputFrameId, player.LastSentInputFrameId, player.AckingInputFrameId)) + } continue } indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1) var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr) - if 0 < (unconfirmedMask & joinMask) { + if MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId || 0 < (unconfirmedMask&joinMask) { + // [WARNING] Even upon "MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED", it could be true that "0 == (unconfirmedMask & joinMask)"! tmp := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId) if nil == tmp { panic(fmt.Sprintf("Required refRenderFrameId=%v for roomId=%v, playerId=%v, candidateToSendInputFrameId=%v doesn't exist! InputsBuffer=%v, RenderFrameBuffer=%v", refRenderFrameId, pR.Id, playerId, candidateToSendInputFrameId, pR.InputsBufferString(false), pR.RenderFrameBufferString())) @@ -519,7 +534,7 @@ func (pR *Room) StartBattle() { } else { pR.sendSafely(nil, toSendInputFrames, DOWNSYNC_MSG_ACT_INPUT_BATCH, playerId) } - atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1) + pR.Players[playerId].LastSentInputFrameId = candidateToSendInputFrameId - 1 } } @@ -762,7 +777,7 @@ func (pR *Room) OnDismissed() { pR.RenderFrameId = 0 pR.CurDynamicsRenderFrameId = 0 pR.InputDelayFrames = 8 - pR.NstDelayFrames = 32 + pR.NstDelayFrames = 8 pR.InputScaleFrames = uint32(2) pR.ServerFps = 60 pR.RollbackEstimatedDt = float64(1.0) / float64(pR.ServerFps) @@ -911,8 +926,8 @@ func (pR *Room) onPlayerReAdded(playerId int32) { } func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { - targetPlayer, ok := pR.Players[playerId] - if false == ok { + targetPlayer, existing := pR.Players[playerId] + if false == existing { return false } @@ -930,10 +945,12 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { // Broadcast added or readded player info to all players in the same room for _, eachPlayer := range pR.Players { /* - [WARNING] - This `playerAckedFrame` is the first ever "RoomDownsyncFrame" for every "PersistentSessionClient on the frontend", and it goes right after each "BattleColliderInfo". + [WARNING] + This `playerAckedFrame` is the first ever "RoomDownsyncFrame" for every "PersistentSessionClient on the frontend", and it goes right after each "BattleColliderInfo". - By making use of the sequential nature of each ws session, all later "RoomDownsyncFrame"s generated after `pRoom.StartBattle()` will be put behind this `playerAckedFrame`. + By making use of the sequential nature of each ws session, all later "RoomDownsyncFrame"s generated after `pRoom.StartBattle()` will be put behind this `playerAckedFrame`. + + This function is triggered by an upsync message via WebSocket, thus downsync sending is also available by now. */ switch targetPlayer.BattleState { case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK: @@ -955,7 +972,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool { } targetPlayer.BattleState = PlayerBattleStateIns.ACTIVE - Logger.Info(fmt.Sprintf("OnPlayerBattleColliderAcked: roomId=%v, roomState=%v, targetPlayerId=%v, capacity=%v, EffectivePlayerCount=%v", pR.Id, pR.State, targetPlayer.Id, pR.Capacity, pR.EffectivePlayerCount)) + Logger.Info(fmt.Sprintf("OnPlayerBattleColliderAcked: roomId=%v, roomState=%v, targetPlayerId=%v, targetPlayerBattleState=%v, capacity=%v, EffectivePlayerCount=%v", pR.Id, pR.State, targetPlayer.Id, targetPlayer.BattleState, pR.Capacity, pR.EffectivePlayerCount)) if pR.Capacity == int(pR.EffectivePlayerCount) { allAcked := true @@ -1029,7 +1046,7 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDowns InputList: currInputList, ConfirmedList: uint64(0), } - } + } pR.InputsBuffer.Put(currInputFrameDownsync) return currInputFrameDownsync @@ -1058,7 +1075,7 @@ func (pR *Room) forceConfirmationIfApplicable() uint64 { panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! InputsBuffer=%v", inputFrameId2, pR.Id, pR.InputsBufferString(false))) } inputFrame2 := tmp.(*pb.InputFrameDownsync) - for _, player := range pR.Players { + for _, player := range pR.Players { // Enrich by already arrived player upsync commands bufIndex := pR.toDiscreteInputsBufferIndex(inputFrame2.InputFrameId, player.JoinIndex) tmp, loaded := pR.DiscreteInputsBuffer.LoadAndDelete(bufIndex) @@ -1117,20 +1134,20 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende dx := baseChange * float64(decodedInput[0]) dy := baseChange * float64(decodedInput[1]) - /* - // The collision lib seems very slow at worst cases, omitting for now - collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex - playerCollider := pR.CollisionSysMap[collisionPlayerIndex] - if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { - changeWithCollision := collision.ContactWithObject(collision.Objects[0]) - dx = changeWithCollision.X() - dy = changeWithCollision.Y() - } - playerCollider.X += dx - playerCollider.Y += dy - // Update in "collision space" - playerCollider.Update() - */ + /* + // The collision lib seems very slow at worst cases, omitting for now + collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex + playerCollider := pR.CollisionSysMap[collisionPlayerIndex] + if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { + changeWithCollision := collision.ContactWithObject(collision.Objects[0]) + dx = changeWithCollision.X() + dy = changeWithCollision.Y() + } + playerCollider.X += dx + playerCollider.Y += dy + // Update in "collision space" + playerCollider.Update() + */ player.Dir.Dx = decodedInput[0] player.Dir.Dy = decodedInput[1] @@ -1142,7 +1159,7 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende newRenderFrame := pb.RoomDownsyncFrame{ Id: collisionSysRenderFrameId + 1, Players: toPbPlayers(pR.Players), - CountdownNanos: (pR.BattleDurationNanos - int64(collisionSysRenderFrameId)*int64(pR.RollbackEstimatedDt*1000000000)), + CountdownNanos: (pR.BattleDurationNanos - int64(collisionSysRenderFrameId)*int64(pR.RollbackEstimatedDt*1000000000)), // TODO: Make this number more synced with frontend! } pR.RenderFrameBuffer.Put(&newRenderFrame) pR.CurDynamicsRenderFrameId++ diff --git a/battle_srv/pb_output/room_downsync_frame.pb.go b/battle_srv/pb_output/room_downsync_frame.pb.go index 794e483..4eb7349 100644 --- a/battle_srv/pb_output/room_downsync_frame.pb.go +++ b/battle_srv/pb_output/room_downsync_frame.pb.go @@ -301,6 +301,7 @@ type BattleColliderInfo struct { NstDelayFrames int32 `protobuf:"varint,15,opt,name=nstDelayFrames,proto3" json:"nstDelayFrames,omitempty"` InputFrameUpsyncDelayTolerance int32 `protobuf:"varint,16,opt,name=inputFrameUpsyncDelayTolerance,proto3" json:"inputFrameUpsyncDelayTolerance,omitempty"` MaxChasingRenderFramesPerUpdate int32 `protobuf:"varint,17,opt,name=maxChasingRenderFramesPerUpdate,proto3" json:"maxChasingRenderFramesPerUpdate,omitempty"` + PlayerBattleState int32 `protobuf:"varint,18,opt,name=playerBattleState,proto3" json:"playerBattleState,omitempty"` } func (x *BattleColliderInfo) Reset() { @@ -454,6 +455,13 @@ func (x *BattleColliderInfo) GetMaxChasingRenderFramesPerUpdate() int32 { return 0 } +func (x *BattleColliderInfo) GetPlayerBattleState() int32 { + if x != nil { + return x.PlayerBattleState + } + return 0 +} + type Player struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1105,7 +1113,7 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x52, 0x0d, 0x70, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, - 0x74, 0x22, 0xd0, 0x08, 0x0a, 0x12, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, + 0x74, 0x22, 0xfe, 0x08, 0x0a, 0x12, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x68, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x54, 0x6f, 0x56, @@ -1161,129 +1169,132 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1f, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, - 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x60, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x54, - 0x6f, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x68, 0x0a, 0x1a, 0x53, 0x74, - 0x72, 0x54, 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, - 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, - 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6f, 0x6c, 0x79, - 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x96, 0x02, 0x0a, 0x06, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, + 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x42, 0x61, 0x74, 0x74, 0x6c, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x60, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x54, 0x6f, 0x56, + 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x78, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x68, 0x0a, 0x1a, 0x53, 0x74, 0x72, 0x54, + 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, + 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, + 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, + 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x96, 0x02, 0x0a, 0x06, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x0c, 0x0a, + 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79, 0x12, 0x2c, 0x0a, 0x03, 0x64, 0x69, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, + 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x12, 0x20, 0x0a, + 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x2c, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, 0x4d, 0x69, + 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, + 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x63, + 0x6f, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x88, 0x01, 0x0a, 0x0a, + 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69, + 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, + 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x22, 0x7c, + 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, + 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, + 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f, + 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, + 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9f, 0x03, 0x0a, 0x11, 0x52, 0x6f, + 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, - 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79, 0x12, 0x2c, 0x0a, 0x03, 0x64, - 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, - 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x12, - 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, - 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6c, 0x61, - 0x73, 0x74, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, - 0x73, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x12, - 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x88, 0x01, - 0x0a, 0x0a, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, - 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, - 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, - 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, - 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, - 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, - 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, - 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, - 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x9f, 0x03, 0x0a, 0x11, - 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, - 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0e, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, - 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, - 0x74, 0x61, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, - 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, - 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, - 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, - 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, - 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xca, 0x02, - 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6a, - 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, - 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x6b, - 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, - 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x63, 0x6b, - 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, - 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, - 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, - 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, - 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, - 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, - 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, - 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x06, 0x57, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, - 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, 0x63, - 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x03, 0x72, - 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, - 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x72, 0x64, - 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, - 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, - 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, - 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, + 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, + 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, + 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, + 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xca, 0x02, 0x0a, 0x05, + 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, + 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, + 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, + 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, + 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x2e, 0x0a, + 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, + 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x57, 0x0a, + 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, + 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, + 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, + 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, + 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x06, 0x57, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, + 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, 0x63, 0x68, 0x6f, + 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x03, 0x72, 0x64, 0x66, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, + 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, + 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x72, 0x64, 0x66, 0x12, + 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, + 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, + 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3f, + 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x42, + 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/battle_srv/ws/serve.go b/battle_srv/ws/serve.go index a828025..a6e398c 100644 --- a/battle_srv/ws/serve.go +++ b/battle_srv/ws/serve.go @@ -260,6 +260,7 @@ func Serve(c *gin.Context) { NstDelayFrames: pRoom.NstDelayFrames, InputFrameUpsyncDelayTolerance: pRoom.InputFrameUpsyncDelayTolerance, MaxChasingRenderFramesPerUpdate: pRoom.MaxChasingRenderFramesPerUpdate, + PlayerBattleState: pThePlayer.BattleState, // For frontend to know whether it's rejoining } resp := &pb.WsResp{ @@ -269,7 +270,7 @@ func Serve(c *gin.Context) { BciFrame: bciFrame, } - // Logger.Info("Sending downsync HeartbeatRequirements:", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("resp", resp)) + Logger.Debug("Sending downsync HeartbeatRequirements:", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("resp", resp)) theBytes, marshalErr := proto.Marshal(resp) if nil != marshalErr { diff --git a/frontend/assets/resources/pbfiles/room_downsync_frame.proto b/frontend/assets/resources/pbfiles/room_downsync_frame.proto index d50ebe4..c2b41d8 100644 --- a/frontend/assets/resources/pbfiles/room_downsync_frame.proto +++ b/frontend/assets/resources/pbfiles/room_downsync_frame.proto @@ -45,6 +45,7 @@ message BattleColliderInfo { int32 nstDelayFrames = 15; int32 inputFrameUpsyncDelayTolerance = 16; int32 maxChasingRenderFramesPerUpdate = 17; + int32 playerBattleState = 18; } message Player { diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index 6324f99..76a6bfa 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 209.57814771583418, + 216.50635094610968, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index e831bbc..34aea87 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -22,6 +22,16 @@ window.MAGIC_ROOM_DOWNSYNC_FRAME_ID = { BATTLE_START: 0 }; +window.PlayerBattleState = { + ADDED_PENDING_BATTLE_COLLIDER_ACK: 0, + READDED_PENDING_BATTLE_COLLIDER_ACK: 1, + ACTIVE: 2, + DISCONNECTED: 3, + LOST: 4, + EXPELLED_DURING_GAME: 5, + EXPELLED_IN_DISMISSAL: 6 +}; + cc.Class({ extends: cc.Component, @@ -97,6 +107,10 @@ cc.Class({ type: cc.TiledMap, default: null }, + renderFrameIdLagTolerance: { + type: cc.Integer, + default: 4 // implies (renderFrameIdLagTolerance >> inputScaleFrames) count of inputFrameIds + }, }, _inputFrameIdDebuggable(inputFrameId) { @@ -334,10 +348,10 @@ cc.Class({ window.handleClientSessionCloseOrError = function() { console.warn('+++++++ Common handleClientSessionCloseOrError()'); - if (ALL_BATTLE_STATES.IN_SETTLEMENT == self.battleState) { //如果是游戏时间结束引起的断连 - console.log("游戏结束引起的断连, 不需要回到登录页面"); + if (ALL_BATTLE_STATES.IN_SETTLEMENT == self.battleState) { + console.log("Battled ended by settlement"); } else { - console.warn("意外断连,即将回到登录页面"); + console.warn("Connection lost, going back to login page"); window.clearLocalStorageAndBackToLoginScene(true); } }; @@ -510,7 +524,7 @@ cc.Class({ } else if (null != boundRoomId) { self.disableGameRuleNode(); self.battleState = ALL_BATTLE_STATES.WAITING; - window.initPersistentSessionClient(self.initAfterWSConnected, expectedRoomId); + window.initPersistentSessionClient(self.initAfterWSConnected, boundRoomId); } else { self.showPopupInCanvas(self.gameRuleNode); // Deliberately left blank. -- YFLu @@ -633,10 +647,11 @@ cc.Class({ if (window.RING_BUFF_NON_CONSECUTIVE_SET == dumpRenderCacheRet) { // Deliberately left blank, in this case "chaserRenderFrameId" is already reset to proper value. } else { + const inputFrameIdConsecutive = (inputFrameDownsyncId == self.lastAllConfirmedInputFrameId + 1); const localInputFrame = self.recentInputCache.getByFrameId(inputFrameDownsyncId); - if (null == localInputFrame) { - console.warn("localInputFrame not existing: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false)); - } else if (null == firstPredictedYetIncorrectInputFrameId && !self.equalInputLists(localInputFrame.inputList, inputFrameDownsync.inputList)) { + if (null == localInputFrame && false == inputFrameIdConsecutive) { + throw "localInputFrame not existing and is NOT CONSECUTIVELY EXTENDING recentInputCache: inputFrameDownsyncId=" + inputFrameDownsyncId + ", lastAllConfirmedInputFrameId=" + self.lastAllConfirmedInputFrameId + ", recentInputCache=" + self._stringifyRecentInputCache(false); + } else if (null == firstPredictedYetIncorrectInputFrameId && null != localInputFrame && !self.equalInputLists(localInputFrame.inputList, inputFrameDownsync.inputList)) { firstPredictedYetIncorrectInputFrameId = inputFrameDownsyncId; } } @@ -687,7 +702,7 @@ cc.Class({ logBattleStats() { const self = this; let s = []; - s.push("Battle stats: lastUpsyncInputFrameId=" + self.lastUpsyncInputFrameId + ", lastAllConfirmedInputFrameId=" + self.lastAllConfirmedInputFrameId); + s.push("Battle stats: renderFrameId=" + self.renderFrameId + ", lastAllConfirmedRenderFrameId=" + self.lastAllConfirmedRenderFrameId + ", lastUpsyncInputFrameId=" + self.lastUpsyncInputFrameId + ", lastAllConfirmedInputFrameId=" + self.lastAllConfirmedInputFrameId); for (let i = self.recentInputCache.stFrameId; i < self.recentInputCache.edFrameId; ++i) { const inputFrameDownsync = self.recentInputCache.getByFrameId(i); @@ -790,7 +805,7 @@ cc.Class({ } finally { // Update countdown if (null != self.countdownNanos) { - self.countdownNanos -= self.rollbackEstimatedDt * 1000000000; + self.countdownNanos -= (performance.now() - self.lastRenderFrameIdTriggeredAt) * 1000000; if (self.countdownNanos <= 0) { self.onBattleStopped(self.playerRichInfoDict); return; @@ -879,29 +894,26 @@ cc.Class({ setLocalZOrder(toShowNode, 10); }, - onBattleReadyToStart(playerMetas, isSelfRejoining) { + hideFindingPlayersGUI() { + const self = this; + if (null == self.findingPlayerNode.parent) return; + self.findingPlayerNode.parent.removeChild(self.findingPlayerNode); + }, + + onBattleReadyToStart(playerMetas) { console.log("Calling `onBattleReadyToStart` with:", playerMetas); const self = this; const findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer"); findingPlayerScriptIns.hideExitButton(); findingPlayerScriptIns.updatePlayersInfo(playerMetas); - const hideFindingPlayersGUI = function() { - if (null == self.findingPlayerNode.parent) return; - self.findingPlayerNode.parent.removeChild(self.findingPlayerNode); - }; - - if (true == isSelfRejoining) { - hideFindingPlayersGUI(); - } else { - // Delay to hide the "finding player" GUI, then show a countdown clock - window.setTimeout(() => { - hideFindingPlayersGUI(); - const countDownScriptIns = self.countdownToBeginGameNode.getComponent("CountdownToBeginGame"); - countDownScriptIns.setData(); - self.showPopupInCanvas(self.countdownToBeginGameNode); - }, 1500); - } + // Delay to hide the "finding player" GUI, then show a countdown clock + window.setTimeout(() => { + self.hideFindingPlayersGUI(); + const countDownScriptIns = self.countdownToBeginGameNode.getComponent("CountdownToBeginGame"); + countDownScriptIns.setData(); + self.showPopupInCanvas(self.countdownToBeginGameNode); + }, 1500); }, _createRoomDownsyncFrameLocally(renderFrameId, collisionSys, collisionSysMap) { @@ -1046,7 +1058,7 @@ cc.Class({ } } - latestRdf = self._createRoomDownsyncFrameLocally(i+1, collisionSys, collisionSysMap); + latestRdf = self._createRoomDownsyncFrameLocally(i + 1, collisionSys, collisionSysMap); } return latestRdf; diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index 6c74ec8..12569a3 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -166,9 +166,10 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { break; case window.DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED: // Deliberately left blank for now + mapIns.hideFindingPlayersGUI(); break; case window.DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START: - mapIns.onBattleReadyToStart(resp.rdf.playerMetas, false); + mapIns.onBattleReadyToStart(resp.rdf.playerMetas); break; case window.DOWNSYNC_MSG_ACT_BATTLE_START: mapIns.onRoomDownsyncFrame(resp.rdf); @@ -180,10 +181,22 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); break; case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC: - console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp)); - // The following order of execution is important, because "onInputFrameDownsyncBatch" is only available when state is IN_BATTLE - const dumpRenderCacheRet = mapIns.onRoomDownsyncFrame(resp.rdf); - mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch, dumpRenderCacheRet); + if (null == resp.inputFrameDownsyncBatch || 0 >= resp.inputFrameDownsyncBatch.length) { + console.error("Got empty inputFrameDownsyncBatch upon resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp, null, 2)); + return; + } + // Unless upon ws session lost and reconnected, it's maintained true that "inputFrameDownsyncBatch[0].inputFrameId == frontend.lastAllConfirmedInputFrameId+1", and in this case we should try to keep frontend moving only by "frontend.recentInputCache" to avoid jiggling of synced positions + const inputFrameIdConsecutive = (resp.inputFrameDownsyncBatch[0].inputFrameId == mapIns.lastAllConfirmedInputFrameId + 1); + const renderFrameIdConsecutive = (resp.rdf.id <= mapIns.renderFrameId + mapIns.renderFrameIdLagTolerance); + if (inputFrameIdConsecutive && renderFrameIdConsecutive) { + console.log("Got consecutive resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp)); + mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); + } else { + console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp, null, 2)); + // The following order of execution is important + const dumpRenderCacheRet = mapIns.onRoomDownsyncFrame(resp.rdf); + mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch, dumpRenderCacheRet); + } break; default: break; diff --git a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js index de4a071..e236559 100644 --- a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js +++ b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js @@ -1196,6 +1196,7 @@ $root.treasurehunterx = (function() { * @property {number|null} [nstDelayFrames] BattleColliderInfo nstDelayFrames * @property {number|null} [inputFrameUpsyncDelayTolerance] BattleColliderInfo inputFrameUpsyncDelayTolerance * @property {number|null} [maxChasingRenderFramesPerUpdate] BattleColliderInfo maxChasingRenderFramesPerUpdate + * @property {number|null} [playerBattleState] BattleColliderInfo playerBattleState */ /** @@ -1351,6 +1352,14 @@ $root.treasurehunterx = (function() { */ BattleColliderInfo.prototype.maxChasingRenderFramesPerUpdate = 0; + /** + * BattleColliderInfo playerBattleState. + * @member {number} playerBattleState + * @memberof treasurehunterx.BattleColliderInfo + * @instance + */ + BattleColliderInfo.prototype.playerBattleState = 0; + /** * Creates a new BattleColliderInfo instance using the specified properties. * @function create @@ -1415,6 +1424,8 @@ $root.treasurehunterx = (function() { writer.uint32(/* id 16, wireType 0 =*/128).int32(message.inputFrameUpsyncDelayTolerance); if (message.maxChasingRenderFramesPerUpdate != null && Object.hasOwnProperty.call(message, "maxChasingRenderFramesPerUpdate")) writer.uint32(/* id 17, wireType 0 =*/136).int32(message.maxChasingRenderFramesPerUpdate); + if (message.playerBattleState != null && Object.hasOwnProperty.call(message, "playerBattleState")) + writer.uint32(/* id 18, wireType 0 =*/144).int32(message.playerBattleState); return writer; }; @@ -1555,6 +1566,10 @@ $root.treasurehunterx = (function() { message.maxChasingRenderFramesPerUpdate = reader.int32(); break; } + case 18: { + message.playerBattleState = reader.int32(); + break; + } default: reader.skipType(tag & 7); break; @@ -1655,6 +1670,9 @@ $root.treasurehunterx = (function() { if (message.maxChasingRenderFramesPerUpdate != null && message.hasOwnProperty("maxChasingRenderFramesPerUpdate")) if (!$util.isInteger(message.maxChasingRenderFramesPerUpdate)) return "maxChasingRenderFramesPerUpdate: integer expected"; + if (message.playerBattleState != null && message.hasOwnProperty("playerBattleState")) + if (!$util.isInteger(message.playerBattleState)) + return "playerBattleState: integer expected"; return null; }; @@ -1727,6 +1745,8 @@ $root.treasurehunterx = (function() { message.inputFrameUpsyncDelayTolerance = object.inputFrameUpsyncDelayTolerance | 0; if (object.maxChasingRenderFramesPerUpdate != null) message.maxChasingRenderFramesPerUpdate = object.maxChasingRenderFramesPerUpdate | 0; + if (object.playerBattleState != null) + message.playerBattleState = object.playerBattleState | 0; return message; }; @@ -1767,6 +1787,7 @@ $root.treasurehunterx = (function() { object.nstDelayFrames = 0; object.inputFrameUpsyncDelayTolerance = 0; object.maxChasingRenderFramesPerUpdate = 0; + object.playerBattleState = 0; } if (message.stageName != null && message.hasOwnProperty("stageName")) object.stageName = message.stageName; @@ -1812,6 +1833,8 @@ $root.treasurehunterx = (function() { object.inputFrameUpsyncDelayTolerance = message.inputFrameUpsyncDelayTolerance; if (message.maxChasingRenderFramesPerUpdate != null && message.hasOwnProperty("maxChasingRenderFramesPerUpdate")) object.maxChasingRenderFramesPerUpdate = message.maxChasingRenderFramesPerUpdate; + if (message.playerBattleState != null && message.hasOwnProperty("playerBattleState")) + object.playerBattleState = message.playerBattleState; return object; };