Temp broken commit, refactoring battle_srv to use jsexport.

This commit is contained in:
genxium 2022-12-25 15:39:30 +08:00
parent 013c1ea312
commit 9ffcc6fbd8
19 changed files with 2163 additions and 1348 deletions

View File

@ -5,7 +5,7 @@ import (
. "battle_srv/common"
"battle_srv/common/utils"
"battle_srv/models"
. "jsexport/protos"
. "battle_srv/protos"
"battle_srv/storage"
"bytes"
"crypto/sha256"

View File

@ -4,7 +4,7 @@ import (
. "battle_srv/common"
"battle_srv/common/utils"
"battle_srv/models"
. "jsexport/protos"
. "battle_srv/protos"
"battle_srv/storage"
. "dnmshared"
sq "github.com/Masterminds/squirrel"

View File

@ -4,7 +4,7 @@ import (
. "battle_srv/common"
"battle_srv/common/utils"
"battle_srv/models"
. "jsexport/protos"
. "battle_srv/protos"
"battle_srv/storage"
. "dnmshared"

View File

@ -1,17 +1,52 @@
package models
import (
. "jsexport/protos"
pb "battle_srv/protos"
"jsexport/battle"
)
func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) map[int32]*PlayerDownsync {
toRet := make(map[int32]*PlayerDownsync, 0)
func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) map[int32]*pb.PlayerDownsync {
toRet := make(map[int32]*pb.PlayerDownsync, 0)
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
toRet[k] = &PlayerDownsync{
toRet[k] = &pb.PlayerDownsync{
Id: last.Id,
VirtualGridX: last.VirtualGridX,
VirtualGridY: last.VirtualGridY,
DirX: last.DirX,
DirY: last.DirY,
VelX: last.VelX,
VelY: last.VelY,
Speed: last.Speed,
BattleState: last.BattleState,
CharacterState: last.CharacterState,
InAir: last.InAir,
JoinIndex: last.JoinIndex,
ColliderRadius: last.ColliderRadius,
Score: last.Score,
Removed: last.Removed,
}
if withMetaInfo {
toRet[k].Name = last.Name
toRet[k].DisplayName = last.DisplayName
toRet[k].Avatar = last.Avatar
}
}
return toRet
}
func toJsPlayers(modelInstances map[int32]*Player, withMetaInfo bool) map[int32]*battle.PlayerDownsync {
toRet := make(map[int32]*battle.PlayerDownsync, 0)
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
toRet[k] = &battle.PlayerDownsync{
Id: last.Id,
VirtualGridX: last.VirtualGridX,
VirtualGridY: last.VirtualGridY,

View File

@ -1,7 +1,7 @@
package models
import (
. "jsexport/protos"
. "battle_srv/protos"
"battle_srv/storage"
. "dnmshared"
"fmt"

View File

@ -3,10 +3,8 @@ package models
import (
. "battle_srv/common"
"battle_srv/common/utils"
. "jsexport/protos"
. "jsexport/models"
pb "battle_srv/protos"
. "dnmshared"
. "dnmshared/sharedprotos"
"encoding/xml"
"fmt"
"github.com/golang/protobuf/proto"
@ -14,6 +12,7 @@ import (
"github.com/solarlune/resolv"
"go.uber.org/zap"
"io/ioutil"
"jsexport/battle"
"math/rand"
"os"
"path/filepath"
@ -116,7 +115,7 @@ type Room struct {
* Moreover, during the invocation of `PlayerSignalToCloseDict`, the `Player` instance is supposed to be deallocated (though not synchronously).
*/
PlayerDownsyncSessionDict map[int32]*websocket.Conn
PlayerDownsyncChanDict map[int32](chan InputsBufferSnapshot)
PlayerDownsyncChanDict map[int32](chan pb.InputsBufferSnapshot)
PlayerActiveWatchdogDict map[int32](*Watchdog)
PlayerSignalToCloseDict map[int32]SignalToCloseConnCbType
Score float32
@ -126,7 +125,6 @@ type Room struct {
CurDynamicsRenderFrameId int32 // [WARNING] The dynamics of backend is ALWAYS MOVING FORWARD BY ALL-CONFIRMED INPUTFRAMES (either by upsync or forced), i.e. no rollback; Moreover when "true == BackendDynamicsEnabled" we always have "Room.CurDynamicsRenderFrameId >= Room.RenderFrameId" because each "all-confirmed inputFrame" is applied on "all applicable renderFrames" in one-go hence often sees a future "renderFrame" earlier
EffectivePlayerCount int32
DismissalWaitGroup sync.WaitGroup
Barriers map[int32]*Barrier
InputsBuffer *RingBuffer // Indices are STRICTLY consecutive
InputsBufferLock sync.Mutex // Guards [InputsBuffer, LatestPlayerUpsyncedInputFrameId, LastAllConfirmedInputFrameId, LastAllConfirmedInputList, LastAllConfirmedInputFrameIdWithChange]
RenderFrameBuffer *RingBuffer // Indices are STRICTLY consecutive
@ -144,7 +142,10 @@ type Room struct {
BulletBattleLocalIdCounter int32
dilutedRollbackEstimatedDtNanos int64
BattleColliderInfo // Compositing to send centralized magic numbers
pb.BattleColliderInfo // Compositing to send centralized magic numbers
TmxPointsMap StrToVec2DListMap
TmxPolygonsMap StrToPolygon2DListMap
}
func (pR *Room) updateScore() {
@ -287,24 +288,8 @@ func (pR *Room) ChooseStage() error {
pR.StageDiscreteH = stageDiscreteH
pR.StageTileW = stageTileW
pR.StageTileH = stageTileH
pR.StrToVec2DListMap = strToVec2DListMap
pR.StrToPolygon2DListMap = strToPolygon2DListMap
barrierPolygon2DList := *(strToPolygon2DListMap["Barrier"])
var barrierLocalIdInBattle int32 = 0
for _, polygon2DUnaligned := range barrierPolygon2DList.Eles {
polygon2D := AlignPolygon2DToBoundingBox(polygon2DUnaligned)
/*
// 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))
*/
pR.Barriers[barrierLocalIdInBattle] = &Barrier{
Boundary: polygon2D,
}
barrierLocalIdInBattle++
}
pR.TmxPointsMap = strToVec2DListMap
pR.TmxPolygonsMap = strToPolygon2DListMap
return nil
}
@ -345,7 +330,7 @@ func (pR *Room) InputsBufferString(allDetails bool) string {
if nil == tmp {
break
}
f := tmp.(*InputFrameDownsync)
f := tmp.(*pb.InputFrameDownsync)
s = append(s, fmt.Sprintf("{\"inputFrameId\":%d,\"inputList\":%v,\"confirmedList\":\"%d\"}", f.InputFrameId, f.InputList, f.ConfirmedList))
}
@ -365,12 +350,12 @@ func (pR *Room) StartBattle() {
// Initialize the "collisionSys" as well as "RenderFrameBuffer"
pR.CurDynamicsRenderFrameId = 0
kickoffFrame := &RoomDownsyncFrame{
kickoffFrameJs := &battle.RoomDownsyncFrame{
Id: pR.RenderFrameId,
Players: toPbPlayers(pR.Players, false),
Players: toJsPlayers(pR.Players, false),
CountdownNanos: pR.BattleDurationNanos,
}
pR.RenderFrameBuffer.Put(kickoffFrame)
pR.RenderFrameBuffer.Put(kickoffFrameJs)
// Refresh "Colliders"
spaceW := pR.StageDiscreteW * pR.StageTileW
@ -435,7 +420,11 @@ func (pR *Room) StartBattle() {
case PlayerBattleStateIns.DISCONNECTED, PlayerBattleStateIns.LOST, PlayerBattleStateIns.EXPELLED_DURING_GAME, PlayerBattleStateIns.EXPELLED_IN_DISMISSAL:
continue
}
kickoffFrame := pR.RenderFrameBuffer.GetByFrameId(0).(*RoomDownsyncFrame)
kickoffFrame := &pb.RoomDownsyncFrame{
Id: pR.RenderFrameId,
Players: toPbPlayers(pR.Players, false),
CountdownNanos: pR.BattleDurationNanos,
}
pR.sendSafely(kickoffFrame, nil, DOWNSYNC_MSG_ACT_BATTLE_START, playerId, true)
}
Logger.Info(fmt.Sprintf("In `battleMainLoop` for roomId=%v sent out kickoffFrame", pR.Id))
@ -463,7 +452,7 @@ func (pR *Room) StartBattle() {
}
}
downsyncLoop := func(playerId int32, player *Player, playerDownsyncChan chan InputsBufferSnapshot) {
downsyncLoop := func(playerId int32, player *Player, playerDownsyncChan chan pb.InputsBufferSnapshot) {
defer func() {
if r := recover(); r != nil {
Logger.Error("downsyncLoop, recovery spot#1, recovered from: ", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("panic", r))
@ -495,7 +484,7 @@ func (pR *Room) StartBattle() {
Each "playerDownsyncChan" stays alive through out the lifecycle of room instead of each "playerDownsyncSession", i.e. not closed or dereferenced upon disconnection.
*/
pR.PlayerDownsyncChanDict[playerId] = make(chan InputsBufferSnapshot, pR.InputsBuffer.N)
pR.PlayerDownsyncChanDict[playerId] = make(chan pb.InputsBufferSnapshot, pR.InputsBuffer.N)
go downsyncLoop(playerId, player, pR.PlayerDownsyncChanDict[playerId])
}
@ -509,7 +498,7 @@ func (pR *Room) toDiscreteInputsBufferIndex(inputFrameId int32, joinIndex int32)
return (inputFrameId << 2) + joinIndex // allowing joinIndex upto 15
}
func (pR *Room) OnBattleCmdReceived(pReq *WsReq) {
func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) {
/*
[WARNING] This function "OnBattleCmdReceived" could be called by different ws sessions and thus from different threads!
@ -560,7 +549,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *WsReq) {
}
}
func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *InputFrameDownsync, playerId int32) {
func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFrameDownsync, playerId int32) {
// [WARNING] This function MUST BE called while "pR.InputsBufferLock" is locked!
inputFrameId := inputFrameDownsync.InputFrameId
if -1 == pR.LastAllConfirmedInputFrameIdWithChange || false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) {
@ -603,7 +592,7 @@ func (pR *Room) StopBattleForSettlement() {
Logger.Info("Stopping the `battleMainLoop` for:", zap.Any("roomId", pR.Id))
pR.RenderFrameId++
for playerId, _ := range pR.Players {
assembledFrame := RoomDownsyncFrame{
assembledFrame := pb.RoomDownsyncFrame{
Id: pR.RenderFrameId,
Players: toPbPlayers(pR.Players, false),
CountdownNanos: -1, // TODO: Replace this magic constant!
@ -629,7 +618,7 @@ func (pR *Room) onBattlePrepare(cb BattleStartCbType) {
pR.State = RoomBattleStateIns.PREPARE
Logger.Info("Battle state transitted to RoomBattleStateIns.PREPARE for:", zap.Any("roomId", pR.Id))
battleReadyToStartFrame := &RoomDownsyncFrame{
battleReadyToStartFrame := &pb.RoomDownsyncFrame{
Id: DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START,
Players: toPbPlayers(pR.Players, true),
CountdownNanos: pR.BattleDurationNanos,
@ -714,10 +703,9 @@ func (pR *Room) OnDismissed() {
for _, oldChan := range pR.PlayerDownsyncChanDict {
close(oldChan)
}
pR.PlayerDownsyncChanDict = make(map[int32](chan InputsBufferSnapshot))
pR.PlayerDownsyncChanDict = make(map[int32](chan pb.InputsBufferSnapshot))
pR.PlayerSignalToCloseDict = make(map[int32]SignalToCloseConnCbType)
pR.JoinIndexBooleanArr = make([]bool, pR.Capacity)
pR.Barriers = make(map[int32]*Barrier)
pR.RenderCacheSize = 1024
pR.RenderFrameBuffer = NewRingBuffer(pR.RenderCacheSize)
pR.InputsBuffer = NewRingBuffer((pR.RenderCacheSize >> 1) + 1)
@ -745,23 +733,15 @@ func (pR *Room) OnDismissed() {
pR.BackendDynamicsEnabled = true // [WARNING] When "false", recovery upon reconnection wouldn't work!
pR.ForceAllResyncOnAnyActiveSlowTicker = true // See tradeoff discussion in "downsyncToAllPlayers"
punchSkillId := int32(1)
pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0)
pR.MeleeSkillConfig[punchSkillId] = &MeleeBullet{
pR.MeleeSkillConfig = make(map[int32]*pb.MeleeBullet, 0)
pR.MeleeSkillConfig[punchSkillId] = &pb.MeleeBullet{
// for offender
StartupFrames: int32(10),
ActiveFrames: int32(10),
RecoveryFrames: int32(34),
RecoveryFramesOnBlock: int32(34),
RecoveryFramesOnHit: int32(34),
Moveforward: &Vec2D{
X: 0,
Y: 0,
},
HitboxOffset: float64(12.0), // should be about the radius of the PlayerCollider
HitboxSize: &Vec2D{
X: float64(24.0),
Y: float64(32.0),
},
HitboxOffset: float64(12.0), // should be about the radius of the PlayerCollider
// for defender
HitStunFrames: int32(18),
@ -769,6 +749,11 @@ func (pR *Room) OnDismissed() {
Pushback: float64(8.0),
ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge
Damage: int32(5),
SelfMoveforwardX: 0,
SelfMoveforwardY: 0,
HitboxSizeX: 24.0,
HitboxSizeY: 32.0,
}
pR.SnapIntoPlatformOverlap = float64(0.1)
@ -893,16 +878,16 @@ func (pR *Room) onPlayerAdded(playerId int32) {
pR.JoinIndexBooleanArr[index] = true
// Lazily assign the initial position of "Player" for "RoomDownsyncFrame".
playerPosList := *(pR.StrToVec2DListMap["PlayerStartingPos"])
if index > len(playerPosList.Eles) {
playerPosList := pR.TmxPointsMap["PlayerStartingPos"]
if index > len(playerPosList) {
panic(fmt.Sprintf("onPlayerAdded error, index >= len(playerPosList), roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
}
playerPos := playerPosList.Eles[index]
playerPos := playerPosList[index]
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 = WorldToVirtualGridPos(playerPos.X, playerPos.Y, pR.WorldToVirtualGridRatio)
pR.Players[playerId].VirtualGridX, pR.Players[playerId].VirtualGridY = battle.WorldToVirtualGridPos(playerPos.X, playerPos.Y, pR.WorldToVirtualGridRatio)
// Hardcoded initial character orientation/facing
if 0 == (pR.Players[playerId].JoinIndex % 2) {
pR.Players[playerId].DirX = -2
@ -942,7 +927,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool {
targetPlayerBattleState := atomic.LoadInt32(&(targetPlayer.BattleState))
switch targetPlayerBattleState {
case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK:
playerAckedFrame := &RoomDownsyncFrame{
playerAckedFrame := &pb.RoomDownsyncFrame{
Id: pR.RenderFrameId,
Players: toPbPlayers(pR.Players, true),
}
@ -995,7 +980,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool {
return true
}
func (pR *Room) sendSafely(roomDownsyncFrame *RoomDownsyncFrame, toSendInputFrameDownsyncs []*InputFrameDownsync, act int32, playerId int32, needLockExplicitly bool) {
func (pR *Room) sendSafely(roomDownsyncFrame *pb.RoomDownsyncFrame, toSendInputFrameDownsyncs []*pb.InputFrameDownsync, act int32, playerId int32, needLockExplicitly bool) {
defer func() {
if r := recover(); r != nil {
Logger.Error("sendSafely, recovered from: ", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("panic", r))
@ -1003,7 +988,7 @@ func (pR *Room) sendSafely(roomDownsyncFrame *RoomDownsyncFrame, toSendInputFram
}()
if playerDownsyncSession, existent := pR.PlayerDownsyncSessionDict[playerId]; existent {
pResp := &WsResp{
pResp := &pb.WsResp{
Ret: int32(Constants.RetCode.Ok),
Act: act,
Rdf: roomDownsyncFrame,
@ -1030,7 +1015,7 @@ func (pR *Room) shouldPrefabInputFrameDownsync(prevRenderFrameId int32, renderFr
return false, -1
}
func (pR *Room) getOrPrefabInputFrameDownsync(inputFrameId int32) *InputFrameDownsync {
func (pR *Room) getOrPrefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDownsync {
/*
[WARNING] This function MUST BE called while "pR.InputsBufferLock" is locked.
@ -1039,12 +1024,12 @@ func (pR *Room) getOrPrefabInputFrameDownsync(inputFrameId int32) *InputFrameDow
- OR there's no change w.r.t. to its prev cmd.
*/
var currInputFrameDownsync *InputFrameDownsync = nil
var currInputFrameDownsync *pb.InputFrameDownsync = nil
tmp1 := pR.InputsBuffer.GetByFrameId(inputFrameId) // Would be nil if "pR.InputsBuffer.EdFrameId <= inputFrameId", else if "pR.InputsBuffer.EdFrameId > inputFrameId" is already met, then by now we can just return "tmp1.(*InputFrameDownsync)"
if nil == tmp1 {
for pR.InputsBuffer.EdFrameId <= inputFrameId {
j := pR.InputsBuffer.EdFrameId
currInputFrameDownsync = &InputFrameDownsync{
currInputFrameDownsync = &pb.InputFrameDownsync{
InputFrameId: j,
InputList: make([]uint64, pR.Capacity),
ConfirmedList: uint64(0),
@ -1056,7 +1041,7 @@ func (pR *Room) getOrPrefabInputFrameDownsync(inputFrameId int32) *InputFrameDow
}
tmp2 := pR.InputsBuffer.GetByFrameId(j2)
if nil != tmp2 {
prevInputFrameDownsync := tmp2.(*InputFrameDownsync)
prevInputFrameDownsync := tmp2.(*pb.InputFrameDownsync)
for i, _ := range currInputFrameDownsync.InputList {
currInputFrameDownsync.InputList[i] = (prevInputFrameDownsync.InputList[i] & uint64(15)) // Don't predict attack input!
}
@ -1065,13 +1050,13 @@ func (pR *Room) getOrPrefabInputFrameDownsync(inputFrameId int32) *InputFrameDow
pR.InputsBuffer.Put(currInputFrameDownsync)
}
} else {
currInputFrameDownsync = tmp1.(*InputFrameDownsync)
currInputFrameDownsync = tmp1.(*pb.InputFrameDownsync)
}
return currInputFrameDownsync
}
func (pR *Room) markConfirmationIfApplicable(inputFrameUpsyncBatch []*InputFrameUpsync, playerId int32, player *Player) *InputsBufferSnapshot {
func (pR *Room) markConfirmationIfApplicable(inputFrameUpsyncBatch []*pb.InputFrameUpsync, playerId int32, player *Player) *pb.InputsBufferSnapshot {
// [WARNING] This function MUST BE called while "pR.InputsBufferLock" is locked!
// Step#1, put the received "inputFrameUpsyncBatch" into "pR.InputsBuffer"
for _, inputFrameUpsync := range inputFrameUpsyncBatch {
@ -1111,7 +1096,7 @@ func (pR *Room) markConfirmationIfApplicable(inputFrameUpsyncBatch []*InputFrame
panic(fmt.Sprintf("inputFrameId=%v doesn't exist for roomId=%v! InputsBuffer=%v", inputFrameId, pR.Id, pR.InputsBufferString(false)))
}
shouldBreakConfirmation := false
inputFrameDownsync := tmp.(*InputFrameDownsync)
inputFrameDownsync := tmp.(*pb.InputFrameDownsync)
if allConfirmedMask != inputFrameDownsync.ConfirmedList {
for _, player := range pR.PlayersArr {
@ -1170,7 +1155,7 @@ func (pR *Room) forceConfirmationIfApplicable(prevRenderFrameId int32) uint64 {
if nil == tmp {
panic(fmt.Sprintf("inputFrameId=%v doesn't exist for roomId=%v! InputsBuffer=%v", j, pR.Id, pR.InputsBufferString(false)))
}
inputFrameDownsync := tmp.(*InputFrameDownsync)
inputFrameDownsync := tmp.(*pb.InputFrameDownsync)
unconfirmedMask |= (allConfirmedMask ^ inputFrameDownsync.ConfirmedList)
inputFrameDownsync.ConfirmedList = allConfirmedMask
pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, -1)
@ -1197,7 +1182,7 @@ func (pR *Room) forceConfirmationIfApplicable(prevRenderFrameId int32) uint64 {
return unconfirmedMask
}
func (pR *Room) produceInputsBufferSnapshotWithCurDynamicsRenderFrameAsRef(unconfirmedMask uint64, snapshotStFrameId, snapshotEdFrameId int32) *InputsBufferSnapshot {
func (pR *Room) produceInputsBufferSnapshotWithCurDynamicsRenderFrameAsRef(unconfirmedMask uint64, snapshotStFrameId, snapshotEdFrameId int32) *pb.InputsBufferSnapshot {
// [WARNING] This function MUST BE called while "pR.InputsBufferLock" is locked!
refRenderFrameIdIfNeeded := pR.CurDynamicsRenderFrameId - 1
if 0 > refRenderFrameIdIfNeeded {
@ -1206,7 +1191,7 @@ func (pR *Room) produceInputsBufferSnapshotWithCurDynamicsRenderFrameAsRef(uncon
// Duplicate downsynced inputFrameIds will be filtered out by frontend.
toSendInputFrameDownsyncs := pR.cloneInputsBuffer(snapshotStFrameId, snapshotEdFrameId)
return &InputsBufferSnapshot{
return &pb.InputsBufferSnapshot{
RefRenderFrameId: refRenderFrameIdIfNeeded,
UnconfirmedMask: unconfirmedMask,
ToSendInputFrameDownsyncs: toSendInputFrameDownsyncs,
@ -1228,9 +1213,9 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
if nil == currRenderFrameTmp {
panic(fmt.Sprintf("collisionSysRenderFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v)! RenderFrameBuffer=%v", collisionSysRenderFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, pR.RenderFrameBufferString()))
}
currRenderFrame := currRenderFrameTmp.(*RoomDownsyncFrame)
currRenderFrame := currRenderFrameTmp.(*battle.RoomDownsyncFrame)
delayedInputFrameId := pR.ConvertToInputFrameId(collisionSysRenderFrameId, pR.InputDelayFrames)
var delayedInputFrame *InputFrameDownsync = nil
var delayedInputFrame *pb.InputFrameDownsync = nil
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! InputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.InputsBufferString(false)))
@ -1239,7 +1224,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! InputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.InputsBufferString(false)))
}
delayedInputFrame = tmp.(*InputFrameDownsync)
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.
delayedInputFrame.ConfirmedList = allConfirmedMask
}
@ -1250,370 +1235,6 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
}
}
// TODO: Write unit-test for this function to compare with its frontend counter part
func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSysMap map[int32]*resolv.Object) *RoomDownsyncFrame {
topPadding, bottomPadding, leftPadding, rightPadding := pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap
// [WARNING] This function MUST BE called while "pR.InputsBufferLock" is locked!
nextRenderFramePlayers := make(map[int32]*PlayerDownsync, pR.Capacity)
// Make a copy first
for playerId, currPlayerDownsync := range currRenderFrame.Players {
nextRenderFramePlayers[playerId] = &PlayerDownsync{
Id: playerId,
VirtualGridX: currPlayerDownsync.VirtualGridX,
VirtualGridY: currPlayerDownsync.VirtualGridY,
DirX: currPlayerDownsync.DirX,
DirY: currPlayerDownsync.DirY,
VelX: currPlayerDownsync.VelX,
VelY: currPlayerDownsync.VelY,
CharacterState: currPlayerDownsync.CharacterState,
InAir: true,
Speed: currPlayerDownsync.Speed,
BattleState: currPlayerDownsync.BattleState,
Score: currPlayerDownsync.Score,
Removed: currPlayerDownsync.Removed,
JoinIndex: currPlayerDownsync.JoinIndex,
FramesToRecover: currPlayerDownsync.FramesToRecover - 1,
Hp: currPlayerDownsync.Hp,
MaxHp: currPlayerDownsync.MaxHp,
}
if nextRenderFramePlayers[playerId].FramesToRecover < 0 {
nextRenderFramePlayers[playerId].FramesToRecover = 0
}
}
nextRenderFrameMeleeBullets := make([]*MeleeBullet, 0, len(currRenderFrame.MeleeBullets)) // Is there any better way to reduce malloc/free impact, e.g. smart prediction for fixed memory allocation?
effPushbacks := make([]Vec2D, pR.Capacity)
hardPushbackNorms := make([][]Vec2D, pR.Capacity)
// 1. Process player inputs
if nil != delayedInputFrame {
var delayedInputFrameForPrevRenderFrame *InputFrameDownsync = nil
tmp := pR.InputsBuffer.GetByFrameId(pR.ConvertToInputFrameId(currRenderFrame.Id-1, pR.InputDelayFrames))
if nil != tmp {
delayedInputFrameForPrevRenderFrame = tmp.(*InputFrameDownsync)
}
inputList := delayedInputFrame.InputList
for _, player := range pR.PlayersArr {
playerId := player.Id
joinIndex := player.JoinIndex
currPlayerDownsync, thatPlayerInNextFrame := currRenderFrame.Players[playerId], nextRenderFramePlayers[playerId]
if 0 < thatPlayerInNextFrame.FramesToRecover {
continue
}
decodedInput := pR.decodeInput(inputList[joinIndex-1])
prevBtnALevel, prevBtnBLevel := int32(0), int32(0)
if nil != delayedInputFrameForPrevRenderFrame {
prevDecodedInput := pR.decodeInput(delayedInputFrameForPrevRenderFrame.InputList[joinIndex-1])
prevBtnALevel = prevDecodedInput.BtnALevel
prevBtnBLevel = prevDecodedInput.BtnBLevel
}
if decodedInput.BtnBLevel > prevBtnBLevel {
characStateAlreadyInAir := false
if ATK_CHARACTER_STATE_INAIR_IDLE1 == thatPlayerInNextFrame.CharacterState || ATK_CHARACTER_STATE_INAIR_ATK1 == thatPlayerInNextFrame.CharacterState || ATK_CHARACTER_STATE_INAIR_ATKED1 == thatPlayerInNextFrame.CharacterState {
characStateAlreadyInAir = true
}
characStateIsInterruptWaivable := false
if ATK_CHARACTER_STATE_IDLE1 == thatPlayerInNextFrame.CharacterState || ATK_CHARACTER_STATE_WALKING == thatPlayerInNextFrame.CharacterState || ATK_CHARACTER_STATE_INAIR_IDLE1 == thatPlayerInNextFrame.CharacterState {
characStateIsInterruptWaivable = true
}
if !characStateAlreadyInAir && characStateIsInterruptWaivable {
thatPlayerInNextFrame.VelY = pR.JumpingInitVelY
if 1 == currPlayerDownsync.JoinIndex {
Logger.Info(fmt.Sprintf("playerId=%v, joinIndex=%v jumped at {renderFrame.id: %d, virtualX: %d, virtualY: %d, nextVelX: %d, nextVelY: %d, nextCharacterState=%d, inAir=%v}, delayedInputFrame.id=%d", playerId, joinIndex, currRenderFrame.Id, currPlayerDownsync.VirtualGridX, currPlayerDownsync.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, thatPlayerInNextFrame.CharacterState, currPlayerDownsync.InAir, delayedInputFrame.InputFrameId))
}
}
}
if decodedInput.BtnALevel > prevBtnALevel {
punchSkillId := int32(1)
punchConfig := pR.MeleeSkillConfig[punchSkillId]
var newMeleeBullet MeleeBullet = *punchConfig
newMeleeBullet.BattleLocalId = pR.BulletBattleLocalIdCounter
pR.BulletBattleLocalIdCounter += 1
newMeleeBullet.OffenderJoinIndex = joinIndex
newMeleeBullet.OffenderPlayerId = playerId
newMeleeBullet.OriginatedRenderFrameId = currRenderFrame.Id
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, &newMeleeBullet)
thatPlayerInNextFrame.FramesToRecover = newMeleeBullet.RecoveryFrames
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_ATK1
if false == currPlayerDownsync.InAir {
thatPlayerInNextFrame.VelX = 0
}
Logger.Debug(fmt.Sprintf("roomId=%v, playerId=%v triggered a rising-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId))
} else if decodedInput.BtnALevel < prevBtnALevel {
Logger.Debug(fmt.Sprintf("roomId=%v, playerId=%v triggered a falling-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId))
} else {
// No bullet trigger, process movement inputs
// Note that by now "0 == thatPlayerInNextFrame.FramesToRecover", we should change "CharacterState" to "WALKING" or "IDLE" depending on player inputs
if 0 != decodedInput.Dx || 0 != decodedInput.Dy {
thatPlayerInNextFrame.DirX = decodedInput.Dx
thatPlayerInNextFrame.DirY = decodedInput.Dy
thatPlayerInNextFrame.VelX = decodedInput.Dx * currPlayerDownsync.Speed
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_WALKING
} else {
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
thatPlayerInNextFrame.VelX = 0
}
}
}
}
// 2. Process player movement
for _, player := range pR.PlayersArr {
playerId := player.Id
joinIndex := player.JoinIndex
effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y = float64(0), float64(0)
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := collisionSysMap[collisionPlayerIndex]
currPlayerDownsync, thatPlayerInNextFrame := currRenderFrame.Players[playerId], nextRenderFramePlayers[playerId]
// Reset playerCollider position from the "virtual grid position"
newVx, newVy := currPlayerDownsync.VirtualGridX+currPlayerDownsync.VelX, currPlayerDownsync.VirtualGridY+currPlayerDownsync.VelY
if thatPlayerInNextFrame.VelY == pR.JumpingInitVelY {
newVy += thatPlayerInNextFrame.VelY
}
halfColliderWidth, halfColliderHeight := player.ColliderRadius, player.ColliderRadius+player.ColliderRadius // avoid multiplying
playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderBLPos(newVx, newVy, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.VirtualGridToWorldRatio)
// Update in the collision system
playerCollider.Update()
if currPlayerDownsync.InAir {
thatPlayerInNextFrame.VelX += pR.GravityX
thatPlayerInNextFrame.VelY += pR.GravityY
}
}
// 3. Add bullet colliders into collision system
bulletColliders := make(map[int32]*resolv.Object, 0) // Will all be removed at the end of `applyInputFrameDownsyncDynamicsOnSingleRenderFrame` due to the need for being rollback-compatible
removedBulletsAtCurrFrame := make(map[int32]int32, 0)
for _, meleeBullet := range currRenderFrame.MeleeBullets {
if (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames <= currRenderFrame.Id) && (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames+meleeBullet.ActiveFrames > currRenderFrame.Id) {
collisionBulletIndex := COLLISION_BULLET_INDEX_PREFIX + meleeBullet.BattleLocalId
collisionOffenderIndex := COLLISION_PLAYER_INDEX_PREFIX + meleeBullet.OffenderJoinIndex
offenderCollider := collisionSysMap[collisionOffenderIndex]
offender := currRenderFrame.Players[meleeBullet.OffenderPlayerId]
xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis"
if 0 > offender.DirX {
xfac = float64(-1.0)
}
offenderWx, offenderWy := VirtualGridToWorldPos(offender.VirtualGridX, offender.VirtualGridY, pR.VirtualGridToWorldRatio)
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, meleeBullet, "MeleeBullet")
pR.Space.Add(newBulletCollider)
collisionSysMap[collisionBulletIndex] = newBulletCollider
bulletColliders[collisionBulletIndex] = newBulletCollider
Logger.Debug(fmt.Sprintf("roomId=%v, a meleeBullet is added to collisionSys at currRenderFrame.id=%v as start-up frames ended and active frame is not yet ended: %v, from offenderCollider=%v, xfac=%v", pR.Id, currRenderFrame.Id, ConvexPolygonStr(newBulletCollider.Shape.(*resolv.ConvexPolygon)), ConvexPolygonStr(offenderCollider.Shape.(*resolv.ConvexPolygon)), xfac))
}
}
// 4. Invoke collision system stepping (no-op for backend collision lib)
// 5. Calc pushbacks for each player (after its movement) w/o bullets
for _, player := range pR.PlayersArr {
joinIndex := player.JoinIndex
playerId := player.Id
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := collisionSysMap[collisionPlayerIndex]
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
hardPushbackNorms[joinIndex-1] = pR.calcHardPushbacksNorms(playerCollider, playerShape, pR.SnapIntoPlatformOverlap, &(effPushbacks[joinIndex-1]))
currPlayerDownsync, thatPlayerInNextFrame := currRenderFrame.Players[playerId], nextRenderFramePlayers[playerId]
fallStopping := false
possiblyFallStoppedOnAnotherPlayer := false
if collision := playerCollider.Check(0, 0); nil != collision {
for _, obj := range collision.Objects {
isBarrier, isAnotherPlayer, isBullet := false, false, false
switch obj.Data.(type) {
case *Barrier:
isBarrier = true
case *Player:
isAnotherPlayer = true
case *MeleeBullet:
isBullet = true
}
if isBullet {
// ignore bullets for this step
continue
}
bShape := obj.Shape.(*resolv.ConvexPolygon)
overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, bShape)
if !overlapped {
continue
}
normAlignmentWithGravity := (overlapResult.OverlapX*float64(0) + overlapResult.OverlapY*float64(-1.0))
landedOnGravityPushback := (pR.SnapIntoPlatformThreshold < normAlignmentWithGravity) // prevents false snapping on the lateral sides
if landedOnGravityPushback {
// kindly note that one player might land on top of another player, and snapping is also required in such case
pushbackX, pushbackY = (overlapResult.Overlap-pR.SnapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-pR.SnapIntoPlatformOverlap)*overlapResult.OverlapY
thatPlayerInNextFrame.InAir = false
}
if isAnotherPlayer {
// [WARNING] See comments of this substep in frontend.
pushbackX, pushbackY = (overlapResult.Overlap-pR.SnapIntoPlatformOverlap*2)*overlapResult.OverlapX, (overlapResult.Overlap-pR.SnapIntoPlatformOverlap*2)*overlapResult.OverlapY
}
for _, hardPushbackNorm := range hardPushbackNorms[joinIndex-1] {
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
if isBarrier || (isAnotherPlayer && 0 > projectedMagnitude) {
pushbackX -= projectedMagnitude * hardPushbackNorm.X
pushbackY -= projectedMagnitude * hardPushbackNorm.Y
}
}
effPushbacks[joinIndex-1].X += pushbackX
effPushbacks[joinIndex-1].Y += pushbackY
if currPlayerDownsync.InAir && landedOnGravityPushback {
fallStopping = true
if isAnotherPlayer {
possiblyFallStoppedOnAnotherPlayer = true
}
}
if 1 == joinIndex {
halfColliderWidth, halfColliderHeight := player.ColliderRadius, player.ColliderRadius+player.ColliderRadius // avoid multiplying
if fallStopping {
Logger.Debug(fmt.Sprintf("playerId=%d, joinIndex=%d fallStopping#1\n{renderFrame.id: %d, possiblyFallStoppedOnAnotherPlayer: %v}\nplayerColliderPos=%v, effPushback={%.3f, %.3f}, overlapMag=%.4f", playerId, joinIndex, currRenderFrame.Id, possiblyFallStoppedOnAnotherPlayer, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap))
} else if currPlayerDownsync.InAir && isBarrier && !landedOnGravityPushback {
//Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d inAir & pushed back by barrier & not landed at {renderFrame.id: %d}\nplayerColliderPos=%v, effPushback={%.3f, %.3f}, overlapMag=%.4f, len(hardPushbackNorms)=%d", playerId, joinIndex, currRenderFrame.Id, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap, len(hardPushbackNorms)))
} else if currPlayerDownsync.InAir && isAnotherPlayer {
//Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d inAir & pushed back by another player\n{renderFrame.id: %d}\nplayerColliderPos=%v, anotherPlayerColliderPos=%v, effPushback={%.3f, %.3f}, landedOnGravityPushback=%v, fallStopping=%v, overlapMag=%.4f, len(hardPushbackNorms)=%d", playerId, joinIndex, currRenderFrame.Id, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), RectCenterStr(obj, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, landedOnGravityPushback, fallStopping, overlapResult.Overlap, len(hardPushbackNorms)))
}
}
}
}
if fallStopping {
thatPlayerInNextFrame.VelX = 0
thatPlayerInNextFrame.VelY = 0
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
thatPlayerInNextFrame.FramesToRecover = 0
}
if currPlayerDownsync.InAir {
oldNextCharacterState := thatPlayerInNextFrame.CharacterState
switch oldNextCharacterState {
case ATK_CHARACTER_STATE_IDLE1, ATK_CHARACTER_STATE_WALKING:
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_IDLE1
case ATK_CHARACTER_STATE_ATK1:
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_ATK1
case ATK_CHARACTER_STATE_ATKED1:
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_ATKED1
}
}
}
// 6. Check bullet-anything collisions
for _, bulletCollider := range bulletColliders {
shouldRemove := false
meleeBullet := bulletCollider.Data.(*MeleeBullet)
collisionBulletIndex := COLLISION_BULLET_INDEX_PREFIX + meleeBullet.BattleLocalId
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
if collision := bulletCollider.Check(0, 0); collision != nil {
offender := currRenderFrame.Players[meleeBullet.OffenderPlayerId]
for _, obj := range collision.Objects {
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
switch t := obj.Data.(type) {
case *Player:
if meleeBullet.OffenderPlayerId != t.Id {
if overlapped, _, _, _ := CalcPushbacks(0, 0, bulletShape, defenderShape); overlapped {
joinIndex := t.JoinIndex
xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis"
if 0 > offender.DirX {
xfac = float64(-1.0)
}
pushbackX, pushbackY := -xfac*meleeBullet.Pushback, float64(0)
for _, hardPushbackNorm := range hardPushbackNorms[joinIndex-1] {
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
if 0 > projectedMagnitude {
//Logger.Debug(fmt.Sprintf("defenderPlayerId=%d, joinIndex=%d reducing bullet pushback={%.3f, %.3f} by {%.3f, %.3f} where hardPushbackNorm={%.3f, %.3f}, projectedMagnitude=%.3f at renderFrame.id=%d", t.Id, joinIndex, pushbackX, pushbackY, projectedMagnitude*hardPushbackNorm.X, projectedMagnitude*hardPushbackNorm.Y, hardPushbackNorm.X, hardPushbackNorm.Y, projectedMagnitude, currRenderFrame.Id))
pushbackX -= projectedMagnitude * hardPushbackNorm.X
pushbackY -= projectedMagnitude * hardPushbackNorm.Y
}
}
effPushbacks[joinIndex-1].X += pushbackX
effPushbacks[joinIndex-1].Y += pushbackY
atkedPlayerInCurFrame, atkedPlayerInNextFrame := currRenderFrame.Players[t.Id], nextRenderFramePlayers[t.Id]
atkedPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_ATKED1
if atkedPlayerInCurFrame.InAir {
atkedPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_ATKED1
}
oldFramesToRecover := nextRenderFramePlayers[t.Id].FramesToRecover
if meleeBullet.HitStunFrames > oldFramesToRecover {
atkedPlayerInNextFrame.FramesToRecover = meleeBullet.HitStunFrames
}
Logger.Debug(fmt.Sprintf("roomId=%v, a meleeBullet collides w/ player at currRenderFrame.id=%v: b=%v, p=%v", pR.Id, currRenderFrame.Id, ConvexPolygonStr(bulletShape), ConvexPolygonStr(defenderShape)))
}
}
default:
Logger.Debug(fmt.Sprintf("Bullet %v collided with non-player %v: roomId=%v, currRenderFrame.Id=%v, delayedInputFrame.Id=%v, objDataType=%t, objData=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(defenderShape), pR.Id, currRenderFrame.Id, delayedInputFrame.InputFrameId, obj.Data, obj.Data))
}
}
shouldRemove = true
}
if shouldRemove {
removedBulletsAtCurrFrame[collisionBulletIndex] = 1
}
}
// [WARNING] Remove bullets from collisionSys ANYWAY for the convenience of rollback
for _, meleeBullet := range currRenderFrame.MeleeBullets {
collisionBulletIndex := COLLISION_BULLET_INDEX_PREFIX + meleeBullet.BattleLocalId
if bulletCollider, existent := collisionSysMap[collisionBulletIndex]; existent {
bulletCollider.Space.Remove(bulletCollider)
delete(collisionSysMap, collisionBulletIndex)
}
if _, existent := removedBulletsAtCurrFrame[collisionBulletIndex]; existent {
continue
}
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, meleeBullet)
}
// 7. Get players out of stuck barriers if there's any
for _, player := range pR.PlayersArr {
joinIndex := player.JoinIndex
playerId := player.Id
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := collisionSysMap[collisionPlayerIndex]
// Update "virtual grid position"
currPlayerDownsync, thatPlayerInNextFrame := currRenderFrame.Players[playerId], nextRenderFramePlayers[playerId]
halfColliderWidth, halfColliderHeight := player.ColliderRadius, player.ColliderRadius+player.ColliderRadius // avoid multiplying
thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY = PolygonColliderBLToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.WorldToVirtualGridRatio)
if 1 == thatPlayerInNextFrame.JoinIndex {
if currPlayerDownsync.InAir && !thatPlayerInNextFrame.InAir {
Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d fallStopping#2:\n{nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}\n\tcalculated from <- playerColliderPos=%v, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
} else if !currPlayerDownsync.InAir && thatPlayerInNextFrame.InAir {
Logger.Warn(fmt.Sprintf("playerId=%d, joinIndex=%d took off:\n{nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}\n\tcalculated from <- playerColliderPos=%v, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
} else if thatPlayerInNextFrame.InAir && (0 != thatPlayerInNextFrame.VelY) {
//Logger.Info(fmt.Sprintf("playerId=%d, joinIndex=%d inAir trajectory:\n{nextRenderFrame.id: %d, nextVirtualX: %d, nextVirtualY: %d, nextVelX: %d, nextVelY: %d}\n\tcalculated from <- playerColliderPos=%v, effPushback={%.3f, %.3f}", playerId, joinIndex, currRenderFrame.Id+1, thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY, thatPlayerInNextFrame.VelX, thatPlayerInNextFrame.VelY, RectCenterStr(playerCollider, halfColliderWidth, halfColliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY), effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y))
}
}
}
return &RoomDownsyncFrame{
Id: currRenderFrame.Id + 1,
Players: nextRenderFramePlayers,
MeleeBullets: nextRenderFrameMeleeBullets,
CountdownNanos: (pR.BattleDurationNanos - int64(currRenderFrame.Id)*pR.RollbackEstimatedDtNanos),
}
}
func (pR *Room) decodeInput(encodedInput uint64) *InputFrameDecoded {
encodedDirection := (encodedInput & uint64(15))
btnALevel := int32((encodedInput >> 4) & 1)
btnBLevel := int32((encodedInput >> 5) & 1)
return &InputFrameDecoded{
Dx: DIRECTION_DECODER[encodedDirection][0],
Dy: DIRECTION_DECODER[encodedDirection][1],
BtnALevel: btnALevel,
BtnBLevel: btnBLevel,
}
}
func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool {
return 0 == (inputFrameId % 10)
}
func (pR *Room) refreshColliders(spaceW, spaceH int32) {
// Kindly note that by now, we've already got all the shapes in the tmx file into "pR.(Players | Barriers)" from "ParseTmxLayersAndGroups"
@ -1621,21 +1242,30 @@ func (pR *Room) refreshColliders(spaceW, spaceH int32) {
minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 3) // the approx minimum distance a player can move per frame in world coordinate
pR.Space = resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled
for _, player := range pR.Players {
wx, wy := VirtualGridToWorldPos(player.VirtualGridX, player.VirtualGridY, pR.VirtualGridToWorldRatio)
jsPlayers := toJsPlayers(pR.Players, false)
for _, player := range jsPlayers {
wx, wy := battle.VirtualGridToWorldPos(player.VirtualGridX, player.VirtualGridY, pR.VirtualGridToWorldRatio)
colliderWidth, colliderHeight := player.ColliderRadius*2, player.ColliderRadius*4
playerCollider := GenerateRectCollider(wx, wy, colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, player, "Player") // the coords of all barrier boundaries are multiples of tileWidth(i.e. 16), by adding snapping y-padding when "landedOnGravityPushback" all "playerCollider.Y" would be a multiple of 1.0
playerCollider := battle.GenerateRectCollider(wx, wy, colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, player, "Player") // the coords of all barrier boundaries are multiples of tileWidth(i.e. 16), by adding snapping y-padding when "landedOnGravityPushback" all "playerCollider.Y" would be a multiple of 1.0
pR.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
collisionPlayerIndex := battle.COLLISION_PLAYER_INDEX_PREFIX + joinIndex
pR.CollisionSysMap[collisionPlayerIndex] = playerCollider
}
for _, barrier := range pR.Barriers {
boundaryUnaligned := barrier.Boundary
barrierCollider := GenerateConvexPolygonCollider(boundaryUnaligned, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, barrier, "Barrier")
for _, player := range pR.Players {
joinIndex := player.JoinIndex
pR.PlayersArr[joinIndex-1] = player
}
barrierPolygon2DList := pR.TmxPolygonsMap["Barrier"]
for _, polygon2DUnaligned := range barrierPolygon2DList {
/*
// 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")
pR.Space.Add(barrierCollider)
}
}
@ -1695,7 +1325,7 @@ func (pR *Room) doBattleMainLoopPerTickBackendDynamicsWithProperLocking(prevRend
}
}
func (pR *Room) downsyncToAllPlayers(inputsBufferSnapshot *InputsBufferSnapshot) {
func (pR *Room) downsyncToAllPlayers(inputsBufferSnapshot *pb.InputsBufferSnapshot) {
/*
[WARNING] This function MUST BE called while "pR.InputsBufferLock" is LOCKED to **preserve the order of generation of "inputsBufferSnapshot" for sending** -- see comments in "OnBattleCmdReceived" and [this issue](https://github.com/genxium/DelayNoMore/issues/12).
@ -1764,7 +1394,7 @@ func (pR *Room) downsyncToAllPlayers(inputsBufferSnapshot *InputsBufferSnapshot)
}
}
func (pR *Room) downsyncToSinglePlayer(playerId int32, player *Player, refRenderFrameId int32, unconfirmedMask uint64, toSendInputFrameDownsyncsSnapshot []*InputFrameDownsync, shouldForceResync bool) {
func (pR *Room) downsyncToSinglePlayer(playerId int32, player *Player, refRenderFrameId int32, unconfirmedMask uint64, toSendInputFrameDownsyncsSnapshot []*pb.InputFrameDownsync, shouldForceResync bool) {
/*
[WARNING] This function MUST BE called while "pR.InputsBufferLock" is unlocked -- otherwise the network I/O blocking of "sendSafely" might cause significant lag for "markConfirmationIfApplicable & forceConfirmationIfApplicable"!
@ -1796,7 +1426,7 @@ func (pR *Room) downsyncToSinglePlayer(playerId int32, player *Player, refRender
panic(fmt.Sprintf("Required refRenderFrameId=%v for (roomId=%v, renderFrameId=%v, playerId=%v, playerLastSentInputFrameId=%v) doesn't exist! InputsBuffer=%v, RenderFrameBuffer=%v", refRenderFrameId, pR.Id, pR.RenderFrameId, playerId, player.LastSentInputFrameId, pR.InputsBufferString(false), pR.RenderFrameBufferString()))
}
refRenderFrame := tmp.(*RoomDownsyncFrame)
refRenderFrame := tmp.(*battle.RoomDownsyncFrame)
for _, player := range pR.PlayersArr {
refRenderFrame.Players[player.Id].ColliderRadius = player.ColliderRadius // hardcoded for now
}
@ -1818,9 +1448,9 @@ func (pR *Room) downsyncToSinglePlayer(playerId int32, player *Player, refRender
}
}
func (pR *Room) cloneInputsBuffer(stFrameId, edFrameId int32) []*InputFrameDownsync {
func (pR *Room) cloneInputsBuffer(stFrameId, edFrameId int32) []*pb.InputFrameDownsync {
// [WARNING] This function MUST BE called while "pR.InputsBufferLock" is locked!
cloned := make([]*InputFrameDownsync, 0, edFrameId-stFrameId)
cloned := make([]*pb.InputFrameDownsync, 0, edFrameId-stFrameId)
prevFrameFound := false
j := stFrameId
for j < edFrameId {
@ -1834,9 +1464,9 @@ func (pR *Room) cloneInputsBuffer(stFrameId, edFrameId int32) []*InputFrameDowns
}
}
prevFrameFound = true
foo := tmp.(*InputFrameDownsync)
foo := tmp.(*pb.InputFrameDownsync)
bar := &InputFrameDownsync{
bar := &pb.InputFrameDownsync{
InputFrameId: foo.InputFrameId,
InputList: make([]uint64, len(foo.InputList)),
ConfirmedList: foo.ConfirmedList,
@ -1850,29 +1480,3 @@ func (pR *Room) cloneInputsBuffer(stFrameId, edFrameId int32) []*InputFrameDowns
return cloned
}
func (pR *Room) calcHardPushbacksNorms(playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D) []Vec2D {
ret := make([]Vec2D, 0, 10) // no one would simultaneously have more than 5 hardPushbacks
collision := playerCollider.Check(0, 0)
if nil == collision {
return ret
}
for _, obj := range collision.Objects {
switch obj.Data.(type) {
case *Barrier:
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, barrierShape)
if !overlapped {
continue
}
// ALWAY snap into hardPushbacks!
// [OverlapX, OverlapY] is the unit vector that points into the platform
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapY
ret = append(ret, Vec2D{X: overlapResult.OverlapX, Y: overlapResult.OverlapY})
pEffPushback.X += pushbackX
pEffPushback.Y += pushbackY
default:
}
}
return ret
}

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ package ws
import (
. "battle_srv/common"
"battle_srv/models"
pb "jsexport/protos"
pb "battle_srv/protos"
"container/heap"
"fmt"
"github.com/gin-gonic/gin"

View File

@ -1,50 +0,0 @@
package dnmshared
import (
. "dnmshared/sharedprotos"
"math"
)
func NormVec2D(dx, dy float64) Vec2D {
return Vec2D{X: dy, Y: -dx}
}
func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
// Transform again to put "anchor" at the "bottom-left point (w.r.t. world space)" of the bounding box for "resolv"
boundingBoxBL := &Vec2D{
X: math.MaxFloat64,
Y: math.MaxFloat64,
}
for _, p := range input.Points {
if p.X < boundingBoxBL.X {
boundingBoxBL.X = p.X
}
if p.Y < boundingBoxBL.Y {
boundingBoxBL.Y = p.Y
}
}
// Now "input.Anchor" should move to "input.Anchor+boundingBoxBL", thus "boundingBoxBL" is also the value of the negative diff for all "input.Points"
output := &Polygon2D{
Anchor: &Vec2D{
X: input.Anchor.X + boundingBoxBL.X,
Y: input.Anchor.Y + boundingBoxBL.Y,
},
Points: make([]*Vec2D, len(input.Points)),
}
for i, p := range input.Points {
output.Points[i] = &Vec2D{
X: p.X - boundingBoxBL.X,
Y: p.Y - boundingBoxBL.Y,
}
}
return output
}
func Distance(pt1 *Vec2D, pt2 *Vec2D) float64 {
dx := pt1.X - pt2.X
dy := pt1.Y - pt2.Y
return math.Sqrt(dx*dx + dy*dy)
}

View File

@ -1,14 +1,16 @@
package dnmshared
import (
. "dnmshared/sharedprotos"
. "jsexport/battle"
"fmt"
"github.com/kvartborg/vector"
"github.com/solarlune/resolv"
"math"
"strings"
)
func NormVec2D(dx, dy float64) Vec2D {
return Vec2D{X: dy, Y: -dx}
}
func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
var s []string = make([]string, len(body.Points))
for i, p := range body.Points {
@ -21,251 +23,3 @@ func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
func RectCenterStr(body *resolv.Object, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64) string {
return fmt.Sprintf("{%.2f, %.2f}", body.X+leftPadding+halfBoundingW-spaceOffsetX, body.Y+bottomPadding+halfBoundingH-spaceOffsetY)
}
func GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *resolv.Object {
blX, blY := WorldToPolygonColliderBLPos(wx, wy, w*0.5, h*0.5, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY)
return generateRectColliderInCollisionSpace(blX, blY, leftPadding+w+rightPadding, bottomPadding+h+topPadding, data, tag)
}
func generateRectColliderInCollisionSpace(blX, blY, w, h float64, data interface{}, tag string) *resolv.Object {
collider := resolv.NewObject(blX, blY, w, h, tag) // Unlike its frontend counter part, the position of a "resolv.Object" must be specified by "bottom-left point" because "w" and "h" must be positive, see "resolv.Object.BoundsToSpace" for details
shape := resolv.NewRectangle(0, 0, w, h)
collider.SetShape(shape)
collider.Data = data
return collider
}
func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *resolv.Object {
aligned := AlignPolygon2DToBoundingBox(unalignedSrc)
var w, h float64 = 0, 0
shape := resolv.NewConvexPolygon()
for i, pi := range aligned.Points {
for j, pj := range aligned.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)
}
}
}
for i := 0; i < len(aligned.Points); i++ {
p := aligned.Points[i]
shape.AddPoints(p.X, p.Y)
}
collider := resolv.NewObject(aligned.Anchor.X+spaceOffsetX, aligned.Anchor.Y+spaceOffsetY, w, h, tag)
collider.SetShape(shape)
collider.Data = data
return collider
}
func CalcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.ConvexPolygon) (bool, float64, float64, *SatResult) {
origX, origY := playerShape.Position()
defer func() {
playerShape.SetPosition(origX, origY)
}()
playerShape.SetPosition(origX+oldDx, origY+oldDy)
overlapResult := &SatResult{
Overlap: 0,
OverlapX: 0,
OverlapY: 0,
AContainedInB: true,
BContainedInA: true,
Axis: vector.Vector{0, 0},
}
if overlapped := isPolygonPairOverlapped(playerShape, barrierShape, overlapResult); overlapped {
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
return true, pushbackX, pushbackY, overlapResult
} else {
return false, 0, 0, overlapResult
}
}
type SatResult struct {
Overlap float64
OverlapX float64
OverlapY float64
AContainedInB bool
BContainedInA bool
Axis vector.Vector
}
func isPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool {
aCnt, bCnt := len(a.Points), len(b.Points)
// Single point case
if 1 == aCnt && 1 == bCnt {
if nil != result {
result.Overlap = 0
}
return a.Points[0][0] == b.Points[0][0] && a.Points[0][1] == b.Points[0][1]
}
//Logger.Info(fmt.Sprintf("Checking collision between a=%v, b=%v", ConvexPolygonStr(a), ConvexPolygonStr(b)))
if 1 < aCnt {
for _, axis := range a.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
return false
}
}
}
if 1 < bCnt {
for _, axis := range b.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
return false
}
}
}
//Logger.Info(fmt.Sprintf("a=%v and b=%v are overlapped", ConvexPolygonStr(a), ConvexPolygonStr(b)))
return true
}
func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, result *SatResult) bool {
/*
[WARNING] This function is deliberately made private, it shouldn't be used alone (i.e. not along the norms of a polygon), otherwise the pushbacks calculated would be meaningless.
Consider the following example
a: {
anchor: [1337.19 1696.74]
points: [[0 0] [24 0] [24 24] [0 24]]
},
b: {
anchor: [1277.72 1570.56]
points: [[642.57 319.16] [0 319.16] [5.73 0] [643.75 0.90]]
}
e = (-2.98, 1.49).Unit()
*/
//Logger.Info(fmt.Sprintf("Checking separation between a=%v, b=%v along axis e={%.3f, %.3f}#1", ConvexPolygonStr(a), ConvexPolygonStr(b), e[0], e[1]))
var aStart, aEnd, bStart, bEnd float64 = math.MaxFloat64, -math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64
for _, p := range a.Points {
dot := (p[0]+a.X)*e[0] + (p[1]+a.Y)*e[1]
if aStart > dot {
aStart = dot
}
if aEnd < dot {
aEnd = dot
}
}
for _, p := range b.Points {
dot := (p[0]+b.X)*e[0] + (p[1]+b.Y)*e[1]
if bStart > dot {
bStart = dot
}
if bEnd < dot {
bEnd = dot
}
}
if aStart > bEnd || aEnd < bStart {
// Separated by unit vector "e"
return true
}
if nil != result {
overlap := float64(0)
if aStart < bStart {
result.AContainedInB = false
if aEnd < bEnd {
overlap = aEnd - bStart
result.BContainedInA = false
} else {
option1 := aEnd - bStart
option2 := bEnd - aStart
if option1 < option2 {
overlap = option1
} else {
overlap = -option2
}
}
} else {
result.BContainedInA = false
if aEnd > bEnd {
overlap = aStart - bEnd
result.AContainedInB = false
} else {
option1 := aEnd - bStart
option2 := bEnd - aStart
if option1 < option2 {
overlap = option1
} else {
overlap = -option2
}
}
}
currentOverlap := result.Overlap
absoluteOverlap := overlap
if overlap < 0 {
absoluteOverlap = -overlap
}
if (0 == result.Axis[0] && 0 == result.Axis[1]) || currentOverlap > absoluteOverlap {
var sign float64 = 1
if overlap < 0 {
sign = -1
}
result.Overlap = absoluteOverlap
result.OverlapX = e[0] * sign
result.OverlapY = e[1] * sign
}
result.Axis = e
//Logger.Info(fmt.Sprintf("Checking separation between a=%v, b=%v along axis e={%.3f, %.3f}#2: aStart=%.3f, aEnd=%.3f, bStart=%.3f, bEnd=%.3f, overlap=%.3f, currentOverlap=%.3f, absoluteOverlap=%.3f, result=%v", ConvexPolygonStr(a), ConvexPolygonStr(b), e[0], e[1], aStart, aEnd, bStart, bEnd, overlap, currentOverlap, absoluteOverlap, result))
}
// the specified unit vector "e" doesn't separate "a" and "b", overlap result is generated
return false
}
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
// [WARNING] Introduces loss of precision!
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
var virtualGridX int32 = int32(math.Round(wx * worldToVirtualGridRatio))
var virtualGridY int32 = int32(math.Round(wy * worldToVirtualGridRatio))
return virtualGridX, virtualGridY
}
func VirtualGridToWorldPos(vx, vy int32, virtualGridToWorldRatio float64) (float64, float64) {
// No loss of precision
var wx float64 = float64(vx) * virtualGridToWorldRatio
var wy float64 = float64(vy) * virtualGridToWorldRatio
return wx, wy
}
func WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
return wx - halfBoundingW - leftPadding + collisionSpaceOffsetX, wy - halfBoundingH - bottomPadding + collisionSpaceOffsetY
}
func PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
return cx + halfBoundingW + leftPadding - collisionSpaceOffsetX, cy + halfBoundingH + bottomPadding - collisionSpaceOffsetY
}
func PolygonColliderBLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
wx, wy := PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
}
func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
}

View File

@ -3,13 +3,13 @@ package dnmshared
import (
"bytes"
"compress/zlib"
. "dnmshared/sharedprotos"
"encoding/base64"
"encoding/xml"
"errors"
"fmt"
"go.uber.org/zap"
"io/ioutil"
. "jsexport/battle"
"math"
"strconv"
"strings"
@ -175,8 +175,8 @@ func (l *TmxLayer) decodeBase64() ([]uint32, error) {
return gids, nil
}
type StrToVec2DListMap map[string]*Vec2DList
type StrToPolygon2DListMap map[string]*Polygon2DList
type StrToVec2DListMap map[string]([]*Vec2D)
type StrToPolygon2DListMap map[string]([]*Polygon2D)
func tmxPolylineToPolygon2D(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) {
if nil == targetPolyline {
@ -321,13 +321,11 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
theStrToPolygon2DListMap = gidBoundariesMap[globalGid]
}
var pThePolygon2DList *Polygon2DList
var pThePolygon2DList []*Polygon2D
if _, ok := theStrToPolygon2DListMap[key]; ok {
pThePolygon2DList = theStrToPolygon2DListMap[key]
} else {
pThePolygon2DList = &Polygon2DList{
Eles: make([]*Polygon2D, 0),
}
pThePolygon2DList = make([]*Polygon2D, 0)
theStrToPolygon2DListMap[key] = pThePolygon2DList
}
@ -335,7 +333,7 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
if nil != err {
panic(err)
}
pThePolygon2DList.Eles = append(pThePolygon2DList.Eles, thePolygon2DFromPolyline)
pThePolygon2DList = append(pThePolygon2DList, thePolygon2DFromPolyline)
}
}
return nil
@ -348,12 +346,10 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
for _, objGroup := range pTmxMapIns.ObjectGroups {
switch objGroup.Name {
case "PlayerStartingPos":
var pTheVec2DListToCache *Vec2DList
var pTheVec2DListToCache []*Vec2D
_, ok := toRetStrToVec2DListMap[objGroup.Name]
if false == ok {
pTheVec2DListToCache = &Vec2DList{
Eles: make([]*Vec2D, 0),
}
pTheVec2DListToCache = make([]*Vec2D, 0)
toRetStrToVec2DListMap[objGroup.Name] = pTheVec2DListToCache
}
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
@ -363,16 +359,14 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
Y: singleObjInTmxFile.Y,
}
thePosInWorld := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedPos)
pTheVec2DListToCache.Eles = append(pTheVec2DListToCache.Eles, &thePosInWorld)
pTheVec2DListToCache = append(pTheVec2DListToCache, &thePosInWorld)
}
case "Barrier":
// Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]".
var pThePolygon2DListToCache *Polygon2DList
var pThePolygon2DListToCache []*Polygon2D
_, ok := toRetStrToPolygon2DListMap[objGroup.Name]
if false == ok {
pThePolygon2DListToCache = &Polygon2DList{
Eles: make([]*Polygon2D, 0),
}
pThePolygon2DListToCache = make([]*Polygon2D, 0)
toRetStrToPolygon2DListMap[objGroup.Name] = pThePolygon2DListToCache
}
@ -408,7 +402,7 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
if nil != err {
panic(err)
}
pThePolygon2DListToCache.Eles = append(pThePolygon2DListToCache.Eles, thePolygon2DInWorld)
pThePolygon2DListToCache = append(pThePolygon2DListToCache, thePolygon2DInWorld)
}
default:
}

File diff suppressed because one or more lines are too long

View File

@ -79,10 +79,6 @@ message InputsBufferSnapshot {
bool shouldForceResync = 4;
}
message Barrier {
sharedprotos.Polygon2D boundary = 1;
}
message MeleeBullet {
// Jargon reference https://www.thegamer.com/fighting-games-frame-data-explained/
// ALL lengths are in world coordinate
@ -94,60 +90,62 @@ message MeleeBullet {
int32 recoveryFrames = 4;
int32 recoveryFramesOnBlock = 5;
int32 recoveryFramesOnHit = 6;
sharedprotos.Vec2D moveforward = 7;
double hitboxOffset = 8;
sharedprotos.Vec2D hitboxSize = 9;
int32 originatedRenderFrameId = 10;
double hitboxOffset = 7;
int32 originatedRenderFrameId = 8;
// for defender
int32 hitStunFrames = 11;
int32 blockStunFrames = 12;
double pushback = 13;
int32 hitStunFrames = 9;
int32 blockStunFrames = 10;
double pushback = 11;
int32 releaseTriggerType = 14; // 1: rising-edge, 2: falling-edge
int32 damage = 15;
int32 releaseTriggerType = 12; // 1: rising-edge, 2: falling-edge
int32 damage = 13;
int32 offenderJoinIndex = 16;
int32 offenderPlayerId = 17;
int32 offenderJoinIndex = 14;
int32 offenderPlayerId = 15;
double hitboxSizeX = 16;
double hitboxSizeY = 17;
double selfMoveforwardX = 18;
double selfMoveforwardY = 19;
}
message BattleColliderInfo {
string stageName = 1;
map<string, sharedprotos.Vec2DList> strToVec2DListMap = 2;
map<string, sharedprotos.Polygon2DList> strToPolygon2DListMap = 3;
int32 stageDiscreteW = 4;
int32 stageDiscreteH = 5;
int32 stageTileW = 6;
int32 stageTileH = 7;
int32 stageDiscreteW = 2;
int32 stageDiscreteH = 3;
int32 stageTileW = 4;
int32 stageTileH = 5;
int32 intervalToPing = 8;
int32 willKickIfInactiveFor = 9;
int32 boundRoomId = 10;
int32 battleDurationFrames = 12;
int64 battleDurationNanos = 13;
int32 serverFps = 14;
int32 inputDelayFrames = 15; // in the count of render frames
uint32 inputScaleFrames = 16; // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
int32 nstDelayFrames = 17; // 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"
int32 inputFrameUpsyncDelayTolerance = 18;
int32 maxChasingRenderFramesPerUpdate = 19;
int32 playerBattleState = 20;
double rollbackEstimatedDtMillis = 21;
int64 rollbackEstimatedDtNanos = 22;
int32 intervalToPing = 6;
int32 willKickIfInactiveFor = 7;
int32 boundRoomId = 8;
int32 battleDurationFrames = 9;
int64 battleDurationNanos = 10;
int32 serverFps = 11;
int32 inputDelayFrames = 12; // in the count of render frames
uint32 inputScaleFrames = 13; // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
int32 nstDelayFrames = 14; // 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"
int32 inputFrameUpsyncDelayTolerance = 15;
int32 maxChasingRenderFramesPerUpdate = 16;
int32 playerBattleState = 17;
double rollbackEstimatedDtMillis = 18;
int64 rollbackEstimatedDtNanos = 19;
double worldToVirtualGridRatio = 23;
double virtualGridToWorldRatio = 24;
double worldToVirtualGridRatio = 20;
double virtualGridToWorldRatio = 21;
int32 spAtkLookupFrames = 25;
int32 renderCacheSize = 26;
int32 spAtkLookupFrames = 22;
int32 renderCacheSize = 23;
map<int32, MeleeBullet> meleeSkillConfig = 27; // skillId -> skill
map<int32, MeleeBullet> meleeSkillConfig = 24; // skillId -> skill
double snapIntoPlatformOverlap = 28;
double snapIntoPlatformThreshold = 29;
int32 jumpingInitVelY = 30;
int32 gravityX = 31;
int32 gravityY = 32;
double snapIntoPlatformOverlap = 25;
double snapIntoPlatformThreshold = 26;
int32 jumpingInitVelY = 27;
int32 gravityX = 28;
int32 gravityY = 29;
}
message RoomDownsyncFrame {

View File

@ -294,11 +294,9 @@ cc.Class({
throw `Failed to get cached delayedInputFrame for i=${i}, j=${j}, renderFrameId=${self.renderFrameId}, lastUpsyncInputFrameId=${self.lastUpsyncInputFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, chaserRenderFrameId=${self.chaserRenderFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`;
}
const delayedInputFrameJs = gopkgs.NewInputFrameDownsyncJs(j, delayedInputFrame.inputList, delayedInputFrame.confirmedList);
const jPrev = self._convertToInputFrameId(i - 1, self.inputDelayFrames);
const delayedInputFrameForPrevRenderFrame = self.recentInputCache.getByFrameId(jPrev);
const delayedInputFrameForPrevRenderFrameJs = gopkgs.NewInputFrameDownsyncJs(jPrev, delayedInputFrameForPrevRenderFrame.inputList, delayedInputFrameForPrevRenderFrame.confirmedList);
const nextRdf = gopkgs.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(delayedInputFrameJs, delayedInputFrameForPrevRenderFrameJs, currRdf, collisionSys, collisionSysMap, self.gravityX, self.gravityY, self.jumpingInitVelY, self.inputDelayFrames, self.inputScaleFrames, self.spaceOffsetX, self.spaceOffsetY, self.snapIntoPlatformOverlap, self.snapIntoPlatformThreshold, self.worldToVirtualGridRatio, self.virtualGridToWorldRatio);
const nextRdf = gopkgs.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(delayedInputFrame.inputList, (null == delayedInputFrameForPrevRenderFrame ? null : delayedInputFrameForPrevRenderFrame.inputList), currRdf, collisionSys, collisionSysMap, self.gravityX, self.gravityY, self.jumpingInitVelY, self.inputDelayFrames, self.inputScaleFrames, self.spaceOffsetX, self.spaceOffsetY, self.snapIntoPlatformOverlap, self.snapIntoPlatformThreshold, self.worldToVirtualGridRatio, self.virtualGridToWorldRatio);
if (true == isChasing) {
// [WARNING] Move the cursor "self.chaserRenderFrameId" when "true == isChasing", keep in mind that "self.chaserRenderFrameId" is not monotonic!

View File

@ -3954,214 +3954,6 @@ $root.protos = (function() {
return InputsBufferSnapshot;
})();
protos.Barrier = (function() {
/**
* Properties of a Barrier.
* @memberof protos
* @interface IBarrier
* @property {sharedprotos.Polygon2D|null} [boundary] Barrier boundary
*/
/**
* Constructs a new Barrier.
* @memberof protos
* @classdesc Represents a Barrier.
* @implements IBarrier
* @constructor
* @param {protos.IBarrier=} [properties] Properties to set
*/
function Barrier(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]];
}
/**
* Barrier boundary.
* @member {sharedprotos.Polygon2D|null|undefined} boundary
* @memberof protos.Barrier
* @instance
*/
Barrier.prototype.boundary = null;
/**
* Creates a new Barrier instance using the specified properties.
* @function create
* @memberof protos.Barrier
* @static
* @param {protos.IBarrier=} [properties] Properties to set
* @returns {protos.Barrier} Barrier instance
*/
Barrier.create = function create(properties) {
return new Barrier(properties);
};
/**
* Encodes the specified Barrier message. Does not implicitly {@link protos.Barrier.verify|verify} messages.
* @function encode
* @memberof protos.Barrier
* @static
* @param {protos.Barrier} message Barrier message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
Barrier.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
if (message.boundary != null && Object.hasOwnProperty.call(message, "boundary"))
$root.sharedprotos.Polygon2D.encode(message.boundary, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim();
return writer;
};
/**
* Encodes the specified Barrier message, length delimited. Does not implicitly {@link protos.Barrier.verify|verify} messages.
* @function encodeDelimited
* @memberof protos.Barrier
* @static
* @param {protos.Barrier} message Barrier message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
Barrier.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim();
};
/**
* Decodes a Barrier message from the specified reader or buffer.
* @function decode
* @memberof protos.Barrier
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand
* @returns {protos.Barrier} Barrier
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
Barrier.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.protos.Barrier();
while (reader.pos < end) {
var tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
message.boundary = $root.sharedprotos.Polygon2D.decode(reader, reader.uint32());
break;
}
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
* Decodes a Barrier message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof protos.Barrier
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {protos.Barrier} Barrier
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
Barrier.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader))
reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
* Verifies a Barrier message.
* @function verify
* @memberof protos.Barrier
* @static
* @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not
*/
Barrier.verify = function verify(message) {
if (typeof message !== "object" || message === null)
return "object expected";
if (message.boundary != null && message.hasOwnProperty("boundary")) {
var error = $root.sharedprotos.Polygon2D.verify(message.boundary);
if (error)
return "boundary." + error;
}
return null;
};
/**
* Creates a Barrier message from a plain object. Also converts values to their respective internal types.
* @function fromObject
* @memberof protos.Barrier
* @static
* @param {Object.<string,*>} object Plain object
* @returns {protos.Barrier} Barrier
*/
Barrier.fromObject = function fromObject(object) {
if (object instanceof $root.protos.Barrier)
return object;
var message = new $root.protos.Barrier();
if (object.boundary != null) {
if (typeof object.boundary !== "object")
throw TypeError(".protos.Barrier.boundary: object expected");
message.boundary = $root.sharedprotos.Polygon2D.fromObject(object.boundary);
}
return message;
};
/**
* Creates a plain object from a Barrier message. Also converts values to other types if specified.
* @function toObject
* @memberof protos.Barrier
* @static
* @param {protos.Barrier} message Barrier
* @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object
*/
Barrier.toObject = function toObject(message, options) {
if (!options)
options = {};
var object = {};
if (options.defaults)
object.boundary = null;
if (message.boundary != null && message.hasOwnProperty("boundary"))
object.boundary = $root.sharedprotos.Polygon2D.toObject(message.boundary, options);
return object;
};
/**
* Converts this Barrier to JSON.
* @function toJSON
* @memberof protos.Barrier
* @instance
* @returns {Object.<string,*>} JSON object
*/
Barrier.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
/**
* Gets the default type url for Barrier
* @function getTypeUrl
* @memberof protos.Barrier
* @static
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns {string} The default type url
*/
Barrier.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === undefined) {
typeUrlPrefix = "type.googleapis.com";
}
return typeUrlPrefix + "/protos.Barrier";
};
return Barrier;
})();
protos.MeleeBullet = (function() {
/**
@ -4174,9 +3966,7 @@ $root.protos = (function() {
* @property {number|null} [recoveryFrames] MeleeBullet recoveryFrames
* @property {number|null} [recoveryFramesOnBlock] MeleeBullet recoveryFramesOnBlock
* @property {number|null} [recoveryFramesOnHit] MeleeBullet recoveryFramesOnHit
* @property {sharedprotos.Vec2D|null} [moveforward] MeleeBullet moveforward
* @property {number|null} [hitboxOffset] MeleeBullet hitboxOffset
* @property {sharedprotos.Vec2D|null} [hitboxSize] MeleeBullet hitboxSize
* @property {number|null} [originatedRenderFrameId] MeleeBullet originatedRenderFrameId
* @property {number|null} [hitStunFrames] MeleeBullet hitStunFrames
* @property {number|null} [blockStunFrames] MeleeBullet blockStunFrames
@ -4185,6 +3975,10 @@ $root.protos = (function() {
* @property {number|null} [damage] MeleeBullet damage
* @property {number|null} [offenderJoinIndex] MeleeBullet offenderJoinIndex
* @property {number|null} [offenderPlayerId] MeleeBullet offenderPlayerId
* @property {number|null} [hitboxSizeX] MeleeBullet hitboxSizeX
* @property {number|null} [hitboxSizeY] MeleeBullet hitboxSizeY
* @property {number|null} [selfMoveforwardX] MeleeBullet selfMoveforwardX
* @property {number|null} [selfMoveforwardY] MeleeBullet selfMoveforwardY
*/
/**
@ -4250,14 +4044,6 @@ $root.protos = (function() {
*/
MeleeBullet.prototype.recoveryFramesOnHit = 0;
/**
* MeleeBullet moveforward.
* @member {sharedprotos.Vec2D|null|undefined} moveforward
* @memberof protos.MeleeBullet
* @instance
*/
MeleeBullet.prototype.moveforward = null;
/**
* MeleeBullet hitboxOffset.
* @member {number} hitboxOffset
@ -4266,14 +4052,6 @@ $root.protos = (function() {
*/
MeleeBullet.prototype.hitboxOffset = 0;
/**
* MeleeBullet hitboxSize.
* @member {sharedprotos.Vec2D|null|undefined} hitboxSize
* @memberof protos.MeleeBullet
* @instance
*/
MeleeBullet.prototype.hitboxSize = null;
/**
* MeleeBullet originatedRenderFrameId.
* @member {number} originatedRenderFrameId
@ -4338,6 +4116,38 @@ $root.protos = (function() {
*/
MeleeBullet.prototype.offenderPlayerId = 0;
/**
* MeleeBullet hitboxSizeX.
* @member {number} hitboxSizeX
* @memberof protos.MeleeBullet
* @instance
*/
MeleeBullet.prototype.hitboxSizeX = 0;
/**
* MeleeBullet hitboxSizeY.
* @member {number} hitboxSizeY
* @memberof protos.MeleeBullet
* @instance
*/
MeleeBullet.prototype.hitboxSizeY = 0;
/**
* MeleeBullet selfMoveforwardX.
* @member {number} selfMoveforwardX
* @memberof protos.MeleeBullet
* @instance
*/
MeleeBullet.prototype.selfMoveforwardX = 0;
/**
* MeleeBullet selfMoveforwardY.
* @member {number} selfMoveforwardY
* @memberof protos.MeleeBullet
* @instance
*/
MeleeBullet.prototype.selfMoveforwardY = 0;
/**
* Creates a new MeleeBullet instance using the specified properties.
* @function create
@ -4374,28 +4184,32 @@ $root.protos = (function() {
writer.uint32(/* id 5, wireType 0 =*/40).int32(message.recoveryFramesOnBlock);
if (message.recoveryFramesOnHit != null && Object.hasOwnProperty.call(message, "recoveryFramesOnHit"))
writer.uint32(/* id 6, wireType 0 =*/48).int32(message.recoveryFramesOnHit);
if (message.moveforward != null && Object.hasOwnProperty.call(message, "moveforward"))
$root.sharedprotos.Vec2D.encode(message.moveforward, writer.uint32(/* id 7, wireType 2 =*/58).fork()).ldelim();
if (message.hitboxOffset != null && Object.hasOwnProperty.call(message, "hitboxOffset"))
writer.uint32(/* id 8, wireType 1 =*/65).double(message.hitboxOffset);
if (message.hitboxSize != null && Object.hasOwnProperty.call(message, "hitboxSize"))
$root.sharedprotos.Vec2D.encode(message.hitboxSize, writer.uint32(/* id 9, wireType 2 =*/74).fork()).ldelim();
writer.uint32(/* id 7, wireType 1 =*/57).double(message.hitboxOffset);
if (message.originatedRenderFrameId != null && Object.hasOwnProperty.call(message, "originatedRenderFrameId"))
writer.uint32(/* id 10, wireType 0 =*/80).int32(message.originatedRenderFrameId);
writer.uint32(/* id 8, wireType 0 =*/64).int32(message.originatedRenderFrameId);
if (message.hitStunFrames != null && Object.hasOwnProperty.call(message, "hitStunFrames"))
writer.uint32(/* id 11, wireType 0 =*/88).int32(message.hitStunFrames);
writer.uint32(/* id 9, wireType 0 =*/72).int32(message.hitStunFrames);
if (message.blockStunFrames != null && Object.hasOwnProperty.call(message, "blockStunFrames"))
writer.uint32(/* id 12, wireType 0 =*/96).int32(message.blockStunFrames);
writer.uint32(/* id 10, wireType 0 =*/80).int32(message.blockStunFrames);
if (message.pushback != null && Object.hasOwnProperty.call(message, "pushback"))
writer.uint32(/* id 13, wireType 1 =*/105).double(message.pushback);
writer.uint32(/* id 11, wireType 1 =*/89).double(message.pushback);
if (message.releaseTriggerType != null && Object.hasOwnProperty.call(message, "releaseTriggerType"))
writer.uint32(/* id 14, wireType 0 =*/112).int32(message.releaseTriggerType);
writer.uint32(/* id 12, wireType 0 =*/96).int32(message.releaseTriggerType);
if (message.damage != null && Object.hasOwnProperty.call(message, "damage"))
writer.uint32(/* id 15, wireType 0 =*/120).int32(message.damage);
writer.uint32(/* id 13, wireType 0 =*/104).int32(message.damage);
if (message.offenderJoinIndex != null && Object.hasOwnProperty.call(message, "offenderJoinIndex"))
writer.uint32(/* id 16, wireType 0 =*/128).int32(message.offenderJoinIndex);
writer.uint32(/* id 14, wireType 0 =*/112).int32(message.offenderJoinIndex);
if (message.offenderPlayerId != null && Object.hasOwnProperty.call(message, "offenderPlayerId"))
writer.uint32(/* id 17, wireType 0 =*/136).int32(message.offenderPlayerId);
writer.uint32(/* id 15, wireType 0 =*/120).int32(message.offenderPlayerId);
if (message.hitboxSizeX != null && Object.hasOwnProperty.call(message, "hitboxSizeX"))
writer.uint32(/* id 16, wireType 1 =*/129).double(message.hitboxSizeX);
if (message.hitboxSizeY != null && Object.hasOwnProperty.call(message, "hitboxSizeY"))
writer.uint32(/* id 17, wireType 1 =*/137).double(message.hitboxSizeY);
if (message.selfMoveforwardX != null && Object.hasOwnProperty.call(message, "selfMoveforwardX"))
writer.uint32(/* id 18, wireType 1 =*/145).double(message.selfMoveforwardX);
if (message.selfMoveforwardY != null && Object.hasOwnProperty.call(message, "selfMoveforwardY"))
writer.uint32(/* id 19, wireType 1 =*/153).double(message.selfMoveforwardY);
return writer;
};
@ -4455,49 +4269,57 @@ $root.protos = (function() {
break;
}
case 7: {
message.moveforward = $root.sharedprotos.Vec2D.decode(reader, reader.uint32());
break;
}
case 8: {
message.hitboxOffset = reader.double();
break;
}
case 9: {
message.hitboxSize = $root.sharedprotos.Vec2D.decode(reader, reader.uint32());
break;
}
case 10: {
case 8: {
message.originatedRenderFrameId = reader.int32();
break;
}
case 11: {
case 9: {
message.hitStunFrames = reader.int32();
break;
}
case 12: {
case 10: {
message.blockStunFrames = reader.int32();
break;
}
case 13: {
case 11: {
message.pushback = reader.double();
break;
}
case 14: {
case 12: {
message.releaseTriggerType = reader.int32();
break;
}
case 15: {
case 13: {
message.damage = reader.int32();
break;
}
case 16: {
case 14: {
message.offenderJoinIndex = reader.int32();
break;
}
case 17: {
case 15: {
message.offenderPlayerId = reader.int32();
break;
}
case 16: {
message.hitboxSizeX = reader.double();
break;
}
case 17: {
message.hitboxSizeY = reader.double();
break;
}
case 18: {
message.selfMoveforwardX = reader.double();
break;
}
case 19: {
message.selfMoveforwardY = reader.double();
break;
}
default:
reader.skipType(tag & 7);
break;
@ -4551,19 +4373,9 @@ $root.protos = (function() {
if (message.recoveryFramesOnHit != null && message.hasOwnProperty("recoveryFramesOnHit"))
if (!$util.isInteger(message.recoveryFramesOnHit))
return "recoveryFramesOnHit: integer expected";
if (message.moveforward != null && message.hasOwnProperty("moveforward")) {
var error = $root.sharedprotos.Vec2D.verify(message.moveforward);
if (error)
return "moveforward." + error;
}
if (message.hitboxOffset != null && message.hasOwnProperty("hitboxOffset"))
if (typeof message.hitboxOffset !== "number")
return "hitboxOffset: number expected";
if (message.hitboxSize != null && message.hasOwnProperty("hitboxSize")) {
var error = $root.sharedprotos.Vec2D.verify(message.hitboxSize);
if (error)
return "hitboxSize." + error;
}
if (message.originatedRenderFrameId != null && message.hasOwnProperty("originatedRenderFrameId"))
if (!$util.isInteger(message.originatedRenderFrameId))
return "originatedRenderFrameId: integer expected";
@ -4588,6 +4400,18 @@ $root.protos = (function() {
if (message.offenderPlayerId != null && message.hasOwnProperty("offenderPlayerId"))
if (!$util.isInteger(message.offenderPlayerId))
return "offenderPlayerId: integer expected";
if (message.hitboxSizeX != null && message.hasOwnProperty("hitboxSizeX"))
if (typeof message.hitboxSizeX !== "number")
return "hitboxSizeX: number expected";
if (message.hitboxSizeY != null && message.hasOwnProperty("hitboxSizeY"))
if (typeof message.hitboxSizeY !== "number")
return "hitboxSizeY: number expected";
if (message.selfMoveforwardX != null && message.hasOwnProperty("selfMoveforwardX"))
if (typeof message.selfMoveforwardX !== "number")
return "selfMoveforwardX: number expected";
if (message.selfMoveforwardY != null && message.hasOwnProperty("selfMoveforwardY"))
if (typeof message.selfMoveforwardY !== "number")
return "selfMoveforwardY: number expected";
return null;
};
@ -4615,18 +4439,8 @@ $root.protos = (function() {
message.recoveryFramesOnBlock = object.recoveryFramesOnBlock | 0;
if (object.recoveryFramesOnHit != null)
message.recoveryFramesOnHit = object.recoveryFramesOnHit | 0;
if (object.moveforward != null) {
if (typeof object.moveforward !== "object")
throw TypeError(".protos.MeleeBullet.moveforward: object expected");
message.moveforward = $root.sharedprotos.Vec2D.fromObject(object.moveforward);
}
if (object.hitboxOffset != null)
message.hitboxOffset = Number(object.hitboxOffset);
if (object.hitboxSize != null) {
if (typeof object.hitboxSize !== "object")
throw TypeError(".protos.MeleeBullet.hitboxSize: object expected");
message.hitboxSize = $root.sharedprotos.Vec2D.fromObject(object.hitboxSize);
}
if (object.originatedRenderFrameId != null)
message.originatedRenderFrameId = object.originatedRenderFrameId | 0;
if (object.hitStunFrames != null)
@ -4643,6 +4457,14 @@ $root.protos = (function() {
message.offenderJoinIndex = object.offenderJoinIndex | 0;
if (object.offenderPlayerId != null)
message.offenderPlayerId = object.offenderPlayerId | 0;
if (object.hitboxSizeX != null)
message.hitboxSizeX = Number(object.hitboxSizeX);
if (object.hitboxSizeY != null)
message.hitboxSizeY = Number(object.hitboxSizeY);
if (object.selfMoveforwardX != null)
message.selfMoveforwardX = Number(object.selfMoveforwardX);
if (object.selfMoveforwardY != null)
message.selfMoveforwardY = Number(object.selfMoveforwardY);
return message;
};
@ -4666,9 +4488,7 @@ $root.protos = (function() {
object.recoveryFrames = 0;
object.recoveryFramesOnBlock = 0;
object.recoveryFramesOnHit = 0;
object.moveforward = null;
object.hitboxOffset = 0;
object.hitboxSize = null;
object.originatedRenderFrameId = 0;
object.hitStunFrames = 0;
object.blockStunFrames = 0;
@ -4677,6 +4497,10 @@ $root.protos = (function() {
object.damage = 0;
object.offenderJoinIndex = 0;
object.offenderPlayerId = 0;
object.hitboxSizeX = 0;
object.hitboxSizeY = 0;
object.selfMoveforwardX = 0;
object.selfMoveforwardY = 0;
}
if (message.battleLocalId != null && message.hasOwnProperty("battleLocalId"))
object.battleLocalId = message.battleLocalId;
@ -4690,12 +4514,8 @@ $root.protos = (function() {
object.recoveryFramesOnBlock = message.recoveryFramesOnBlock;
if (message.recoveryFramesOnHit != null && message.hasOwnProperty("recoveryFramesOnHit"))
object.recoveryFramesOnHit = message.recoveryFramesOnHit;
if (message.moveforward != null && message.hasOwnProperty("moveforward"))
object.moveforward = $root.sharedprotos.Vec2D.toObject(message.moveforward, options);
if (message.hitboxOffset != null && message.hasOwnProperty("hitboxOffset"))
object.hitboxOffset = options.json && !isFinite(message.hitboxOffset) ? String(message.hitboxOffset) : message.hitboxOffset;
if (message.hitboxSize != null && message.hasOwnProperty("hitboxSize"))
object.hitboxSize = $root.sharedprotos.Vec2D.toObject(message.hitboxSize, options);
if (message.originatedRenderFrameId != null && message.hasOwnProperty("originatedRenderFrameId"))
object.originatedRenderFrameId = message.originatedRenderFrameId;
if (message.hitStunFrames != null && message.hasOwnProperty("hitStunFrames"))
@ -4712,6 +4532,14 @@ $root.protos = (function() {
object.offenderJoinIndex = message.offenderJoinIndex;
if (message.offenderPlayerId != null && message.hasOwnProperty("offenderPlayerId"))
object.offenderPlayerId = message.offenderPlayerId;
if (message.hitboxSizeX != null && message.hasOwnProperty("hitboxSizeX"))
object.hitboxSizeX = options.json && !isFinite(message.hitboxSizeX) ? String(message.hitboxSizeX) : message.hitboxSizeX;
if (message.hitboxSizeY != null && message.hasOwnProperty("hitboxSizeY"))
object.hitboxSizeY = options.json && !isFinite(message.hitboxSizeY) ? String(message.hitboxSizeY) : message.hitboxSizeY;
if (message.selfMoveforwardX != null && message.hasOwnProperty("selfMoveforwardX"))
object.selfMoveforwardX = options.json && !isFinite(message.selfMoveforwardX) ? String(message.selfMoveforwardX) : message.selfMoveforwardX;
if (message.selfMoveforwardY != null && message.hasOwnProperty("selfMoveforwardY"))
object.selfMoveforwardY = options.json && !isFinite(message.selfMoveforwardY) ? String(message.selfMoveforwardY) : message.selfMoveforwardY;
return object;
};
@ -4751,8 +4579,6 @@ $root.protos = (function() {
* @memberof protos
* @interface IBattleColliderInfo
* @property {string|null} [stageName] BattleColliderInfo stageName
* @property {Object.<string,sharedprotos.Vec2DList>|null} [strToVec2DListMap] BattleColliderInfo strToVec2DListMap
* @property {Object.<string,sharedprotos.Polygon2DList>|null} [strToPolygon2DListMap] BattleColliderInfo strToPolygon2DListMap
* @property {number|null} [stageDiscreteW] BattleColliderInfo stageDiscreteW
* @property {number|null} [stageDiscreteH] BattleColliderInfo stageDiscreteH
* @property {number|null} [stageTileW] BattleColliderInfo stageTileW
@ -4792,8 +4618,6 @@ $root.protos = (function() {
* @param {protos.IBattleColliderInfo=} [properties] Properties to set
*/
function BattleColliderInfo(properties) {
this.strToVec2DListMap = {};
this.strToPolygon2DListMap = {};
this.meleeSkillConfig = {};
if (properties)
for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i)
@ -4809,22 +4633,6 @@ $root.protos = (function() {
*/
BattleColliderInfo.prototype.stageName = "";
/**
* BattleColliderInfo strToVec2DListMap.
* @member {Object.<string,sharedprotos.Vec2DList>} strToVec2DListMap
* @memberof protos.BattleColliderInfo
* @instance
*/
BattleColliderInfo.prototype.strToVec2DListMap = $util.emptyObject;
/**
* BattleColliderInfo strToPolygon2DListMap.
* @member {Object.<string,sharedprotos.Polygon2DList>} strToPolygon2DListMap
* @memberof protos.BattleColliderInfo
* @instance
*/
BattleColliderInfo.prototype.strToPolygon2DListMap = $util.emptyObject;
/**
* BattleColliderInfo stageDiscreteW.
* @member {number} stageDiscreteW
@ -5075,75 +4883,65 @@ $root.protos = (function() {
writer = $Writer.create();
if (message.stageName != null && Object.hasOwnProperty.call(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 2, wireType 2 =*/18).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]);
$root.sharedprotos.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 3, wireType 2 =*/26).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]);
$root.sharedprotos.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 4, wireType 0 =*/32).int32(message.stageDiscreteW);
writer.uint32(/* id 2, wireType 0 =*/16).int32(message.stageDiscreteW);
if (message.stageDiscreteH != null && Object.hasOwnProperty.call(message, "stageDiscreteH"))
writer.uint32(/* id 5, wireType 0 =*/40).int32(message.stageDiscreteH);
writer.uint32(/* id 3, wireType 0 =*/24).int32(message.stageDiscreteH);
if (message.stageTileW != null && Object.hasOwnProperty.call(message, "stageTileW"))
writer.uint32(/* id 6, wireType 0 =*/48).int32(message.stageTileW);
writer.uint32(/* id 4, wireType 0 =*/32).int32(message.stageTileW);
if (message.stageTileH != null && Object.hasOwnProperty.call(message, "stageTileH"))
writer.uint32(/* id 7, wireType 0 =*/56).int32(message.stageTileH);
writer.uint32(/* id 5, wireType 0 =*/40).int32(message.stageTileH);
if (message.intervalToPing != null && Object.hasOwnProperty.call(message, "intervalToPing"))
writer.uint32(/* id 8, wireType 0 =*/64).int32(message.intervalToPing);
writer.uint32(/* id 6, wireType 0 =*/48).int32(message.intervalToPing);
if (message.willKickIfInactiveFor != null && Object.hasOwnProperty.call(message, "willKickIfInactiveFor"))
writer.uint32(/* id 9, wireType 0 =*/72).int32(message.willKickIfInactiveFor);
writer.uint32(/* id 7, wireType 0 =*/56).int32(message.willKickIfInactiveFor);
if (message.boundRoomId != null && Object.hasOwnProperty.call(message, "boundRoomId"))
writer.uint32(/* id 10, wireType 0 =*/80).int32(message.boundRoomId);
writer.uint32(/* id 8, wireType 0 =*/64).int32(message.boundRoomId);
if (message.battleDurationFrames != null && Object.hasOwnProperty.call(message, "battleDurationFrames"))
writer.uint32(/* id 12, wireType 0 =*/96).int32(message.battleDurationFrames);
writer.uint32(/* id 9, wireType 0 =*/72).int32(message.battleDurationFrames);
if (message.battleDurationNanos != null && Object.hasOwnProperty.call(message, "battleDurationNanos"))
writer.uint32(/* id 13, wireType 0 =*/104).int64(message.battleDurationNanos);
writer.uint32(/* id 10, wireType 0 =*/80).int64(message.battleDurationNanos);
if (message.serverFps != null && Object.hasOwnProperty.call(message, "serverFps"))
writer.uint32(/* id 14, wireType 0 =*/112).int32(message.serverFps);
writer.uint32(/* id 11, wireType 0 =*/88).int32(message.serverFps);
if (message.inputDelayFrames != null && Object.hasOwnProperty.call(message, "inputDelayFrames"))
writer.uint32(/* id 15, wireType 0 =*/120).int32(message.inputDelayFrames);
writer.uint32(/* id 12, wireType 0 =*/96).int32(message.inputDelayFrames);
if (message.inputScaleFrames != null && Object.hasOwnProperty.call(message, "inputScaleFrames"))
writer.uint32(/* id 16, wireType 0 =*/128).uint32(message.inputScaleFrames);
writer.uint32(/* id 13, wireType 0 =*/104).uint32(message.inputScaleFrames);
if (message.nstDelayFrames != null && Object.hasOwnProperty.call(message, "nstDelayFrames"))
writer.uint32(/* id 17, wireType 0 =*/136).int32(message.nstDelayFrames);
writer.uint32(/* id 14, wireType 0 =*/112).int32(message.nstDelayFrames);
if (message.inputFrameUpsyncDelayTolerance != null && Object.hasOwnProperty.call(message, "inputFrameUpsyncDelayTolerance"))
writer.uint32(/* id 18, wireType 0 =*/144).int32(message.inputFrameUpsyncDelayTolerance);
writer.uint32(/* id 15, wireType 0 =*/120).int32(message.inputFrameUpsyncDelayTolerance);
if (message.maxChasingRenderFramesPerUpdate != null && Object.hasOwnProperty.call(message, "maxChasingRenderFramesPerUpdate"))
writer.uint32(/* id 19, wireType 0 =*/152).int32(message.maxChasingRenderFramesPerUpdate);
writer.uint32(/* id 16, wireType 0 =*/128).int32(message.maxChasingRenderFramesPerUpdate);
if (message.playerBattleState != null && Object.hasOwnProperty.call(message, "playerBattleState"))
writer.uint32(/* id 20, wireType 0 =*/160).int32(message.playerBattleState);
writer.uint32(/* id 17, wireType 0 =*/136).int32(message.playerBattleState);
if (message.rollbackEstimatedDtMillis != null && Object.hasOwnProperty.call(message, "rollbackEstimatedDtMillis"))
writer.uint32(/* id 21, wireType 1 =*/169).double(message.rollbackEstimatedDtMillis);
writer.uint32(/* id 18, wireType 1 =*/145).double(message.rollbackEstimatedDtMillis);
if (message.rollbackEstimatedDtNanos != null && Object.hasOwnProperty.call(message, "rollbackEstimatedDtNanos"))
writer.uint32(/* id 22, wireType 0 =*/176).int64(message.rollbackEstimatedDtNanos);
writer.uint32(/* id 19, wireType 0 =*/152).int64(message.rollbackEstimatedDtNanos);
if (message.worldToVirtualGridRatio != null && Object.hasOwnProperty.call(message, "worldToVirtualGridRatio"))
writer.uint32(/* id 23, wireType 1 =*/185).double(message.worldToVirtualGridRatio);
writer.uint32(/* id 20, wireType 1 =*/161).double(message.worldToVirtualGridRatio);
if (message.virtualGridToWorldRatio != null && Object.hasOwnProperty.call(message, "virtualGridToWorldRatio"))
writer.uint32(/* id 24, wireType 1 =*/193).double(message.virtualGridToWorldRatio);
writer.uint32(/* id 21, wireType 1 =*/169).double(message.virtualGridToWorldRatio);
if (message.spAtkLookupFrames != null && Object.hasOwnProperty.call(message, "spAtkLookupFrames"))
writer.uint32(/* id 25, wireType 0 =*/200).int32(message.spAtkLookupFrames);
writer.uint32(/* id 22, wireType 0 =*/176).int32(message.spAtkLookupFrames);
if (message.renderCacheSize != null && Object.hasOwnProperty.call(message, "renderCacheSize"))
writer.uint32(/* id 26, wireType 0 =*/208).int32(message.renderCacheSize);
writer.uint32(/* id 23, wireType 0 =*/184).int32(message.renderCacheSize);
if (message.meleeSkillConfig != null && Object.hasOwnProperty.call(message, "meleeSkillConfig"))
for (var keys = Object.keys(message.meleeSkillConfig), i = 0; i < keys.length; ++i) {
writer.uint32(/* id 27, wireType 2 =*/218).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
writer.uint32(/* id 24, wireType 2 =*/194).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
$root.protos.MeleeBullet.encode(message.meleeSkillConfig[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
}
if (message.snapIntoPlatformOverlap != null && Object.hasOwnProperty.call(message, "snapIntoPlatformOverlap"))
writer.uint32(/* id 28, wireType 1 =*/225).double(message.snapIntoPlatformOverlap);
writer.uint32(/* id 25, wireType 1 =*/201).double(message.snapIntoPlatformOverlap);
if (message.snapIntoPlatformThreshold != null && Object.hasOwnProperty.call(message, "snapIntoPlatformThreshold"))
writer.uint32(/* id 29, wireType 1 =*/233).double(message.snapIntoPlatformThreshold);
writer.uint32(/* id 26, wireType 1 =*/209).double(message.snapIntoPlatformThreshold);
if (message.jumpingInitVelY != null && Object.hasOwnProperty.call(message, "jumpingInitVelY"))
writer.uint32(/* id 30, wireType 0 =*/240).int32(message.jumpingInitVelY);
writer.uint32(/* id 27, wireType 0 =*/216).int32(message.jumpingInitVelY);
if (message.gravityX != null && Object.hasOwnProperty.call(message, "gravityX"))
writer.uint32(/* id 31, wireType 0 =*/248).int32(message.gravityX);
writer.uint32(/* id 28, wireType 0 =*/224).int32(message.gravityX);
if (message.gravityY != null && Object.hasOwnProperty.call(message, "gravityY"))
writer.uint32(/* id 32, wireType 0 =*/256).int32(message.gravityY);
writer.uint32(/* id 29, wireType 0 =*/232).int32(message.gravityY);
return writer;
};
@ -5183,140 +4981,94 @@ $root.protos = (function() {
break;
}
case 2: {
if (message.strToVec2DListMap === $util.emptyObject)
message.strToVec2DListMap = {};
var end2 = reader.uint32() + reader.pos;
key = "";
value = null;
while (reader.pos < end2) {
var tag2 = reader.uint32();
switch (tag2 >>> 3) {
case 1:
key = reader.string();
break;
case 2:
value = $root.sharedprotos.Vec2DList.decode(reader, reader.uint32());
break;
default:
reader.skipType(tag2 & 7);
break;
}
}
message.strToVec2DListMap[key] = value;
break;
}
case 3: {
if (message.strToPolygon2DListMap === $util.emptyObject)
message.strToPolygon2DListMap = {};
var end2 = reader.uint32() + reader.pos;
key = "";
value = null;
while (reader.pos < end2) {
var tag2 = reader.uint32();
switch (tag2 >>> 3) {
case 1:
key = reader.string();
break;
case 2:
value = $root.sharedprotos.Polygon2DList.decode(reader, reader.uint32());
break;
default:
reader.skipType(tag2 & 7);
break;
}
}
message.strToPolygon2DListMap[key] = value;
break;
}
case 4: {
message.stageDiscreteW = reader.int32();
break;
}
case 5: {
case 3: {
message.stageDiscreteH = reader.int32();
break;
}
case 6: {
case 4: {
message.stageTileW = reader.int32();
break;
}
case 7: {
case 5: {
message.stageTileH = reader.int32();
break;
}
case 8: {
case 6: {
message.intervalToPing = reader.int32();
break;
}
case 9: {
case 7: {
message.willKickIfInactiveFor = reader.int32();
break;
}
case 10: {
case 8: {
message.boundRoomId = reader.int32();
break;
}
case 12: {
case 9: {
message.battleDurationFrames = reader.int32();
break;
}
case 13: {
case 10: {
message.battleDurationNanos = reader.int64();
break;
}
case 14: {
case 11: {
message.serverFps = reader.int32();
break;
}
case 15: {
case 12: {
message.inputDelayFrames = reader.int32();
break;
}
case 16: {
case 13: {
message.inputScaleFrames = reader.uint32();
break;
}
case 17: {
case 14: {
message.nstDelayFrames = reader.int32();
break;
}
case 18: {
case 15: {
message.inputFrameUpsyncDelayTolerance = reader.int32();
break;
}
case 19: {
case 16: {
message.maxChasingRenderFramesPerUpdate = reader.int32();
break;
}
case 20: {
case 17: {
message.playerBattleState = reader.int32();
break;
}
case 21: {
case 18: {
message.rollbackEstimatedDtMillis = reader.double();
break;
}
case 22: {
case 19: {
message.rollbackEstimatedDtNanos = reader.int64();
break;
}
case 23: {
case 20: {
message.worldToVirtualGridRatio = reader.double();
break;
}
case 24: {
case 21: {
message.virtualGridToWorldRatio = reader.double();
break;
}
case 25: {
case 22: {
message.spAtkLookupFrames = reader.int32();
break;
}
case 26: {
case 23: {
message.renderCacheSize = reader.int32();
break;
}
case 27: {
case 24: {
if (message.meleeSkillConfig === $util.emptyObject)
message.meleeSkillConfig = {};
var end2 = reader.uint32() + reader.pos;
@ -5339,23 +5091,23 @@ $root.protos = (function() {
message.meleeSkillConfig[key] = value;
break;
}
case 28: {
case 25: {
message.snapIntoPlatformOverlap = reader.double();
break;
}
case 29: {
case 26: {
message.snapIntoPlatformThreshold = reader.double();
break;
}
case 30: {
case 27: {
message.jumpingInitVelY = reader.int32();
break;
}
case 31: {
case 28: {
message.gravityX = reader.int32();
break;
}
case 32: {
case 29: {
message.gravityY = reader.int32();
break;
}
@ -5397,26 +5149,6 @@ $root.protos = (function() {
if (message.stageName != null && message.hasOwnProperty("stageName"))
if (!$util.isString(message.stageName))
return "stageName: string expected";
if (message.strToVec2DListMap != null && message.hasOwnProperty("strToVec2DListMap")) {
if (!$util.isObject(message.strToVec2DListMap))
return "strToVec2DListMap: object expected";
var key = Object.keys(message.strToVec2DListMap);
for (var i = 0; i < key.length; ++i) {
var error = $root.sharedprotos.Vec2DList.verify(message.strToVec2DListMap[key[i]]);
if (error)
return "strToVec2DListMap." + error;
}
}
if (message.strToPolygon2DListMap != null && message.hasOwnProperty("strToPolygon2DListMap")) {
if (!$util.isObject(message.strToPolygon2DListMap))
return "strToPolygon2DListMap: object expected";
var key = Object.keys(message.strToPolygon2DListMap);
for (var i = 0; i < key.length; ++i) {
var error = $root.sharedprotos.Polygon2DList.verify(message.strToPolygon2DListMap[key[i]]);
if (error)
return "strToPolygon2DListMap." + error;
}
}
if (message.stageDiscreteW != null && message.hasOwnProperty("stageDiscreteW"))
if (!$util.isInteger(message.stageDiscreteW))
return "stageDiscreteW: integer expected";
@ -5529,26 +5261,6 @@ $root.protos = (function() {
var message = new $root.protos.BattleColliderInfo();
if (object.stageName != null)
message.stageName = String(object.stageName);
if (object.strToVec2DListMap) {
if (typeof object.strToVec2DListMap !== "object")
throw TypeError(".protos.BattleColliderInfo.strToVec2DListMap: object expected");
message.strToVec2DListMap = {};
for (var keys = Object.keys(object.strToVec2DListMap), i = 0; i < keys.length; ++i) {
if (typeof object.strToVec2DListMap[keys[i]] !== "object")
throw TypeError(".protos.BattleColliderInfo.strToVec2DListMap: object expected");
message.strToVec2DListMap[keys[i]] = $root.sharedprotos.Vec2DList.fromObject(object.strToVec2DListMap[keys[i]]);
}
}
if (object.strToPolygon2DListMap) {
if (typeof object.strToPolygon2DListMap !== "object")
throw TypeError(".protos.BattleColliderInfo.strToPolygon2DListMap: object expected");
message.strToPolygon2DListMap = {};
for (var keys = Object.keys(object.strToPolygon2DListMap), i = 0; i < keys.length; ++i) {
if (typeof object.strToPolygon2DListMap[keys[i]] !== "object")
throw TypeError(".protos.BattleColliderInfo.strToPolygon2DListMap: object expected");
message.strToPolygon2DListMap[keys[i]] = $root.sharedprotos.Polygon2DList.fromObject(object.strToPolygon2DListMap[keys[i]]);
}
}
if (object.stageDiscreteW != null)
message.stageDiscreteW = object.stageDiscreteW | 0;
if (object.stageDiscreteH != null)
@ -5643,11 +5355,8 @@ $root.protos = (function() {
if (!options)
options = {};
var object = {};
if (options.objects || options.defaults) {
object.strToVec2DListMap = {};
object.strToPolygon2DListMap = {};
if (options.objects || options.defaults)
object.meleeSkillConfig = {};
}
if (options.defaults) {
object.stageName = "";
object.stageDiscreteW = 0;
@ -5688,17 +5397,6 @@ $root.protos = (function() {
}
if (message.stageName != null && message.hasOwnProperty("stageName"))
object.stageName = message.stageName;
var keys2;
if (message.strToVec2DListMap && (keys2 = Object.keys(message.strToVec2DListMap)).length) {
object.strToVec2DListMap = {};
for (var j = 0; j < keys2.length; ++j)
object.strToVec2DListMap[keys2[j]] = $root.sharedprotos.Vec2DList.toObject(message.strToVec2DListMap[keys2[j]], options);
}
if (message.strToPolygon2DListMap && (keys2 = Object.keys(message.strToPolygon2DListMap)).length) {
object.strToPolygon2DListMap = {};
for (var j = 0; j < keys2.length; ++j)
object.strToPolygon2DListMap[keys2[j]] = $root.sharedprotos.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"))
@ -5749,6 +5447,7 @@ $root.protos = (function() {
object.spAtkLookupFrames = message.spAtkLookupFrames;
if (message.renderCacheSize != null && message.hasOwnProperty("renderCacheSize"))
object.renderCacheSize = message.renderCacheSize;
var keys2;
if (message.meleeSkillConfig && (keys2 = Object.keys(message.meleeSkillConfig)).length) {
object.meleeSkillConfig = {};
for (var j = 0; j < keys2.length; ++j)

View File

@ -43,7 +43,7 @@ func ConvertToInputFrameId(renderFrameId int32, inputDelayFrames int32, inputSca
return ((renderFrameId - inputDelayFrames) >> inputScaleFrames)
}
func DecodeInput(encodedInput uint64) *InputFrameDecoded {
func decodeInput(encodedInput uint64) *InputFrameDecoded {
encodedDirection := (encodedInput & uint64(15))
btnALevel := int32((encodedInput >> 4) & 1)
btnBLevel := int32((encodedInput >> 5) & 1)
@ -255,7 +255,7 @@ func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBounding
return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
}
func CalcHardPushbacksNorms(playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D) []Vec2D {
func calcHardPushbacksNorms(playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D) []Vec2D {
ret := make([]Vec2D, 0, 10) // no one would simultaneously have more than 5 hardPushbacks
collision := playerCollider.Check(0, 0)
if nil == collision {
@ -281,8 +281,9 @@ func CalcHardPushbacksNorms(playerCollider *resolv.Object, playerShape *resolv.C
return ret
}
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, delayedInputFrameForPrevRenderFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames int32, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64) *RoomDownsyncFrame {
// [WARNING] This function MUST BE called while "InputsBufferLock" is locked!
// [WARNING] The params of this method is carefully tuned such that only "battle.RoomDownsyncFrame" is a necessary custom struct.
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputList, delayedInputListForPrevRenderFrame []uint64, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames int32, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64) *RoomDownsyncFrame {
// [WARNING] On backend this function MUST BE called while "InputsBufferLock" is locked!
roomCapacity := len(currRenderFrame.PlayersArr)
nextRenderFramePlayers := make([]*PlayerDownsync, roomCapacity)
// Make a copy first
@ -315,18 +316,17 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, delay
hardPushbackNorms := make([][]Vec2D, roomCapacity)
// 1. Process player inputs
if nil != delayedInputFrame {
inputList := delayedInputFrame.InputList
if nil != delayedInputList {
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
thatPlayerInNextFrame := nextRenderFramePlayers[i]
if 0 < thatPlayerInNextFrame.FramesToRecover {
continue
}
decodedInput := DecodeInput(inputList[joinIndex-1])
decodedInput := decodeInput(delayedInputList[joinIndex-1])
prevBtnBLevel := int32(0)
if nil != delayedInputFrameForPrevRenderFrame {
prevDecodedInput := DecodeInput(delayedInputFrameForPrevRenderFrame.InputList[joinIndex-1])
if nil != delayedInputListForPrevRenderFrame {
prevDecodedInput := decodeInput(delayedInputListForPrevRenderFrame[joinIndex-1])
prevBtnBLevel = prevDecodedInput.BtnBLevel
}
@ -386,7 +386,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, delay
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := collisionSysMap[collisionPlayerIndex]
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
hardPushbackNorms[joinIndex-1] = CalcHardPushbacksNorms(playerCollider, playerShape, snapIntoPlatformOverlap, &(effPushbacks[joinIndex-1]))
hardPushbackNorms[joinIndex-1] = calcHardPushbacksNorms(playerCollider, playerShape, snapIntoPlatformOverlap, &(effPushbacks[joinIndex-1]))
thatPlayerInNextFrame := nextRenderFramePlayers[i]
fallStopping := false
if collision := playerCollider.Check(0, 0); nil != collision {
@ -394,12 +394,13 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, delay
isBarrier, isAnotherPlayer, isBullet := false, false, false
// TODO: Make this part work in JavaScript without having to expose all types Barrier/PlayerDownsync/MeleeBullet by js.MakeWrapper.
switch obj.Data.(type) {
case *Barrier:
isBarrier = true
case *PlayerDownsync:
isAnotherPlayer = true
case *MeleeBullet:
isBullet = true
default:
// By default it's a regular barrier, even if data is nil
isBarrier = true
}
if isBullet {
// ignore bullets for this step

View File

@ -47,12 +47,6 @@ type InputFrameUpsync struct {
Encoded uint64
}
type InputFrameDownsync struct {
InputFrameId int32
InputList []uint64
ConfirmedList uint64
}
type Barrier struct {
Boundary *Polygon2D
}

View File

@ -58,14 +58,6 @@ func NewRoomDownsyncFrameJs(id int32, playersArr []*PlayerDownsync, meleeBullets
})
}
func NewInputFrameDownsyncJs(inputFrameId int32, inputList []uint64, confirmedList uint64) *js.Object {
return js.MakeFullWrapper(&InputFrameDownsync{
InputFrameId: inputFrameId,
InputList: inputList,
ConfirmedList: confirmedList,
})
}
func GetCollisionSpaceObjsJs(space *resolv.Space) []*js.Object {
objs := space.Objects()
ret := make([]*js.Object, 0, len(objs))
@ -110,9 +102,9 @@ func CheckCollisionJs(obj *resolv.Object, dx, dy float64) *js.Object {
return js.MakeFullWrapper(obj.Check(dx, dy))
}
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(delayedInputFrame, delayedInputFrameForPrevRenderFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames int32, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64) *js.Object {
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(delayedInputList, delayedInputListForPrevRenderFrame []uint64, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames int32, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64) *js.Object {
// We need access to all fields of RoomDownsyncFrame for displaying in frontend
return js.MakeFullWrapper(ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, delayedInputFrameForPrevRenderFrame, currRenderFrame, collisionSys, collisionSysMap, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio))
return js.MakeFullWrapper(ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputList, delayedInputListForPrevRenderFrame, currRenderFrame, collisionSys, collisionSysMap, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio))
}
func main() {
@ -123,7 +115,6 @@ func main() {
"NewPlayerDownsyncJs": NewPlayerDownsyncJs,
"NewRoomDownsyncFrameJs": NewRoomDownsyncFrameJs,
"NewCollisionSpaceJs": NewCollisionSpaceJs,
"NewInputFrameDownsyncJs": NewInputFrameDownsyncJs,
"GenerateRectColliderJs": GenerateRectColliderJs,
"GenerateConvexPolygonColliderJs": GenerateConvexPolygonColliderJs,
"GetPlayersArrJs": GetPlayersArrJs,

View File

@ -6,7 +6,7 @@
golang_basedir_1=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/dnmshared
protoc -I=$golang_basedir_1/../frontend/assets/resources/pbfiles/ --go_out=. geometry.proto
echo "GOLANG part 1 done"
echo "GOLANG part 1 done" # [WARNING] The output of "part 1" is DEPRECATED, the codes are not using it anymore.
# [WARNING] The following "room_downsync_frame.proto" is generated in another Go package than "geometry.proto", but the generated Go codes are also required to work with imports correctly!
golang_basedir_2=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/battle_srv