From 80c6e057310587642501db6318d58f21a4ad8ac7 Mon Sep 17 00:00:00 2001 From: genxium Date: Mon, 2 Jan 2023 23:35:56 +0800 Subject: [PATCH] Initial attempt integrating online battle. --- battle_srv/models/pb_type_convert.go | 98 +++++++++---------- battle_srv/models/room.go | 97 +++++++++--------- battle_srv/protos/room_downsync_frame.pb.go | 17 +++- battle_srv/ws/serve.go | 19 ++-- frontend/assets/plugin_scripts/jsexport.js | 10 +- frontend/assets/resources/map/dungeon/map.tmx | 4 +- .../pbfiles/room_downsync_frame.proto | 1 + frontend/assets/scenes/login.fire | 2 +- frontend/assets/scenes/offline_map.fire | 2 +- frontend/assets/scripts/Map.js | 2 +- ...om_downsync_frame_proto_bundle.forcemsg.js | 47 +++++++++ jsexport/battle/battle.go | 6 +- jsexport/battle/characterConfig.go | 3 + jsexport/main.go | 14 +-- 14 files changed, 198 insertions(+), 124 deletions(-) diff --git a/battle_srv/models/pb_type_convert.go b/battle_srv/models/pb_type_convert.go index cacfe48..b7ce3f0 100644 --- a/battle_srv/models/pb_type_convert.go +++ b/battle_srv/models/pb_type_convert.go @@ -10,12 +10,12 @@ func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame return nil } ret := &pb.RoomDownsyncFrame{ - Id: rdf.Id, - PlayersArr: make([]*pb.PlayerDownsync, len(rdf.PlayersArr), len(rdf.PlayersArr)), - MeleeBullets: make([]*pb.MeleeBullet, len(rdf.MeleeBullets), len(rdf.MeleeBullets)), - CountdownNanos: rdf.CountdownNanos, - BackendUnconfirmedMask: rdf.BackendUnconfirmedMask, - ShouldForceResync: rdf.ShouldForceResync, + Id: rdf.Id, + PlayersArr: make([]*pb.PlayerDownsync, len(rdf.PlayersArr), len(rdf.PlayersArr)), + MeleeBullets: make([]*pb.MeleeBullet, len(rdf.MeleeBullets), len(rdf.MeleeBullets)), + CountdownNanos: rdf.CountdownNanos, + BackendUnconfirmedMask: rdf.BackendUnconfirmedMask, + ShouldForceResync: rdf.ShouldForceResync, } for i, last := range rdf.PlayersArr { @@ -29,8 +29,8 @@ func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame VelY: last.VelY, FramesToRecover: last.FramesToRecover, FramesInChState: last.FramesInChState, - ActiveSkillId: last.ActiveSkillId, - ActiveSkillHit: last.ActiveSkillHit, + ActiveSkillId: last.ActiveSkillId, + ActiveSkillHit: last.ActiveSkillHit, Speed: last.Speed, BattleState: last.BattleState, CharacterState: last.CharacterState, @@ -47,29 +47,29 @@ func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame for i, last := range rdf.MeleeBullets { pbBullet := &pb.MeleeBullet{ - OriginatedRenderFrameId: last.OriginatedRenderFrameId, - OffenderJoinIndex: last.OffenderJoinIndex, + OriginatedRenderFrameId: last.OriginatedRenderFrameId, + OffenderJoinIndex: last.OffenderJoinIndex, - StartupFrames: last.StartupFrames, - CancellableStFrame: last.CancellableStFrame, - CancellableEdFrame: last.CancellableEdFrame, - ActiveFrames: last.ActiveFrames, + StartupFrames: last.StartupFrames, + CancellableStFrame: last.CancellableStFrame, + CancellableEdFrame: last.CancellableEdFrame, + ActiveFrames: last.ActiveFrames, - HitStunFrames: last.HitStunFrames, - BlockStunFrames: last.BlockStunFrames, - PushbackVelX: last.PushbackVelX, - PushbackVelY: last.PushbackVelY, - Damage: last.Damage, + HitStunFrames: last.HitStunFrames, + BlockStunFrames: last.BlockStunFrames, + PushbackVelX: last.PushbackVelX, + PushbackVelY: last.PushbackVelY, + Damage: last.Damage, SelfLockVelX: last.SelfLockVelX, SelfLockVelY: last.SelfLockVelY, - HitboxOffsetX: last.HitboxOffsetX, - HitboxOffsetY: last.HitboxOffsetY, - HitboxSizeX: last.HitboxSizeX, - HitboxSizeY: last.HitboxSizeY, + HitboxOffsetX: last.HitboxOffsetX, + HitboxOffsetY: last.HitboxOffsetY, + HitboxSizeX: last.HitboxSizeX, + HitboxSizeY: last.HitboxSizeY, - BlowUp: last.BlowUp, + BlowUp: last.BlowUp, } ret.MeleeBullets[i] = pbBullet } @@ -77,7 +77,6 @@ func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame return ret } -/* func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) []*pb.PlayerDownsync { toRet := make([]*pb.PlayerDownsync, len(modelInstances), len(modelInstances)) if nil == modelInstances { @@ -93,6 +92,10 @@ func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) []*pb.Play DirY: last.DirY, VelX: last.VelX, VelY: last.VelY, + FramesToRecover: last.FramesToRecover, + FramesInChState: last.FramesInChState, + ActiveSkillId: last.ActiveSkillId, + ActiveSkillHit: last.ActiveSkillHit, Speed: last.Speed, BattleState: last.BattleState, CharacterState: last.CharacterState, @@ -101,8 +104,6 @@ func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) []*pb.Play ColliderRadius: last.ColliderRadius, Score: last.Score, Removed: last.Removed, - FramesToRecover: last.FramesToRecover, - FramesInChState: last.FramesInChState, } if withMetaInfo { pbPlayer.Name = last.Name @@ -114,7 +115,6 @@ func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) []*pb.Play return toRet } -*/ func toJsPlayers(modelInstances map[int32]*Player) []*battle.PlayerDownsync { toRet := make([]*battle.PlayerDownsync, len(modelInstances), len(modelInstances)) @@ -124,27 +124,27 @@ func toJsPlayers(modelInstances map[int32]*Player) []*battle.PlayerDownsync { for _, last := range modelInstances { toRet[last.JoinIndex-1] = &battle.PlayerDownsync{ - Id: last.Id, - VirtualGridX: last.VirtualGridX, - VirtualGridY: last.VirtualGridY, - DirX: last.DirX, - DirY: last.DirY, - VelX: last.VelX, - VelY: last.VelY, - FramesToRecover: last.FramesToRecover, - FramesInChState: last.FramesInChState, - ActiveSkillId: last.ActiveSkillId, - ActiveSkillHit: last.ActiveSkillHit, - Speed: last.Speed, - BattleState: last.BattleState, - CharacterState: last.CharacterState, - JoinIndex: last.JoinIndex, - Hp: last.Hp, - MaxHp: last.MaxHp, - ColliderRadius: last.ColliderRadius, - InAir: last.InAir, - Score: last.Score, - Removed: last.Removed, + Id: last.Id, + VirtualGridX: last.VirtualGridX, + VirtualGridY: last.VirtualGridY, + DirX: last.DirX, + DirY: last.DirY, + VelX: last.VelX, + VelY: last.VelY, + FramesToRecover: last.FramesToRecover, + FramesInChState: last.FramesInChState, + ActiveSkillId: last.ActiveSkillId, + ActiveSkillHit: last.ActiveSkillHit, + Speed: last.Speed, + BattleState: last.BattleState, + CharacterState: last.CharacterState, + JoinIndex: last.JoinIndex, + Hp: last.Hp, + MaxHp: last.MaxHp, + ColliderRadius: last.ColliderRadius, + InAir: last.InAir, + Score: last.Score, + Removed: last.Removed, } } diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index ec362cd..0cc7ac3 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -88,12 +88,16 @@ func calRoomScore(inRoomPlayerCount int32, roomPlayerCnt int, currentRoomBattleS } type Room struct { - Id int32 - Capacity int - Players map[int32]*Player - PlayersArr []*Player // ordered by joinIndex - Space *resolv.Space - CollisionSysMap map[int32]*resolv.Object + Id int32 + Capacity int + BattleDurationFrames int32 + NstDelayFrames int32 + Players map[int32]*Player + PlayersArr []*Player // ordered by joinIndex + SpeciesIdList []int32 // ordered by joinIndex + CharacterConfigsArr []*battle.CharacterConfig // ordered by joinIndex + Space *resolv.Space + CollisionSysMap map[int32]*resolv.Object /** * The following `PlayerDownsyncSessionDict` is NOT individually put * under `type Player struct` for a reason. @@ -135,7 +139,6 @@ type Room struct { BackendDynamicsEnabled bool ForceAllResyncOnAnyActiveSlowTicker bool LastRenderFrameIdTriggeredAt int64 - PlayerDefaultSpeed int32 BulletBattleLocalIdCounter int32 dilutedRollbackEstimatedDtNanos int64 @@ -165,11 +168,12 @@ func (pR *Room) AddPlayerIfPossible(pPlayerFromDbInit *Player, session *websocke } defer pR.onPlayerAdded(playerId) + pPlayerFromDbInit.AckingFrameId = -1 pPlayerFromDbInit.AckingInputFrameId = -1 pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED pPlayerFromDbInit.BattleState = PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK - pPlayerFromDbInit.Speed = pR.PlayerDefaultSpeed // Hardcoded + pPlayerFromDbInit.ColliderRadius = DEFAULT_PLAYER_RADIUS // Hardcoded pPlayerFromDbInit.InAir = true // Hardcoded @@ -207,7 +211,7 @@ func (pR *Room) ReAddPlayerIfPossible(pTmpPlayerInstance *Player, session *webso pEffectiveInRoomPlayerInstance.AckingInputFrameId = -1 pEffectiveInRoomPlayerInstance.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED pEffectiveInRoomPlayerInstance.BattleState = PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK - pEffectiveInRoomPlayerInstance.Speed = pR.PlayerDefaultSpeed // Hardcoded + pEffectiveInRoomPlayerInstance.ColliderRadius = DEFAULT_PLAYER_RADIUS // Hardcoded pEffectiveInRoomPlayerInstance.InAir = true // Hardcoded @@ -286,8 +290,8 @@ func (pR *Room) ChooseStage() error { //Logger.Info("parsed tmx:", zap.Any("stageDiscreteW", stageDiscreteW), zap.Any("strToVec2DListMap", strToVec2DListMap), zap.Any("strToPolygon2DListMap", strToPolygon2DListMap)) - pR.SpaceOffsetX = float64((stageDiscreteW*stageTileW) >> 1) - pR.SpaceOffsetY = float64((stageDiscreteH*stageTileH) >> 1) + pR.SpaceOffsetX = float64((stageDiscreteW * stageTileW) >> 1) + pR.SpaceOffsetY = float64((stageDiscreteH * stageTileH) >> 1) pR.TmxPointsMap = strToVec2DListMap pR.TmxPolygonsMap = strToPolygon2DListMap @@ -373,12 +377,20 @@ func (pR *Room) StartBattle() { pR.RenderFrameId = 0 + for _, player := range pR.Players { + speciesId := int(player.JoinIndex - 1) // FIXME: Hardcoded the values for now + chosenCh := battle.Characters[speciesId] + pR.CharacterConfigsArr[player.JoinIndex-1] = chosenCh + pR.SpeciesIdList[player.JoinIndex-1] = int32(speciesId) + } + Logger.Info("[StartBattle] ", zap.Any("roomId", pR.Id), zap.Any("roomState", pR.State), zap.Any("SpeciesIdList", pR.SpeciesIdList)) + // Initialize the "collisionSys" as well as "RenderFrameBuffer" pR.CurDynamicsRenderFrameId = 0 kickoffFrameJs := &battle.RoomDownsyncFrame{ - Id: pR.RenderFrameId, - PlayersArr: toJsPlayers(pR.Players), - CountdownNanos: pR.BattleDurationNanos, + Id: pR.RenderFrameId, + PlayersArr: toJsPlayers(pR.Players), + CountdownNanos: pR.BattleDurationNanos, } pR.RenderFrameBuffer.Put(kickoffFrameJs) @@ -446,7 +458,9 @@ func (pR *Room) StartBattle() { continue } kickoffFrameJs := pR.RenderFrameBuffer.GetByFrameId(0).(*battle.RoomDownsyncFrame) - pR.sendSafely(toPbRoomDownsyncFrame(kickoffFrameJs), nil, DOWNSYNC_MSG_ACT_BATTLE_START, playerId, true) + pbKickOffRenderFrame := toPbRoomDownsyncFrame(kickoffFrameJs) + pbKickOffRenderFrame.SpeciesIdList = pR.SpeciesIdList + pR.sendSafely(pbKickOffRenderFrame, nil, DOWNSYNC_MSG_ACT_BATTLE_START, playerId, true) } Logger.Info(fmt.Sprintf("In `battleMainLoop` for roomId=%v sent out kickoffFrame", pR.Id)) } @@ -515,10 +529,6 @@ 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) { /* [WARNING] This function "OnBattleCmdReceived" could be called by different ws sessions and thus from different threads! @@ -709,14 +719,11 @@ 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.BulletBattleLocalIdCounter = 0 - pR.WorldToVirtualGridRatio = battle.WORLD_TO_VIRTUAL_GRID_RATIO - pR.VirtualGridToWorldRatio = float64(1.0) / pR.WorldToVirtualGridRatio // this is a one-off computation, should avoid division in iterations - pR.SpAtkLookupFrames = 5 - pR.PlayerDefaultSpeed = int32(float64(1) * pR.WorldToVirtualGridRatio) // in virtual grids per frame - pR.CollisionMinStep = (int32(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 3) // the approx minimum distance a player can move per frame in world coordinate - pR.playerOpPatternToSkillId = make(map[int]int) + pR.CollisionMinStep = 8 // the approx minimum distance a player can move per frame in world coordinate pR.Players = make(map[int32]*Player) pR.PlayersArr = make([]*Player, pR.Capacity) + pR.SpeciesIdList = make([]int32, pR.Capacity) + pR.CharacterConfigsArr = make([]*battle.CharacterConfig, pR.Capacity) pR.CollisionSysMap = make(map[int32]*resolv.Object) pR.PlayerDownsyncSessionDict = make(map[int32]*websocket.Conn) for _, oldWatchdog := range pR.PlayerActiveWatchdogDict { @@ -741,18 +748,17 @@ func (pR *Room) OnDismissed() { pR.RenderFrameId = 0 pR.CurDynamicsRenderFrameId = 0 - pR.InputDelayFrames = 8 pR.NstDelayFrames = 16 - pR.InputScaleFrames = uint32(2) - pR.ServerFps = 60 + + serverFps := 60 pR.RollbackEstimatedDtMillis = 16.667 // Use fixed-and-low-precision to mitigate the inconsistent floating-point-number issue between Golang and JavaScript pR.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for logging FAST FRAME dilutedServerFps := float64(58.0) // Don't set this value too small, otherwise we might miss force confirmation needs for slow tickers! - pR.dilutedRollbackEstimatedDtNanos = int64(float64(pR.RollbackEstimatedDtNanos) * float64(pR.ServerFps) / dilutedServerFps) - pR.BattleDurationFrames = 60 * pR.ServerFps + pR.dilutedRollbackEstimatedDtNanos = int64(float64(pR.RollbackEstimatedDtNanos) * float64(serverFps) / dilutedServerFps) + pR.BattleDurationFrames = int32(60 * serverFps) pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1) - pR.InputFrameUpsyncDelayTolerance = (pR.NstDelayFrames >> pR.InputScaleFrames) - 1 // this value should be strictly smaller than (NstDelayFrames >> InputScaleFrames), otherwise "type#1 forceConfirmation" might become a lag avalanche - pR.MaxChasingRenderFramesPerUpdate = 12 // Don't set this value too high to avoid exhausting frontend CPU within a single frame + pR.InputFrameUpsyncDelayTolerance = battle.ConvertToNoDelayInputFrameId(pR.NstDelayFrames) - 1 // this value should be strictly smaller than (NstDelayFrames >> InputScaleFrames), otherwise "type#1 forceConfirmation" might become a lag avalanche + pR.MaxChasingRenderFramesPerUpdate = 12 // Don't set this value too high to avoid exhausting frontend CPU within a single frame pR.BackendDynamicsEnabled = true // [WARNING] When "false", recovery upon reconnection wouldn't work! pR.ForceAllResyncOnAnyActiveSlowTicker = true // See tradeoff discussion in "downsyncToAllPlayers" @@ -874,6 +880,10 @@ func (pR *Room) onPlayerAdded(playerId int32) { pR.Players[playerId].JoinIndex = int32(index) + 1 pR.JoinIndexBooleanArr[index] = true + speciesId := index // FIXME + chosenCh := battle.Characters[speciesId] + pR.Players[playerId].Speed = chosenCh.Speed + // Lazily assign the initial position of "Player" for "RoomDownsyncFrame". playerPosList := *pR.TmxPointsMap["PlayerStartingPos"] if index > len(playerPosList) { @@ -884,7 +894,7 @@ func (pR *Room) onPlayerAdded(playerId int32) { if nil == playerPos { panic(fmt.Sprintf("onPlayerAdded error, nil == playerPos, roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount)) } - pR.Players[playerId].VirtualGridX, pR.Players[playerId].VirtualGridY = battle.WorldToVirtualGridPos(playerPos.X, playerPos.Y, pR.WorldToVirtualGridRatio) + pR.Players[playerId].VirtualGridX, pR.Players[playerId].VirtualGridY = battle.WorldToVirtualGridPos(playerPos.X, playerPos.Y) // Hardcoded initial character orientation/facing if 0 == (pR.Players[playerId].JoinIndex % 2) { pR.Players[playerId].DirX = -2 @@ -1121,7 +1131,7 @@ func (pR *Room) markConfirmationIfApplicable(inputFrameUpsyncBatch []*pb.InputFr */ snapshotStFrameId := (pR.LastAllConfirmedInputFrameId - newAllConfirmedCount) refRenderFrameIdIfNeeded := pR.CurDynamicsRenderFrameId - 1 - refSnapshotStFrameId := battle.ConvertToInputFrameId(refRenderFrameIdIfNeeded, battle.INPUT_DELAY_FRAMES, battle.INPUT_SCALE_FRAMES) + refSnapshotStFrameId := battle.ConvertToDelayedInputFrameId(refRenderFrameIdIfNeeded) if refSnapshotStFrameId < snapshotStFrameId { snapshotStFrameId = refSnapshotStFrameId } @@ -1137,7 +1147,7 @@ func (pR *Room) forceConfirmationIfApplicable(prevRenderFrameId int32) uint64 { totPlayerCnt := uint32(pR.Capacity) allConfirmedMask := uint64((1 << totPlayerCnt) - 1) unconfirmedMask := uint64(0) - if pR.LatestPlayerUpsyncedInputFrameId > (pR.LastAllConfirmedInputFrameId + (pR.NstDelayFrames >> pR.InputScaleFrames)) { + if pR.LatestPlayerUpsyncedInputFrameId > (pR.LastAllConfirmedInputFrameId + pR.InputFrameUpsyncDelayTolerance + 1) { // Type#1 check whether there's a significantly slow ticker among players oldLastAllConfirmedInputFrameId := pR.LastAllConfirmedInputFrameId for j := pR.LastAllConfirmedInputFrameId + 1; j <= pR.LatestPlayerUpsyncedInputFrameId; j++ { @@ -1151,7 +1161,7 @@ func (pR *Room) forceConfirmationIfApplicable(prevRenderFrameId int32) uint64 { pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, -1) } if 0 < unconfirmedMask { - Logger.Info(fmt.Sprintf("[type#1 forceConfirmation] For roomId=%d@renderFrameId=%d, curDynamicsRenderFrameId=%d, LatestPlayerUpsyncedInputFrameId:%d, oldLastAllConfirmedInputFrameId:%d, newLastAllConfirmedInputFrameId:%d, (pR.NstDelayFrames >> pR.InputScaleFrames):%d, InputFrameUpsyncDelayTolerance:%d, unconfirmedMask=%d; there's a slow ticker suspect, forcing all-confirmation", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LatestPlayerUpsyncedInputFrameId, oldLastAllConfirmedInputFrameId, pR.LastAllConfirmedInputFrameId, (pR.NstDelayFrames >> pR.InputScaleFrames), pR.InputFrameUpsyncDelayTolerance, unconfirmedMask)) + Logger.Info(fmt.Sprintf("[type#1 forceConfirmation] For roomId=%d@renderFrameId=%d, curDynamicsRenderFrameId=%d, LatestPlayerUpsyncedInputFrameId:%d, oldLastAllConfirmedInputFrameId:%d, newLastAllConfirmedInputFrameId:%d, InputFrameUpsyncDelayTolerance:%d, unconfirmedMask=%d; there's a slow ticker suspect, forcing all-confirmation", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LatestPlayerUpsyncedInputFrameId, oldLastAllConfirmedInputFrameId, pR.LastAllConfirmedInputFrameId, pR.InputFrameUpsyncDelayTolerance, unconfirmedMask)) } } else { // Type#2 helps resolve the edge case when all players are disconnected temporarily @@ -1226,7 +1236,7 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende } } - nextRenderFrame := battle.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(pR.InputsBuffer, currRenderFrame, pR.Space, pR.CollisionSysMap, pR.SpaceOffsetX, pR.SpaceOffsetY) + nextRenderFrame := battle.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(pR.InputsBuffer, currRenderFrame, pR.Space, pR.CollisionSysMap, pR.SpaceOffsetX, pR.SpaceOffsetY, pR.CharacterConfigsArr) pR.RenderFrameBuffer.Put(nextRenderFrame) pR.CurDynamicsRenderFrameId++ } @@ -1248,7 +1258,7 @@ func (pR *Room) refreshColliders() { // For debug-printing only. Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points)) */ - barrierCollider := battle.GenerateConvexPolygonCollider(polygon2DUnaligned, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, nil, "Barrier") + barrierCollider := battle.GenerateConvexPolygonCollider(polygon2DUnaligned, pR.SpaceOffsetX, pR.SpaceOffsetY, nil, "Barrier") pR.Space.Add(barrierCollider) } } @@ -1280,7 +1290,7 @@ func (pR *Room) doBattleMainLoopPerTickBackendDynamicsWithProperLocking(prevRend dynamicsStartedAt := utils.UnixtimeNano() // Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId" nextDynamicsRenderFrameId := battle.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId) + 1 - Logger.Debug(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, room.CurDynamicsRenderFrameId=%v, LastAllConfirmedInputFrameId=%v, InputDelayFrames=%v, nextDynamicsRenderFrameId=%v", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames, nextDynamicsRenderFrameId)) + Logger.Debug(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, room.CurDynamicsRenderFrameId=%v, LastAllConfirmedInputFrameId=%v, nextDynamicsRenderFrameId=%v", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LastAllConfirmedInputFrameId, nextDynamicsRenderFrameId)) pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId) *pDynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt } @@ -1298,7 +1308,7 @@ func (pR *Room) doBattleMainLoopPerTickBackendDynamicsWithProperLocking(prevRend if 0 < unconfirmedMask { // [WARNING] As "pR.CurDynamicsRenderFrameId" was just incremented above, "refSnapshotStFrameId" is most possibly larger than "oldLastAllConfirmedInputFrameId + 1", therefore this initial assignment is critical for `ACTIVE NORMAL TICKER`s to receive consecutive ids of inputFrameDownsync. snapshotStFrameId := oldLastAllConfirmedInputFrameId + 1 - refSnapshotStFrameId := battle.ConvertToDelayedInputFrameId(pR.CurDynamicsRenderFrameId-1) + refSnapshotStFrameId := battle.ConvertToDelayedInputFrameId(pR.CurDynamicsRenderFrameId - 1) if refSnapshotStFrameId < snapshotStFrameId { snapshotStFrameId = refSnapshotStFrameId } @@ -1410,14 +1420,13 @@ func (pR *Room) downsyncToSinglePlayer(playerId int32, player *Player, refRender } refRenderFrame := tmp.(*battle.RoomDownsyncFrame) - for i, player := range pR.PlayersArr { - refRenderFrame.PlayersArr[i].ColliderRadius = player.ColliderRadius // hardcoded for now - } if shouldResync3 { refRenderFrame.ShouldForceResync = true } refRenderFrame.BackendUnconfirmedMask = unconfirmedMask - pR.sendSafely(toPbRoomDownsyncFrame(refRenderFrame), toSendInputFrameDownsyncsSnapshot, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId, false) + pbRefRenderFrame := toPbRoomDownsyncFrame(refRenderFrame) + pbRefRenderFrame.SpeciesIdList = pR.SpeciesIdList + pR.sendSafely(pbRefRenderFrame, toSendInputFrameDownsyncsSnapshot, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId, false) //Logger.Warn(fmt.Sprintf("Sent refRenderFrameId=%v & inputFrameIds [%d, %d), for roomId=%v, playerId=%d, playerJoinIndex=%d, renderFrameId=%d, curDynamicsRenderFrameId=%d, playerLastSentInputFrameId=%d: InputsBuffer=%v", refRenderFrameId, toSendInputFrameIdSt, toSendInputFrameIdEd, pR.Id, playerId, player.JoinIndex, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, player.LastSentInputFrameId, pR.InputsBufferString(false))) if shouldResync1 { Logger.Warn(fmt.Sprintf("Sent refRenderFrameId=%v & inputFrameIds [%d, %d), for roomId=%v, playerId=%d, playerJoinIndex=%d, renderFrameId=%d, curDynamicsRenderFrameId=%d, playerLastSentInputFrameId=%d: shouldResync1=%v, shouldResync2=%v, shouldResync3=%v, playerBattleState=%d", refRenderFrameId, toSendInputFrameIdSt, toSendInputFrameIdEd, pR.Id, playerId, player.JoinIndex, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, player.LastSentInputFrameId, shouldResync1, shouldResync2, shouldResync3, playerBattleState)) diff --git a/battle_srv/protos/room_downsync_frame.pb.go b/battle_srv/protos/room_downsync_frame.pb.go index 9f5f3f5..a93f289 100644 --- a/battle_srv/protos/room_downsync_frame.pb.go +++ b/battle_srv/protos/room_downsync_frame.pb.go @@ -1103,6 +1103,7 @@ type RoomDownsyncFrame struct { MeleeBullets []*MeleeBullet `protobuf:"bytes,4,rep,name=meleeBullets,proto3" json:"meleeBullets,omitempty"` // I don't know how to mimic inheritance/composition in protobuf by far, thus using an array for each type of bullet as a compromise BackendUnconfirmedMask uint64 `protobuf:"varint,5,opt,name=backendUnconfirmedMask,proto3" json:"backendUnconfirmedMask,omitempty"` // Indexed by "joinIndex", same compression concern as stated in InputFrameDownsync ShouldForceResync bool `protobuf:"varint,6,opt,name=shouldForceResync,proto3" json:"shouldForceResync,omitempty"` + SpeciesIdList []int32 `protobuf:"varint,7,rep,packed,name=speciesIdList,proto3" json:"speciesIdList,omitempty"` } func (x *RoomDownsyncFrame) Reset() { @@ -1179,6 +1180,13 @@ func (x *RoomDownsyncFrame) GetShouldForceResync() bool { return false } +func (x *RoomDownsyncFrame) GetSpeciesIdList() []int32 { + if x != nil { + return x.SpeciesIdList + } + return nil +} + var File_room_downsync_frame_proto protoreflect.FileDescriptor var file_room_downsync_frame_proto_rawDesc = []byte{ @@ -1396,7 +1404,7 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x12, 0x39, 0x0a, 0x17, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0xe7, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4c, 0x6f, 0x67, - 0x67, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xa2, 0x02, 0x0a, 0x11, + 0x67, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xc8, 0x02, 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, 0x36, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x41, 0x72, 0x72, 0x18, @@ -1415,8 +1423,11 @@ var file_room_downsync_frame_proto_rawDesc = []byte{ 0x73, 0x6b, 0x12, 0x2c, 0x0a, 0x11, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x79, 0x6e, 0x63, - 0x42, 0x13, 0x5a, 0x11, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x73, 0x72, 0x76, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x65, 0x73, 0x49, 0x64, 0x4c, 0x69, 0x73, + 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x65, 0x73, + 0x49, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x13, 0x5a, 0x11, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, + 0x5f, 0x73, 0x72, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/battle_srv/ws/serve.go b/battle_srv/ws/serve.go index 1c5c03f..f1db6d8 100644 --- a/battle_srv/ws/serve.go +++ b/battle_srv/ws/serve.go @@ -240,24 +240,23 @@ func Serve(c *gin.Context) { // Construct "battleColliderInfo" to downsync bciFrame := &pb.BattleColliderInfo{ - BoundRoomId: pRoom.Id, - StageName: pRoom.StageName, + BoundRoomId: pRoom.Id, + StageName: pRoom.StageName, - IntervalToPing: int32(Constants.Ws.IntervalToPing), - WillKickIfInactiveFor: int32(Constants.Ws.WillKickIfInactiveFor), - BattleDurationNanos: pRoom.BattleDurationNanos, + IntervalToPing: int32(Constants.Ws.IntervalToPing), + WillKickIfInactiveFor: int32(Constants.Ws.WillKickIfInactiveFor), + BattleDurationNanos: pRoom.BattleDurationNanos, InputFrameUpsyncDelayTolerance: pRoom.InputFrameUpsyncDelayTolerance, MaxChasingRenderFramesPerUpdate: pRoom.MaxChasingRenderFramesPerUpdate, - PlayerBattleState: pThePlayer.BattleState, // For frontend to know whether it's rejoining RollbackEstimatedDtMillis: pRoom.RollbackEstimatedDtMillis, RollbackEstimatedDtNanos: pRoom.RollbackEstimatedDtNanos, - InputDelayFrames: pRoom.InputDelayFrames, - InputScaleFrames: pRoom.InputScaleFrames, + SpaceOffsetX: pRoom.SpaceOffsetX, + SpaceOffsetY: pRoom.SpaceOffsetY, - RenderCacheSize: pRoom.RenderCacheSize, - CollisionMinStep: pRoom.CollisionMinStep, + RenderCacheSize: pRoom.RenderCacheSize, + CollisionMinStep: pRoom.CollisionMinStep, FrameDataLoggingEnabled: pRoom.FrameDataLoggingEnabled, } diff --git a/frontend/assets/plugin_scripts/jsexport.js b/frontend/assets/plugin_scripts/jsexport.js index c0aab2b..2c5ffa7 100644 --- a/frontend/assets/plugin_scripts/jsexport.js +++ b/frontend/assets/plugin_scripts/jsexport.js @@ -4968,7 +4968,7 @@ $packages["jsexport/battle"] = (function() { this.Eles = Eles_; }); SkillMapperType = $pkg.SkillMapperType = $newType(4, $kindFunc, "battle.SkillMapperType", true, "jsexport/battle", true, null); - CharacterConfig = $pkg.CharacterConfig = $newType(0, $kindStruct, "battle.CharacterConfig", true, "jsexport/battle", true, function(SpeciesId_, SpeciesName_, InAirIdleFrameIdxTurningPoint_, InAirIdleFrameIdxTurnedCycle_, LayDownFrames_, LayDownFramesToRecover_, GetUpFrames_, GetUpFramesToRecover_, JumpingInitVelY_, SkillMapper_) { + CharacterConfig = $pkg.CharacterConfig = $newType(0, $kindStruct, "battle.CharacterConfig", true, "jsexport/battle", true, function(SpeciesId_, SpeciesName_, InAirIdleFrameIdxTurningPoint_, InAirIdleFrameIdxTurnedCycle_, LayDownFrames_, LayDownFramesToRecover_, GetUpFrames_, GetUpFramesToRecover_, Speed_, JumpingInitVelY_, SkillMapper_) { this.$val = this; if (arguments.length === 0) { this.SpeciesId = 0; @@ -4979,6 +4979,7 @@ $packages["jsexport/battle"] = (function() { this.LayDownFramesToRecover = 0; this.GetUpFrames = 0; this.GetUpFramesToRecover = 0; + this.Speed = 0; this.JumpingInitVelY = 0; this.SkillMapper = $throwNilPointerError; return; @@ -4991,6 +4992,7 @@ $packages["jsexport/battle"] = (function() { this.LayDownFramesToRecover = LayDownFramesToRecover_; this.GetUpFrames = GetUpFrames_; this.GetUpFramesToRecover = GetUpFramesToRecover_; + this.Speed = Speed_; this.JumpingInitVelY = JumpingInitVelY_; this.SkillMapper = SkillMapper_; }); @@ -6006,7 +6008,7 @@ $packages["jsexport/battle"] = (function() { InputFrameDownsync.init("", [{prop: "InputFrameId", name: "InputFrameId", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "InputList", name: "InputList", embedded: false, exported: true, typ: sliceType$5, tag: ""}, {prop: "ConfirmedList", name: "ConfirmedList", embedded: false, exported: true, typ: $Uint64, tag: ""}]); RingBuffer.init("", [{prop: "Ed", name: "Ed", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "St", name: "St", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "EdFrameId", name: "EdFrameId", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "StFrameId", name: "StFrameId", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "N", name: "N", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "Cnt", name: "Cnt", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "Eles", name: "Eles", embedded: false, exported: true, typ: sliceType$2, tag: ""}]); SkillMapperType.init([$Int, ptrType$5], [$Int], false); - CharacterConfig.init("", [{prop: "SpeciesId", name: "SpeciesId", embedded: false, exported: true, typ: $Int, tag: ""}, {prop: "SpeciesName", name: "SpeciesName", embedded: false, exported: true, typ: $String, tag: ""}, {prop: "InAirIdleFrameIdxTurningPoint", name: "InAirIdleFrameIdxTurningPoint", embedded: false, exported: true, typ: $Int, tag: ""}, {prop: "InAirIdleFrameIdxTurnedCycle", name: "InAirIdleFrameIdxTurnedCycle", embedded: false, exported: true, typ: $Int, tag: ""}, {prop: "LayDownFrames", name: "LayDownFrames", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "LayDownFramesToRecover", name: "LayDownFramesToRecover", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "GetUpFrames", name: "GetUpFrames", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "GetUpFramesToRecover", name: "GetUpFramesToRecover", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "JumpingInitVelY", name: "JumpingInitVelY", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "SkillMapper", name: "SkillMapper", embedded: false, exported: true, typ: SkillMapperType, tag: ""}]); + CharacterConfig.init("", [{prop: "SpeciesId", name: "SpeciesId", embedded: false, exported: true, typ: $Int, tag: ""}, {prop: "SpeciesName", name: "SpeciesName", embedded: false, exported: true, typ: $String, tag: ""}, {prop: "InAirIdleFrameIdxTurningPoint", name: "InAirIdleFrameIdxTurningPoint", embedded: false, exported: true, typ: $Int, tag: ""}, {prop: "InAirIdleFrameIdxTurnedCycle", name: "InAirIdleFrameIdxTurnedCycle", embedded: false, exported: true, typ: $Int, tag: ""}, {prop: "LayDownFrames", name: "LayDownFrames", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "LayDownFramesToRecover", name: "LayDownFramesToRecover", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "GetUpFrames", name: "GetUpFrames", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "GetUpFramesToRecover", name: "GetUpFramesToRecover", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "Speed", name: "Speed", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "JumpingInitVelY", name: "JumpingInitVelY", embedded: false, exported: true, typ: $Int32, tag: ""}, {prop: "SkillMapper", name: "SkillMapper", embedded: false, exported: true, typ: SkillMapperType, tag: ""}]); SatResult.init("", [{prop: "Overlap", name: "Overlap", embedded: false, exported: true, typ: $Float64, tag: ""}, {prop: "OverlapX", name: "OverlapX", embedded: false, exported: true, typ: $Float64, tag: ""}, {prop: "OverlapY", name: "OverlapY", embedded: false, exported: true, typ: $Float64, tag: ""}, {prop: "AContainedInB", name: "AContainedInB", embedded: false, exported: true, typ: $Bool, tag: ""}, {prop: "BContainedInA", name: "BContainedInA", embedded: false, exported: true, typ: $Bool, tag: ""}, {prop: "Axis", name: "Axis", embedded: false, exported: true, typ: resolv.Vector, tag: ""}]); $init = function() { $pkg.$init = function() {}; @@ -6015,7 +6017,7 @@ $packages["jsexport/battle"] = (function() { $r = resolv.$init(); /* */ $s = 2; case 2: if($c) { $c = false; $r = $r.$blk(); } if ($r && $r.$blk !== undefined) { break s; } $pkg.DIRECTION_DECODER = new sliceType$1([new sliceType([0, 0]), new sliceType([0, 2]), new sliceType([0, -2]), new sliceType([2, 0]), new sliceType([-2, 0]), new sliceType([1, 1]), new sliceType([-1, -1]), new sliceType([1, -1]), new sliceType([-1, 1])]); skills = $makeMap($Int.keyFor, [{ k: 1, v: new Skill.ptr(0, 30, 30, 30, 1, 2, new sliceType$2([new MeleeBullet.ptr(new Bullet.ptr(0, 0, 7, 13, 30, 22, 13, 9, 50, 0, 5, 0, 0, 1200, 0, 2400, 3200, false, $makeMap($Int.keyFor, [{ k: 1, v: 2 }])))])) }, { k: 2, v: new Skill.ptr(0, 36, 36, 36, 1, 11, new sliceType$2([new MeleeBullet.ptr(new Bullet.ptr(0, 0, 18, 22, 36, 18, 18, 9, 50, 0, 5, 0, 0, 1800, 0, 2400, 3200, false, $makeMap($Int.keyFor, [{ k: 1, v: 3 }])))])) }, { k: 3, v: new Skill.ptr(0, 60, 60, 60, 1, 12, new sliceType$2([new MeleeBullet.ptr(new Bullet.ptr(0, 0, 15, 0, 0, 40, 999999999, 9, 200, 700, 10, 0, 0, 2400, 0, 3200, 3200, true, false))])) }, { k: 4, v: new Skill.ptr(0, 30, 30, 30, 1, 2, new sliceType$2([new MeleeBullet.ptr(new Bullet.ptr(0, 0, 7, 13, 30, 22, 13, 9, 50, 0, 5, 0, 0, 1200, 0, 2400, 3200, false, $makeMap($Int.keyFor, [{ k: 1, v: 5 }])))])) }, { k: 5, v: new Skill.ptr(0, 36, 36, 36, 1, 11, new sliceType$2([new MeleeBullet.ptr(new Bullet.ptr(0, 0, 18, 22, 36, 18, 18, 9, 50, 0, 5, 0, 0, 1800, 0, 2400, 3200, false, $makeMap($Int.keyFor, [{ k: 1, v: 6 }])))])) }, { k: 6, v: new Skill.ptr(0, 60, 60, 60, 1, 12, new sliceType$2([new MeleeBullet.ptr(new Bullet.ptr(0, 0, 15, 0, 0, 40, 999999999, 9, 200, 700, 10, 0, 0, 2400, 0, 3200, 3200, true, false))])) }, { k: 255, v: new Skill.ptr(0, 34, 34, 34, 1, 6, new sliceType$2([new MeleeBullet.ptr(new Bullet.ptr(0, 0, 3, 0, 0, 20, 18, 9, 50, 0, 5, 0, 0, 1200, 0, 3200, 2400, false, false))])) }, { k: 256, v: new Skill.ptr(0, 34, 34, 34, 1, 6, new sliceType$2([new MeleeBullet.ptr(new Bullet.ptr(0, 0, 3, 0, 0, 20, 18, 9, 50, 0, 5, 0, 0, 1200, 0, 3200, 2400, false, false))])) }]); - $pkg.Characters = $makeMap($Int.keyFor, [{ k: 0, v: new CharacterConfig.ptr(0, "MonkGirl", 11, 1, 16, 16, 33, 30, 800, (function(patternId, currPlayerDownsync) { + $pkg.Characters = $makeMap($Int.keyFor, [{ k: 0, v: new CharacterConfig.ptr(0, "MonkGirl", 11, 1, 16, 16, 33, 30, 120, 800, (function(patternId, currPlayerDownsync) { var _entry, _entry$1, _ref, _tuple, _tuple$1, currPlayerDownsync, existent1, existent2, nextSkillId, patternId, skillConfig, v, x, x$1; if (1 === patternId) { if (0 === currPlayerDownsync.FramesToRecover) { @@ -6045,7 +6047,7 @@ $packages["jsexport/battle"] = (function() { } } return -1; - })) }, { k: 1, v: new CharacterConfig.ptr(1, "KnifeGirl", 9, 1, 16, 16, 30, 27, 750, (function(patternId, currPlayerDownsync) { + })) }, { k: 1, v: new CharacterConfig.ptr(1, "KnifeGirl", 9, 1, 16, 16, 30, 27, 140, 750, (function(patternId, currPlayerDownsync) { var _entry, _entry$1, _ref, _tuple, _tuple$1, currPlayerDownsync, existent1, existent2, nextSkillId, patternId, skillConfig, v, x, x$1; if (1 === patternId) { if (0 === currPlayerDownsync.FramesToRecover) { diff --git a/frontend/assets/resources/map/dungeon/map.tmx b/frontend/assets/resources/map/dungeon/map.tmx index 6e24f2a..3639788 100644 --- a/frontend/assets/resources/map/dungeon/map.tmx +++ b/frontend/assets/resources/map/dungeon/map.tmx @@ -9,10 +9,10 @@ - + - + diff --git a/frontend/assets/resources/pbfiles/room_downsync_frame.proto b/frontend/assets/resources/pbfiles/room_downsync_frame.proto index b22bc48..7551b66 100644 --- a/frontend/assets/resources/pbfiles/room_downsync_frame.proto +++ b/frontend/assets/resources/pbfiles/room_downsync_frame.proto @@ -139,4 +139,5 @@ message RoomDownsyncFrame { repeated MeleeBullet meleeBullets = 4; // I don't know how to mimic inheritance/composition in protobuf by far, thus using an array for each type of bullet as a compromise uint64 backendUnconfirmedMask = 5; // Indexed by "joinIndex", same compression concern as stated in InputFrameDownsync bool shouldForceResync = 6; + repeated int32 speciesIdList = 7; } diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index d408e2a..903c81d 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 216.50135522089343, + 216.79917701871616, 0, 0, 0, diff --git a/frontend/assets/scenes/offline_map.fire b/frontend/assets/scenes/offline_map.fire index de9f2b1..33d98c7 100644 --- a/frontend/assets/scenes/offline_map.fire +++ b/frontend/assets/scenes/offline_map.fire @@ -461,7 +461,7 @@ "array": [ 0, 0, - 210.4441731196186, + 209.83528025849938, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 3d29d8f..63a8d52 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -279,7 +279,7 @@ cc.Class({ self.collisionPlayerIndexPrefix = (1 << 17); // For tracking the movements of players if (null != self.playerRichInfoDict) { self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { - if (playerRichInfo.node.parent) { + if (playerRichInfo.node && playerRichInfo.node.parent) { playerRichInfo.node.parent.removeChild(playerRichInfo.node); } }); 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 ee15c16..3546a90 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 @@ -5161,6 +5161,7 @@ $root.protos = (function() { * @property {Array.|null} [meleeBullets] RoomDownsyncFrame meleeBullets * @property {number|Long|null} [backendUnconfirmedMask] RoomDownsyncFrame backendUnconfirmedMask * @property {boolean|null} [shouldForceResync] RoomDownsyncFrame shouldForceResync + * @property {Array.|null} [speciesIdList] RoomDownsyncFrame speciesIdList */ /** @@ -5174,6 +5175,7 @@ $root.protos = (function() { function RoomDownsyncFrame(properties) { this.playersArr = []; this.meleeBullets = []; + this.speciesIdList = []; if (properties) for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -5228,6 +5230,14 @@ $root.protos = (function() { */ RoomDownsyncFrame.prototype.shouldForceResync = false; + /** + * RoomDownsyncFrame speciesIdList. + * @member {Array.} speciesIdList + * @memberof protos.RoomDownsyncFrame + * @instance + */ + RoomDownsyncFrame.prototype.speciesIdList = $util.emptyArray; + /** * Creates a new RoomDownsyncFrame instance using the specified properties. * @function create @@ -5266,6 +5276,12 @@ $root.protos = (function() { writer.uint32(/* id 5, wireType 0 =*/40).uint64(message.backendUnconfirmedMask); if (message.shouldForceResync != null && Object.hasOwnProperty.call(message, "shouldForceResync")) writer.uint32(/* id 6, wireType 0 =*/48).bool(message.shouldForceResync); + if (message.speciesIdList != null && message.speciesIdList.length) { + writer.uint32(/* id 7, wireType 2 =*/58).fork(); + for (var i = 0; i < message.speciesIdList.length; ++i) + writer.int32(message.speciesIdList[i]); + writer.ldelim(); + } return writer; }; @@ -5328,6 +5344,17 @@ $root.protos = (function() { message.shouldForceResync = reader.bool(); break; } + case 7: { + if (!(message.speciesIdList && message.speciesIdList.length)) + message.speciesIdList = []; + if ((tag & 7) === 2) { + var end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) + message.speciesIdList.push(reader.int32()); + } else + message.speciesIdList.push(reader.int32()); + break; + } default: reader.skipType(tag & 7); break; @@ -5393,6 +5420,13 @@ $root.protos = (function() { if (message.shouldForceResync != null && message.hasOwnProperty("shouldForceResync")) if (typeof message.shouldForceResync !== "boolean") return "shouldForceResync: boolean expected"; + if (message.speciesIdList != null && message.hasOwnProperty("speciesIdList")) { + if (!Array.isArray(message.speciesIdList)) + return "speciesIdList: array expected"; + for (var i = 0; i < message.speciesIdList.length; ++i) + if (!$util.isInteger(message.speciesIdList[i])) + return "speciesIdList: integer[] expected"; + } return null; }; @@ -5450,6 +5484,13 @@ $root.protos = (function() { message.backendUnconfirmedMask = new $util.LongBits(object.backendUnconfirmedMask.low >>> 0, object.backendUnconfirmedMask.high >>> 0).toNumber(true); if (object.shouldForceResync != null) message.shouldForceResync = Boolean(object.shouldForceResync); + if (object.speciesIdList) { + if (!Array.isArray(object.speciesIdList)) + throw TypeError(".protos.RoomDownsyncFrame.speciesIdList: array expected"); + message.speciesIdList = []; + for (var i = 0; i < object.speciesIdList.length; ++i) + message.speciesIdList[i] = object.speciesIdList[i] | 0; + } return message; }; @@ -5469,6 +5510,7 @@ $root.protos = (function() { if (options.arrays || options.defaults) { object.playersArr = []; object.meleeBullets = []; + object.speciesIdList = []; } if (options.defaults) { object.id = 0; @@ -5508,6 +5550,11 @@ $root.protos = (function() { object.backendUnconfirmedMask = options.longs === String ? $util.Long.prototype.toString.call(message.backendUnconfirmedMask) : options.longs === Number ? new $util.LongBits(message.backendUnconfirmedMask.low >>> 0, message.backendUnconfirmedMask.high >>> 0).toNumber(true) : message.backendUnconfirmedMask; if (message.shouldForceResync != null && message.hasOwnProperty("shouldForceResync")) object.shouldForceResync = message.shouldForceResync; + if (message.speciesIdList && message.speciesIdList.length) { + object.speciesIdList = []; + for (var j = 0; j < message.speciesIdList.length; ++j) + object.speciesIdList[j] = message.speciesIdList[j]; + } return object; }; diff --git a/jsexport/battle/battle.go b/jsexport/battle/battle.go index 0385853..34e2ad0 100644 --- a/jsexport/battle/battle.go +++ b/jsexport/battle/battle.go @@ -25,6 +25,8 @@ const ( INPUT_SCALE_FRAMES = uint32(2) // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames) NST_DELAY_FRAMES = int32(16) // 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" + SP_ATK_LOOKUP_FRAMES = int32(5) + SNAP_INTO_PLATFORM_OVERLAP = float64(0.1) SNAP_INTO_PLATFORM_THRESHOLD = float64(0.5) @@ -96,7 +98,7 @@ func ShouldPrefabInputFrameDownsync(prevRenderFrameId int32, renderFrameId int32 } func ShouldGenerateInputFrameUpsync(renderFrameId int32) bool { - return ((renderFrameId & ((1 << INPUT_SCALE_FRAMES) - 1)) == 0) + return ((renderFrameId & ((1 << INPUT_SCALE_FRAMES) - 1)) == 0) } func ConvertToDelayedInputFrameId(renderFrameId int32) int32 { @@ -371,7 +373,7 @@ func calcHardPushbacksNorms(joinIndex int32, playerCollider *resolv.Object, play func deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync, currRenderFrame *RoomDownsyncFrame, inputsBuffer *RingBuffer) (int, bool, int32, int32) { // returns (patternId, jumpedOrNot, effectiveDx, effectiveDy) delayedInputFrameId := ConvertToDelayedInputFrameId(currRenderFrame.Id) - delayedInputFrameIdForPrevRdf := ConvertToDelayedInputFrameId(currRenderFrame.Id-1) + delayedInputFrameIdForPrevRdf := ConvertToDelayedInputFrameId(currRenderFrame.Id - 1) if 0 >= delayedInputFrameId { return PATTERN_ID_UNABLE_TO_OP, false, 0, 0 diff --git a/jsexport/battle/characterConfig.go b/jsexport/battle/characterConfig.go index 165882a..8e8feaa 100644 --- a/jsexport/battle/characterConfig.go +++ b/jsexport/battle/characterConfig.go @@ -15,6 +15,7 @@ type CharacterConfig struct { GetUpFrames int32 GetUpFramesToRecover int32 + Speed int32 JumpingInitVelY int32 SkillMapper SkillMapperType @@ -34,6 +35,7 @@ var Characters = map[int]*CharacterConfig{ GetUpFrames: int32(33), GetUpFramesToRecover: int32(30), // 3 invinsible frames for just-blown-up character to make a comeback + Speed: int32(float64(1.2) * WORLD_TO_VIRTUAL_GRID_RATIO), JumpingInitVelY: int32(float64(8) * WORLD_TO_VIRTUAL_GRID_RATIO), SkillMapper: func(patternId int, currPlayerDownsync *PlayerDownsync) int { @@ -76,6 +78,7 @@ var Characters = map[int]*CharacterConfig{ GetUpFrames: int32(30), GetUpFramesToRecover: int32(27), // 3 invinsible frames for just-blown-up character to make a comeback + Speed: int32(float64(1.4) * WORLD_TO_VIRTUAL_GRID_RATIO), JumpingInitVelY: int32(float64(7.5) * WORLD_TO_VIRTUAL_GRID_RATIO), SkillMapper: func(patternId int, currPlayerDownsync *PlayerDownsync) int { diff --git a/jsexport/main.go b/jsexport/main.go index 6286ea6..275f902 100644 --- a/jsexport/main.go +++ b/jsexport/main.go @@ -53,8 +53,8 @@ func NewPlayerDownsyncJs(id, virtualGridX, virtualGridY, dirX, dirY, velX, velY, VelY: velY, FramesToRecover: framesToRecover, FramesInChState: framesInChState, - ActiveSkillId: activeSkillId, - ActiveSkillHit: activeSkillHit, + ActiveSkillId: activeSkillId, + ActiveSkillHit: activeSkillHit, Speed: speed, BattleState: battleState, CharacterState: characterState, @@ -169,10 +169,10 @@ func main() { "VirtualGridToWorldPos": VirtualGridToWorldPos, "GetCharacterConfigsOrderedByJoinIndex": GetCharacterConfigsOrderedByJoinIndex, "ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs": ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs, - "ConvertToDelayedInputFrameId": ConvertToDelayedInputFrameId, - "ConvertToNoDelayInputFrameId": ConvertToNoDelayInputFrameId, - "ConvertToFirstUsedRenderFrameId": ConvertToFirstUsedRenderFrameId, - "ConvertToLastUsedRenderFrameId": ConvertToLastUsedRenderFrameId, - "ShouldGenerateInputFrameUpsync": ShouldGenerateInputFrameUpsync, + "ConvertToDelayedInputFrameId": ConvertToDelayedInputFrameId, + "ConvertToNoDelayInputFrameId": ConvertToNoDelayInputFrameId, + "ConvertToFirstUsedRenderFrameId": ConvertToFirstUsedRenderFrameId, + "ConvertToLastUsedRenderFrameId": ConvertToLastUsedRenderFrameId, + "ShouldGenerateInputFrameUpsync": ShouldGenerateInputFrameUpsync, }) }