mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-25 11:18:55 +00:00
Refactoring backend for periodical force confirmation.
This commit is contained in:
parent
14fb8e94b2
commit
266335b7c6
@ -1,3 +1,4 @@
|
||||
# Potential avalanche from local lag
|
||||
Under the current "input delay" algorithm, the lag of a single player would cause all the other players to receive outdated commands, e.g. when at a certain moment
|
||||
- player#1: renderFrameId = 100, significantly lagged due to local CPU overheated
|
||||
- player#2: renderFrameId = 240
|
||||
@ -9,3 +10,34 @@ players #2, #3 #4 would receive "outdated(in their subjective feelings) but all-
|
||||
In a "no-server & p2p" setup, I couldn't think of a proper way to cope with such edge case. Solely on the frontend we could only mitigate the impact to players #2, #3, #4, e.g. a potential lag due to "large range of frame-chasing" is proactively avoided in `<proj-root>/frontend/assets/scripts/Map.js, function update(dt)`.
|
||||
|
||||
However in a "server as authority" setup, the server could force confirming an inputFrame without player#1's upsync, and notify player#1 to apply a "roomDownsyncFrame" as well as drop all its outdated local inputFrames.
|
||||
|
||||
# Start up frames
|
||||
renderFrameId | generatedInputFrameId | toApplyInputFrameId
|
||||
-------------------|----------------------------|----------------------
|
||||
0, 1, 2, 3 | 0, _EMP_, _EMP_, _EMP_ | 0
|
||||
4, 5, 6, 7 | 1, _EMP_, _EMP_, _EMP_ | 0
|
||||
8, 9, 10, 11 | 2, _EMP_, _EMP_, _EMP_ | 1
|
||||
12, 13, 14, 15 | 3, _EMP_, _EMP_, _EMP_ | 2
|
||||
|
||||
It should be reasonable to assume that inputFrameId=0 is always of all-empty content, because human has no chance of clicking at the very first render frame.
|
||||
|
||||
# Alignment of the current setup
|
||||
The following setup is chosen deliberately for some "%4" number coincidence.
|
||||
- NstDelayFrames = 2
|
||||
- InputDelayFrames = 4
|
||||
- InputScaleFrames = 2
|
||||
|
||||
If "InputDelayFrames" is changed, the impact would be as follows, kindly note that "372%4 == 0".
|
||||
|
||||
### pR.InputDelayFrames = 4
|
||||
toApplyInputFrameId | renderFrameId
|
||||
--------------------------|-----------------------------------------
|
||||
92 | _EMP_, _EMP_, _EMP_, _EMP_, 372, 373, 374, 375
|
||||
91 | 368, 369, 370, 371
|
||||
|
||||
### pR.InputDelayFrames = 5
|
||||
toApplyInputFrameId | renderFrameId
|
||||
--------------------------|-----------------------------------------
|
||||
92 | _EMP_, _EMP_, _EMP_, _EMP_, 373, 374, 375
|
||||
91 | _EMP_, 369, 370, 371, 372
|
||||
90 | 368
|
||||
|
@ -26,6 +26,7 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.3 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.9.0
|
||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
|
||||
github.com/solarlune/resolv v0.5.1
|
||||
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713
|
||||
github.com/ugorji/go v1.1.1 // indirect
|
||||
go.uber.org/atomic v1.3.2 // indirect
|
||||
|
@ -45,6 +45,8 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+uGHxWtRt4C0Wjc6kJKo5XYx8xE=
|
||||
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
|
||||
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 h1:v8lWpj5957KtDMKu+xQtlu6G3ZoZR6Tn9bsfZCRG5Xw=
|
||||
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0/go.mod h1:GAX7tMJqXx9fB1BrsTWPOXy6IBRX+J461BffVPAdpwo=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||
@ -61,6 +63,8 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0C
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE=
|
||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/solarlune/resolv v0.5.1 h1:Ul6PAs/zaxiMUOEYz1Z6VeUj5k3CDcWMvSh+kivybDY=
|
||||
github.com/solarlune/resolv v0.5.1/go.mod h1:HjM2f/0NoVjVdZsi26GtugX5aFbA62COEFEXkOhveRw=
|
||||
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713 h1:knaxjm6QMbUMNvuaSnJZmw0gRX4V/79JVUQiziJGM84=
|
||||
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713/go.mod h1:mlR+dHGb+4YgXkf13rkQTuzrneeHANxOm6+ZnEV9HsA=
|
||||
github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w=
|
||||
|
@ -37,7 +37,7 @@ type Player struct {
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
Dir *Direction `json:"dir,omitempty"`
|
||||
Speed int32 `json:"speed,omitempty"`
|
||||
Speed float64 `json:"speed,omitempty"`
|
||||
BattleState int32 `json:"battleState,omitempty"`
|
||||
LastMoveGmtMillis int32 `json:"lastMoveGmtMillis,omitempty"`
|
||||
Score int32 `json:"score,omitempty"`
|
||||
|
@ -3,9 +3,9 @@ package models
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/ByteArena/box2d"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/solarlune/resolv"
|
||||
"go.uber.org/zap"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
@ -41,12 +41,14 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
// You can equivalently use the `GroupIndex` approach, but the more complicated and general purpose approach is used deliberately here. Reference http://www.aurelienribon.com/post/2011-07-box2d-tutorial-collision-filtering.
|
||||
COLLISION_CATEGORY_CONTROLLED_PLAYER = (1 << 1)
|
||||
COLLISION_CATEGORY_BARRIER = (1 << 2)
|
||||
|
||||
COLLISION_MASK_FOR_CONTROLLED_PLAYER = (COLLISION_CATEGORY_BARRIER)
|
||||
COLLISION_MASK_FOR_BARRIER = (COLLISION_CATEGORY_CONTROLLED_PLAYER)
|
||||
|
||||
COLLISION_PLAYER_INDEX_PREFIX = (1 << 17)
|
||||
COLLISION_BARRIER_INDEX_PREFIX = (1 << 16)
|
||||
)
|
||||
|
||||
var DIRECTION_DECODER = [][]int32{
|
||||
@ -65,7 +67,7 @@ var DIRECTION_DECODER = [][]int32{
|
||||
{0, -1},
|
||||
}
|
||||
|
||||
var DIRECTION_DECODER_INVERSE_LENGTH = []float32{
|
||||
var DIRECTION_DECODER_INVERSE_LENGTH = []float64{
|
||||
0.0,
|
||||
1.0,
|
||||
1.0,
|
||||
@ -117,9 +119,11 @@ func calRoomScore(inRoomPlayerCount int32, roomPlayerCnt int, currentRoomBattleS
|
||||
}
|
||||
|
||||
type Room struct {
|
||||
Id int32
|
||||
Capacity int
|
||||
Players map[int32]*Player
|
||||
Id int32
|
||||
Capacity int
|
||||
Players map[int32]*Player
|
||||
PlayersArr []*Player // ordered by joinIndex
|
||||
CollisionSysMap map[int32]*resolv.Object
|
||||
/**
|
||||
* The following `PlayerDownsyncSessionDict` is NOT individually put
|
||||
* under `type Player struct` for a reason.
|
||||
@ -143,22 +147,23 @@ type Room struct {
|
||||
Score float32
|
||||
State int32
|
||||
Index int
|
||||
Tick int32
|
||||
RenderFrameId int32
|
||||
CurDynamicsRenderFrameId int32 // [WARNING] The dynamics of backend is ALWAYS MOVING FORWARD BY ALL-CONFIRMED INPUTFRAMES (either by upsync or forced), i.e. no rollback
|
||||
ServerFPS int32
|
||||
BattleDurationNanos int64
|
||||
EffectivePlayerCount int32
|
||||
DismissalWaitGroup sync.WaitGroup
|
||||
Barriers map[int32]*Barrier
|
||||
AccumulatedLocalIdForBullets int32
|
||||
CollidableWorld *box2d.B2World
|
||||
AllPlayerInputsBuffer *RingBuffer
|
||||
RenderFrameBuffer *RingBuffer
|
||||
LastAllConfirmedInputFrameId int32
|
||||
LastAllConfirmedInputFrameIdWithChange int32
|
||||
LastAllConfirmedInputList []uint64
|
||||
InputDelayFrames int32
|
||||
InputDelayFrames int32 // in the count of render frames
|
||||
NstDelayFrames int32 // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames"
|
||||
InputScaleFrames uint32 // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
|
||||
JoinIndexBooleanArr []bool
|
||||
RollbackEstimatedDt float64
|
||||
|
||||
StageName string
|
||||
StageDiscreteW int32
|
||||
@ -170,8 +175,8 @@ type Room struct {
|
||||
}
|
||||
|
||||
const (
|
||||
PLAYER_DEFAULT_SPEED = 200 // Hardcoded
|
||||
ADD_SPEED = 100 // Hardcoded
|
||||
PLAYER_DEFAULT_SPEED = float64(200) // Hardcoded
|
||||
ADD_SPEED = float64(100) // Hardcoded
|
||||
)
|
||||
|
||||
func (pR *Room) updateScore() {
|
||||
@ -310,9 +315,9 @@ func (pR *Room) ChooseStage() error {
|
||||
Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points))
|
||||
*/
|
||||
pR.Barriers[barrierLocalIdInBattle] = &Barrier{
|
||||
X: polygon2D.Anchor.X,
|
||||
Y: polygon2D.Anchor.Y,
|
||||
Boundary: polygon2D,
|
||||
X: polygon2D.Anchor.X,
|
||||
Y: polygon2D.Anchor.Y,
|
||||
Boundary: polygon2D,
|
||||
}
|
||||
|
||||
barrierLocalIdInBattle++
|
||||
@ -321,11 +326,17 @@ func (pR *Room) ChooseStage() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pR *Room) ConvertToInputFrameId(originalFrameId int32, inputDelayFrames int32) int32 {
|
||||
if originalFrameId < inputDelayFrames {
|
||||
return 0
|
||||
}
|
||||
return ((originalFrameId - inputDelayFrames) >> pR.InputScaleFrames)
|
||||
func (pR *Room) ConvertToInputFrameId(renderFrameId int32, inputDelayFrames int32) int32 {
|
||||
// Specifically when "renderFrameId < inputDelayFrames", the result is 0.
|
||||
return ((renderFrameId - inputDelayFrames) >> pR.InputScaleFrames)
|
||||
}
|
||||
|
||||
func (pR *Room) ConvertToFirstUsedRenderFrameId(inputFrameId int32, inputDelayFrames int32) int32 {
|
||||
return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames)
|
||||
}
|
||||
|
||||
func (pR *Room) ConvertToLastUsedRenderFrameId(inputFrameId int32, inputDelayFrames int32) int32 {
|
||||
return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames + (1 << pR.InputScaleFrames)-1)
|
||||
}
|
||||
|
||||
func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 {
|
||||
@ -335,19 +346,6 @@ func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (pR *Room) CanPopSt(refLowerInputFrameId int32) bool {
|
||||
rb := pR.AllPlayerInputsBuffer
|
||||
if rb.Cnt <= 0 {
|
||||
return false
|
||||
}
|
||||
if rb.StFrameId <= refLowerInputFrameId {
|
||||
// already delayed too much
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (pR *Room) AllPlayerInputsBufferString() string {
|
||||
s := make([]string, 0)
|
||||
s = append(s, fmt.Sprintf("{lastAllConfirmedInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v}", pR.LastAllConfirmedInputFrameId, pR.LastAllConfirmedInputFrameIdWithChange))
|
||||
@ -374,9 +372,10 @@ func (pR *Room) StartBattle() {
|
||||
|
||||
// Always instantiates a new channel and let the old one die out due to not being retained by any root reference.
|
||||
nanosPerFrame := 1000000000 / int64(pR.ServerFPS)
|
||||
pR.Tick = 0
|
||||
pR.RenderFrameId = 0
|
||||
pR.CurDynamicsRenderFrameId = 0
|
||||
|
||||
// Refresh "Colliders" for server-side contact listening of B2World.
|
||||
// Refresh "Colliders"
|
||||
pR.refreshColliders()
|
||||
|
||||
/**
|
||||
@ -397,15 +396,19 @@ func (pR *Room) StartBattle() {
|
||||
|
||||
Logger.Info("The `battleMainLoop` is started for:", zap.Any("roomId", pR.Id))
|
||||
for {
|
||||
if 0 == pR.Tick {
|
||||
stCalculation := utils.UnixtimeNano()
|
||||
|
||||
if 0 == pR.RenderFrameId {
|
||||
// The legacy frontend code needs this "kickoffFrame" to remove the "ready to start 3-2-1" panel
|
||||
kickoffFrame := pb.RoomDownsyncFrame{
|
||||
Id: pR.Tick,
|
||||
Id: pR.RenderFrameId,
|
||||
RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
SentAt: utils.UnixtimeMilli(),
|
||||
CountdownNanos: (pR.BattleDurationNanos - totalElapsedNanos),
|
||||
}
|
||||
|
||||
pR.RenderFrameBuffer.Put(&kickoffFrame)
|
||||
for playerId, player := range pR.Players {
|
||||
if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped {
|
||||
/*
|
||||
@ -418,80 +421,102 @@ func (pR *Room) StartBattle() {
|
||||
}
|
||||
|
||||
if totalElapsedNanos > pR.BattleDurationNanos {
|
||||
Logger.Info(fmt.Sprintf("The `battleMainLoop` is stopped:\n%v", pR.AllPlayerInputsBufferString()))
|
||||
Logger.Info(fmt.Sprintf("The `battleMainLoop` for roomId=%v is stopped:\n%v", pR.Id, pR.AllPlayerInputsBufferString()))
|
||||
pR.StopBattleForSettlement()
|
||||
}
|
||||
|
||||
if swapped := atomic.CompareAndSwapInt32(&pR.State, RoomBattleStateIns.IN_BATTLE, RoomBattleStateIns.IN_BATTLE); !swapped {
|
||||
return
|
||||
}
|
||||
stCalculation := utils.UnixtimeNano()
|
||||
|
||||
// TODO: Force confirm some non-all-confirmed but outdated input frames, derive server-sider RenderFrameBuffer elements from them, and send to respective players with "RoomDownsyncFrame.refFrameId=MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED"
|
||||
|
||||
refInputFrameId := int32(999999999) // Hardcoded as a max reference.
|
||||
for playerId, _ := range pR.Players {
|
||||
thatId := atomic.LoadInt32(&(pR.Players[playerId].AckingInputFrameId))
|
||||
if thatId > refInputFrameId {
|
||||
continue
|
||||
}
|
||||
refInputFrameId = thatId
|
||||
// Prefab and buffer backend inputFrameDownsync
|
||||
if pR.shouldPrefabInputFrameDownsync(pR.RenderFrameId) {
|
||||
noDelayInputFrameId := pR.ConvertToInputFrameId(pR.RenderFrameId, 0)
|
||||
pR.prefabInputFrameDownsync(noDelayInputFrameId)
|
||||
}
|
||||
|
||||
// Force setting all-confirmed of buffered inputFrames periodically
|
||||
unconfirmedMask := pR.forceConfirmationIfApplicable()
|
||||
|
||||
for pR.CanPopSt(refInputFrameId) {
|
||||
// _ = pR.AllPlayerInputsBuffer.Pop()
|
||||
f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync)
|
||||
if pR.inputFrameIdDebuggable(f.InputFrameId) {
|
||||
// Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked
|
||||
Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("refInputFrameId", refInputFrameId), zap.Any("inputFrameId", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
}
|
||||
}
|
||||
// Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId"
|
||||
if 0 <= pR.CurDynamicsRenderFrameId {
|
||||
nextDynamicsRenderFrameId := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames)
|
||||
pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId)
|
||||
}
|
||||
|
||||
lastAllConfirmedInputFrameIdWithChange := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameIdWithChange))
|
||||
for playerId, player := range pR.Players {
|
||||
if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped {
|
||||
/*
|
||||
[WARNING] DON'T send anything into "DedicatedForwardingChanForPlayer" if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player".
|
||||
*/
|
||||
continue
|
||||
}
|
||||
lastAllConfirmedInputFrameIdWithChange := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameIdWithChange))
|
||||
for playerId, player := range pR.Players {
|
||||
if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped {
|
||||
/*
|
||||
[WARNING] DON'T send anything into "DedicatedForwardingChanForPlayer" if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player".
|
||||
*/
|
||||
continue
|
||||
}
|
||||
// [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player!
|
||||
toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt)
|
||||
candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1
|
||||
if candidateToSendInputFrameId < pR.AllPlayerInputsBuffer.StFrameId {
|
||||
Logger.Warn("LastSentInputFrameId already popped:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("lastSentInputFrameId", candidateToSendInputFrameId-1), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()))
|
||||
candidateToSendInputFrameId = pR.AllPlayerInputsBuffer.StFrameId
|
||||
}
|
||||
|
||||
toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt)
|
||||
// [WARNING] Websocket is TCP-based, thus no need to re-send a previously sent inputFrame to a same player!
|
||||
anchorInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId))
|
||||
candidateToSendInputFrameId := anchorInputFrameId + 1
|
||||
// [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed!
|
||||
debugSendingInputFrameId := int32(-1)
|
||||
|
||||
// [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed!
|
||||
debugSendingInputFrameId := int32(-1)
|
||||
for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange {
|
||||
tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId)
|
||||
if nil == tmp {
|
||||
panic(fmt.Sprintf("Required inputFrameId=%v for roomId=%v, playerId=%v doesn't exist! AllPlayerInputsBuffer=%v", candidateToSendInputFrameId, pR.Id, playerId, pR.AllPlayerInputsBufferString()))
|
||||
}
|
||||
f := tmp.(*pb.InputFrameDownsync)
|
||||
if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) {
|
||||
debugSendingInputFrameId = candidateToSendInputFrameId
|
||||
Logger.Info("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()), zap.Any("ConfirmedList", f.ConfirmedList))
|
||||
}
|
||||
toSendInputFrames = append(toSendInputFrames, f)
|
||||
candidateToSendInputFrameId++
|
||||
}
|
||||
|
||||
for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange {
|
||||
tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId)
|
||||
if nil == tmp {
|
||||
break
|
||||
}
|
||||
f := tmp.(*pb.InputFrameDownsync)
|
||||
if pR.inputFrameIdDebuggable(candidateToSendInputFrameId) {
|
||||
debugSendingInputFrameId = candidateToSendInputFrameId
|
||||
Logger.Info("inputFrame lifecycle#3[sending]:", zap.Any("roomId", pR.Id), zap.Any("refInputFrameId", refInputFrameId), zap.Any("playerId", playerId), zap.Any("playerAnchorInputFrameId", anchorInputFrameId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", candidateToSendInputFrameId), zap.Any("inputFrameId-doublecheck", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId), zap.Any("ConfirmedList", f.ConfirmedList))
|
||||
}
|
||||
toSendInputFrames = append(toSendInputFrames, f)
|
||||
candidateToSendInputFrameId++
|
||||
}
|
||||
indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1)
|
||||
var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr)
|
||||
if 0 < (unconfirmedMask & joinMask) {
|
||||
refRenderFrame := pR.RenderFrameBuffer.GetByFrameId(pR.CurDynamicsRenderFrameId).(*pb.RoomDownsyncFrame)
|
||||
resp := pb.WsResp {
|
||||
Ret: int32(Constants.RetCode.Ok),
|
||||
EchoedMsgId: int32(0),
|
||||
Act: DOWNSYNC_MSG_ACT_ROOM_FRAME,
|
||||
InputFrameDownsyncBatch: toSendInputFrames,
|
||||
Rdf: refRenderFrame,
|
||||
}
|
||||
pR.sendSafely(resp, playerId)
|
||||
} else {
|
||||
if 0 >= len(toSendInputFrames) {
|
||||
continue
|
||||
}
|
||||
pR.sendSafely(toSendInputFrames, playerId)
|
||||
atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1)
|
||||
if -1 != debugSendingInputFrameId {
|
||||
Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if 0 >= len(toSendInputFrames) {
|
||||
continue
|
||||
}
|
||||
for 0 < pR.RenderFrameBuffer.Cnt && pR.RenderFrameBuffer.StFrameId < pR.CurDynamicsRenderFrameId {
|
||||
_ = pR.RenderFrameBuffer.Pop()
|
||||
}
|
||||
|
||||
pR.sendSafely(toSendInputFrames, playerId)
|
||||
atomic.StoreInt32(&(pR.Players[playerId].LastSentInputFrameId), candidateToSendInputFrameId-1)
|
||||
if -1 != debugSendingInputFrameId {
|
||||
Logger.Info("inputFrame lifecycle#4[sent]:", zap.Any("roomId", pR.Id), zap.Any("refInputFrameId", refInputFrameId), zap.Any("playerId", playerId), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("inputFrameId", debugSendingInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
}
|
||||
}
|
||||
toApplyInputFrameId := pR.ConvertToInputFrameId(pR.CurDynamicsRenderFrameId, pR.InputDelayFrames)
|
||||
for 0 < pR.AllPlayerInputsBuffer.Cnt && pR.AllPlayerInputsBuffer.StFrameId < toApplyInputFrameId {
|
||||
f := pR.AllPlayerInputsBuffer.Pop().(*pb.InputFrameDownsync)
|
||||
if pR.inputFrameIdDebuggable(f.InputFrameId) {
|
||||
// Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked
|
||||
Logger.Info("inputFrame lifecycle#5[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
}
|
||||
}
|
||||
|
||||
pR.Tick++
|
||||
pR.RenderFrameId++
|
||||
now := utils.UnixtimeNano()
|
||||
elapsedInCalculation := now - stCalculation
|
||||
elapsedInCalculation := (now - stCalculation)
|
||||
totalElapsedNanos = (now - battleMainLoopStartedNanos)
|
||||
// Logger.Info("Elapsed time statistics:", zap.Any("roomId", pR.Id), zap.Any("elapsedInCalculation", elapsedInCalculation), zap.Any("totalElapsedNanos", totalElapsedNanos))
|
||||
time.Sleep(time.Duration(nanosPerFrame - elapsedInCalculation))
|
||||
@ -515,19 +540,20 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) {
|
||||
ackingFrameId := pReq.AckingFrameId
|
||||
ackingInputFrameId := pReq.AckingInputFrameId
|
||||
|
||||
if _, existent := pR.Players[playerId]; !existent {
|
||||
Logger.Warn("upcmd player doesn't exist:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId))
|
||||
return
|
||||
}
|
||||
|
||||
if swapped := atomic.CompareAndSwapInt32(&(pR.Players[playerId].AckingFrameId), pR.Players[playerId].AckingFrameId, ackingFrameId); !swapped {
|
||||
panic(fmt.Sprintf("Failed to update AckingFrameId to %v for roomId=%v, playerId=%v", ackingFrameId, pR.Id, playerId))
|
||||
}
|
||||
|
||||
if swapped := atomic.CompareAndSwapInt32(&(pR.Players[playerId].AckingInputFrameId), pR.Players[playerId].AckingInputFrameId, ackingInputFrameId); !swapped {
|
||||
panic(fmt.Sprintf("Failed to update AckingInputFrameId to %v for roomId=%v, playerId=%v", ackingInputFrameId, pR.Id, playerId))
|
||||
}
|
||||
|
||||
for _, inputFrameUpsync := range inputFrameUpsyncBatch {
|
||||
if _, existent := pR.Players[playerId]; !existent {
|
||||
Logger.Warn("upcmd player doesn't exist:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId))
|
||||
return
|
||||
}
|
||||
|
||||
if swapped := atomic.CompareAndSwapInt32(&(pR.Players[playerId].AckingFrameId), pR.Players[playerId].AckingFrameId, ackingFrameId); !swapped {
|
||||
panic(fmt.Sprintf("Failed to update AckingFrameId to %v for roomId=%v, playerId=%v", ackingFrameId, pR.Id, playerId))
|
||||
}
|
||||
|
||||
if swapped := atomic.CompareAndSwapInt32(&(pR.Players[playerId].AckingInputFrameId), pR.Players[playerId].AckingInputFrameId, ackingInputFrameId); !swapped {
|
||||
panic(fmt.Sprintf("Failed to update AckingInputFrameId to %v for roomId=%v, playerId=%v", ackingInputFrameId, pR.Id, playerId))
|
||||
}
|
||||
clientInputFrameId := inputFrameUpsync.InputFrameId
|
||||
if clientInputFrameId < pR.AllPlayerInputsBuffer.StFrameId {
|
||||
Logger.Warn("Obsolete inputFrameUpsync:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
@ -538,66 +564,56 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) {
|
||||
encodedInput := pR.EncodeUpsyncCmd(inputFrameUpsync)
|
||||
|
||||
if clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId {
|
||||
// The outer-if branching is for reducing an extra get-and-set operation, which is now placed in the else-branch.
|
||||
for clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId {
|
||||
newInputList := make([]uint64, len(pR.Players))
|
||||
newInputList[indiceInJoinIndexBooleanArr] = encodedInput
|
||||
pR.AllPlayerInputsBuffer.Put(&pb.InputFrameDownsync{
|
||||
InputFrameId: pR.AllPlayerInputsBuffer.EdFrameId,
|
||||
InputList: newInputList,
|
||||
ConfirmedList: joinMask, // by now only the current player has confirmed this input frame
|
||||
})
|
||||
if pR.inputFrameIdDebuggable(clientInputFrameId) {
|
||||
Logger.Info("inputFrame lifecycle#1[inserted]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId)
|
||||
if nil == tmp2 {
|
||||
// This shouldn't happen due to the previous 2 checks
|
||||
Logger.Warn("Mysterious error getting an input frame:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
return
|
||||
}
|
||||
inputFrameDownsync := tmp2.(*pb.InputFrameDownsync)
|
||||
oldConfirmedList := inputFrameDownsync.ConfirmedList
|
||||
if (oldConfirmedList & joinMask) > 0 {
|
||||
Logger.Warn("Cmd already confirmed but getting set attempt:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
return
|
||||
}
|
||||
|
||||
// In Golang 1.12, there's no "compare-and-swap primitive" on a custom struct (or it's pointer, unless it's an unsafe pointer https://pkg.go.dev/sync/atomic@go1.12#CompareAndSwapPointer). Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible.
|
||||
if swapped := atomic.CompareAndSwapUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], uint64(0), encodedInput); !swapped {
|
||||
if encodedInput > 0 {
|
||||
Logger.Warn("Failed input CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
newConfirmedList := (oldConfirmedList | joinMask)
|
||||
if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped {
|
||||
if encodedInput > 0 {
|
||||
Logger.Warn("Failed confirm CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
totPlayerCnt := uint32(len(pR.Players))
|
||||
allConfirmedMask := uint64((1 << totPlayerCnt) - 1) // TODO: What if a player is disconnected backthen?
|
||||
if allConfirmedMask == newConfirmedList {
|
||||
if false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) {
|
||||
atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), clientInputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here!
|
||||
Logger.Info("Key inputFrame change", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", clientInputFrameId), zap.Any("lastInputFrameId", pR.LastAllConfirmedInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId), zap.Any("newInputList", inputFrameDownsync.InputList), zap.Any("lastInputList", pR.LastAllConfirmedInputList))
|
||||
}
|
||||
atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays!
|
||||
for i, v := range inputFrameDownsync.InputList {
|
||||
// To avoid potential misuse of pointers
|
||||
pR.LastAllConfirmedInputList[i] = v
|
||||
}
|
||||
if pR.inputFrameIdDebuggable(clientInputFrameId) {
|
||||
Logger.Info("inputFrame lifecycle#2[allconfirmed]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
}
|
||||
}
|
||||
Logger.Warn("inputFrame too advanced! is the player cheating?", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
return
|
||||
}
|
||||
tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId)
|
||||
if nil == tmp2 {
|
||||
// This shouldn't happen due to the previous 2 checks
|
||||
Logger.Warn("Mysterious error getting an input frame:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
return
|
||||
}
|
||||
inputFrameDownsync := tmp2.(*pb.InputFrameDownsync)
|
||||
oldConfirmedList := atomic.LoadUint64(&(inputFrameDownsync.ConfirmedList))
|
||||
if (oldConfirmedList & joinMask) > 0 {
|
||||
Logger.Warn("Cmd already confirmed but getting set attempt, omitting this upsync cmd:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
|
||||
return
|
||||
}
|
||||
|
||||
// In Golang 1.12, there's no "compare-and-swap primitive" on a custom struct (or it's pointer, unless it's an unsafe pointer https://pkg.go.dev/sync/atomic@go1.12#CompareAndSwapPointer). Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible.
|
||||
if swapped := atomic.CompareAndSwapUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], uint64(0), encodedInput); !swapped {
|
||||
Logger.Warn("Failed input CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId))
|
||||
return
|
||||
}
|
||||
|
||||
newConfirmedList := (oldConfirmedList | joinMask)
|
||||
if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped {
|
||||
// [WARNING] Upon this error, the actual input has already been updated, which is an expected result if it caused by the force confirmation from "battleMainLoop".
|
||||
Logger.Warn("Failed confirm CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId))
|
||||
return
|
||||
}
|
||||
|
||||
totPlayerCnt := uint32(len(pR.Players))
|
||||
allConfirmedMask := uint64((1 << totPlayerCnt) - 1)
|
||||
if allConfirmedMask == newConfirmedList {
|
||||
pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, playerId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFrameDownsync, playerId int32) {
|
||||
clientInputFrameId := inputFrameDownsync.InputFrameId
|
||||
if false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) {
|
||||
atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), clientInputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here!
|
||||
Logger.Info("Key inputFrame change", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", clientInputFrameId), zap.Any("lastInputFrameId", pR.LastAllConfirmedInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()), zap.Any("newInputList", inputFrameDownsync.InputList), zap.Any("lastInputList", pR.LastAllConfirmedInputList))
|
||||
}
|
||||
atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays!
|
||||
for i, v := range inputFrameDownsync.InputList {
|
||||
// To avoid potential misuse of pointers
|
||||
pR.LastAllConfirmedInputList[i] = v
|
||||
}
|
||||
if pR.inputFrameIdDebuggable(clientInputFrameId) {
|
||||
Logger.Info("inputFrame lifecycle#2[allconfirmed]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,11 +635,11 @@ func (pR *Room) StopBattleForSettlement() {
|
||||
}
|
||||
pR.State = RoomBattleStateIns.STOPPING_BATTLE_FOR_SETTLEMENT
|
||||
Logger.Info("Stopping the `battleMainLoop` for:", zap.Any("roomId", pR.Id))
|
||||
pR.Tick++
|
||||
pR.RenderFrameId++
|
||||
for playerId, _ := range pR.Players {
|
||||
assembledFrame := pb.RoomDownsyncFrame{
|
||||
Id: pR.Tick,
|
||||
RefFrameId: pR.Tick, // Hardcoded for now.
|
||||
Id: pR.RenderFrameId,
|
||||
RefFrameId: pR.RenderFrameId, // Hardcoded for now.
|
||||
Players: toPbPlayers(pR.Players),
|
||||
SentAt: utils.UnixtimeMilli(),
|
||||
CountdownNanos: -1, // TODO: Replace this magic constant!
|
||||
@ -661,7 +677,7 @@ func (pR *Room) onBattlePrepare(cb BattleStartCbType) {
|
||||
}
|
||||
|
||||
battleReadyToStartFrame := pb.RoomDownsyncFrame{
|
||||
Id: pR.Tick,
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
SentAt: utils.UnixtimeMilli(),
|
||||
RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START,
|
||||
@ -733,6 +749,8 @@ func (pR *Room) onDismissed() {
|
||||
|
||||
// Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference.
|
||||
pR.Players = make(map[int32]*Player)
|
||||
pR.PlayersArr = make([]*Player, pR.Capacity)
|
||||
pR.CollisionSysMap = make(map[int32]*resolv.Object)
|
||||
pR.PlayerDownsyncSessionDict = make(map[int32]*websocket.Conn)
|
||||
pR.PlayerSignalToCloseDict = make(map[int32]SignalToCloseConnCbType)
|
||||
|
||||
@ -908,7 +926,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool {
|
||||
switch pPlayer.BattleState {
|
||||
case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK:
|
||||
playerAckedFrame = pb.RoomDownsyncFrame{
|
||||
Id: pR.Tick,
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
SentAt: utils.UnixtimeMilli(),
|
||||
RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_ADDED_AND_ACKED,
|
||||
@ -916,7 +934,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool {
|
||||
}
|
||||
case PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK:
|
||||
playerAckedFrame = pb.RoomDownsyncFrame{
|
||||
Id: pR.Tick,
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
SentAt: utils.UnixtimeMilli(),
|
||||
RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED,
|
||||
@ -963,12 +981,15 @@ func (pR *Room) sendSafely(s interface{}, playerId int32) {
|
||||
}
|
||||
}()
|
||||
|
||||
var resp *pb.WsResp = nil
|
||||
var pResp *pb.WsResp = nil
|
||||
|
||||
switch v := s.(type) {
|
||||
case pb.WsResp:
|
||||
resp := s.(pb.WsResp)
|
||||
pResp = &resp
|
||||
case pb.RoomDownsyncFrame:
|
||||
roomDownsyncFrame := s.(pb.RoomDownsyncFrame)
|
||||
resp = &pb.WsResp{
|
||||
pResp = &pb.WsResp{
|
||||
Ret: int32(Constants.RetCode.Ok),
|
||||
EchoedMsgId: int32(0),
|
||||
Act: DOWNSYNC_MSG_ACT_ROOM_FRAME,
|
||||
@ -976,7 +997,7 @@ func (pR *Room) sendSafely(s interface{}, playerId int32) {
|
||||
}
|
||||
case []*pb.InputFrameDownsync:
|
||||
toSendFrames := s.([]*pb.InputFrameDownsync)
|
||||
resp = &pb.WsResp{
|
||||
pResp = &pb.WsResp{
|
||||
Ret: int32(Constants.RetCode.Ok),
|
||||
EchoedMsgId: int32(0),
|
||||
Act: DOWNSYNC_MSG_ACT_INPUT_BATCH,
|
||||
@ -986,7 +1007,7 @@ func (pR *Room) sendSafely(s interface{}, playerId int32) {
|
||||
panic(fmt.Sprintf("Unknown downsync message type, roomId=%v, playerId=%v, roomState=%v, v=%v", pR.Id, playerId, v))
|
||||
}
|
||||
|
||||
theBytes, marshalErr := proto.Marshal(resp)
|
||||
theBytes, marshalErr := proto.Marshal(pResp)
|
||||
if nil != marshalErr {
|
||||
panic(fmt.Sprintf("Error marshaling downsync message: roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
|
||||
}
|
||||
@ -996,156 +1017,142 @@ func (pR *Room) sendSafely(s interface{}, playerId int32) {
|
||||
}
|
||||
}
|
||||
|
||||
func (pR *Room) shouldPrefabInputFrameDownsync(renderFrameId int32) bool {
|
||||
return ((renderFrameId & ((1 << pR.InputScaleFrames) - 1)) == 0)
|
||||
}
|
||||
|
||||
func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDownsync {
|
||||
/*
|
||||
Kindly note that on backend the prefab is much simpler than its frontend counterpart, because frontend will upsync its latest command immediately if there's any change w.r.t. its own prev cmd, thus if no upsync received from a frontend,
|
||||
- EITHER it's due to local lag and bad network,
|
||||
- OR there's no change w.r.t. to its prev cmd.
|
||||
*/
|
||||
var currInputFrameDownsync *pb.InputFrameDownsync = nil
|
||||
|
||||
if 0 == inputFrameId && 0 == pR.AllPlayerInputsBuffer.Cnt {
|
||||
currInputFrameDownsync = &pb.InputFrameDownsync{
|
||||
InputFrameId: 0,
|
||||
InputList: make([]uint64, pR.Capacity),
|
||||
ConfirmedList: uint64(0),
|
||||
}
|
||||
} else {
|
||||
tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId - 1)
|
||||
if nil == tmp {
|
||||
panic(fmt.Sprintf("Error prefabbing inputFrameDownsync: roomId=%v, AllPlayerInputsBuffer=%v", pR.Id, pR.AllPlayerInputsBufferString()))
|
||||
}
|
||||
prevInputFrameDownsync := tmp.(*pb.InputFrameDownsync)
|
||||
currInputList := prevInputFrameDownsync.InputList // Would be a clone of the values
|
||||
currInputFrameDownsync = &pb.InputFrameDownsync{
|
||||
InputFrameId: inputFrameId,
|
||||
InputList: currInputList,
|
||||
ConfirmedList: uint64(0),
|
||||
}
|
||||
}
|
||||
|
||||
pR.AllPlayerInputsBuffer.Put(currInputFrameDownsync)
|
||||
return currInputFrameDownsync
|
||||
}
|
||||
|
||||
func (pR *Room) forceConfirmationIfApplicable() uint64 {
|
||||
// Force confirmation of non-all-confirmed inputFrame EXACTLY ONE AT A TIME, returns the non-confirmed mask of players, e.g. in a 4-player-battle returning 1001 means that players with JoinIndex=1 and JoinIndex=4 are non-confirmed for inputFrameId2
|
||||
renderFrameId1 := (pR.RenderFrameId - pR.NstDelayFrames) // the renderFrameId which should've been rendered on frontend
|
||||
if 0 > renderFrameId1 || !pR.shouldPrefabInputFrameDownsync(renderFrameId1) {
|
||||
/*
|
||||
The backend "shouldPrefabInputFrameDownsync" shares the same rule as frontend "shouldGenerateInputFrameUpsync".
|
||||
*/
|
||||
return 0
|
||||
}
|
||||
|
||||
inputFrameId2 := pR.ConvertToInputFrameId(renderFrameId1, 0) // The inputFrame to force confirmation (if necessary)
|
||||
tmp := pR.AllPlayerInputsBuffer.GetByFrameId(inputFrameId2)
|
||||
if nil == tmp {
|
||||
panic(fmt.Sprintf("inputFrameId2=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! AllPlayerInputsBuffer=%v", inputFrameId2, pR.Id, pR.AllPlayerInputsBufferString()))
|
||||
}
|
||||
inputFrame2 := tmp.(*pb.InputFrameDownsync)
|
||||
|
||||
totPlayerCnt := uint32(pR.Capacity)
|
||||
allConfirmedMask := uint64((1 << totPlayerCnt) - 1)
|
||||
if swapped := atomic.CompareAndSwapUint64(&(inputFrame2.ConfirmedList), allConfirmedMask, allConfirmedMask); swapped {
|
||||
Logger.Info(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v, no need to force confirmation of it", inputFrameId2, pR.Id))
|
||||
return 0
|
||||
}
|
||||
|
||||
// Force confirmation of "inputFrame2"
|
||||
oldConfirmedList := atomic.LoadUint64(&(inputFrame2.ConfirmedList))
|
||||
atomic.StoreUint64(&(inputFrame2.ConfirmedList), allConfirmedMask)
|
||||
pR.onInputFrameDownsyncAllConfirmed(inputFrame2, -1)
|
||||
|
||||
return (oldConfirmedList^allConfirmedMask)
|
||||
}
|
||||
|
||||
func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRenderFrameId int32) {
|
||||
if fromRenderFrameId >= toRenderFrameId {
|
||||
return
|
||||
}
|
||||
|
||||
totPlayerCnt := uint32(pR.Capacity)
|
||||
allConfirmedMask := uint64((1 << totPlayerCnt) - 1)
|
||||
|
||||
for collisionSysRenderFrameId := fromRenderFrameId; collisionSysRenderFrameId < toRenderFrameId; collisionSysRenderFrameId++ {
|
||||
delayedInputFrameId := pR.ConvertToInputFrameId(collisionSysRenderFrameId, pR.InputDelayFrames)
|
||||
if 0 <= delayedInputFrameId {
|
||||
tmp := pR.AllPlayerInputsBuffer.GetByFrameId(delayedInputFrameId)
|
||||
if nil == tmp {
|
||||
panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString()))
|
||||
}
|
||||
delayedInputFrame := tmp.(pb.InputFrameDownsync)
|
||||
if swapped := atomic.CompareAndSwapUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask, allConfirmedMask); !swapped {
|
||||
panic(fmt.Sprintf("delayedInputFrameId=%v is not yet all-confirmed for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString()))
|
||||
}
|
||||
|
||||
inputList := delayedInputFrame.InputList
|
||||
// Ordered by joinIndex to guarantee determinism
|
||||
for _, player := range pR.PlayersArr {
|
||||
joinIndex := player.JoinIndex
|
||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||
playerCollider := pR.CollisionSysMap[collisionPlayerIndex]
|
||||
encodedInput := inputList[joinIndex-1]
|
||||
decodedInput := DIRECTION_DECODER[encodedInput]
|
||||
decodedInputSpeedFactor := DIRECTION_DECODER_INVERSE_LENGTH[encodedInput]
|
||||
baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor
|
||||
dx := baseChange * float64(decodedInput[0])
|
||||
dy := baseChange * float64(decodedInput[1])
|
||||
if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil {
|
||||
changeWithCollision := collision.ContactWithObject(collision.Objects[0])
|
||||
dx = changeWithCollision.X()
|
||||
dy = changeWithCollision.Y()
|
||||
}
|
||||
playerCollider.X += dx
|
||||
playerCollider.Y += dy
|
||||
// Update in "collision space"
|
||||
playerCollider.Update()
|
||||
|
||||
player.Dir.Dx = decodedInput[0]
|
||||
player.Dir.Dy = decodedInput[1]
|
||||
player.X = playerCollider.X
|
||||
player.Y = playerCollider.Y
|
||||
}
|
||||
}
|
||||
|
||||
newRenderFrame := pb.RoomDownsyncFrame{
|
||||
Id: collisionSysRenderFrameId+1,
|
||||
RefFrameId: collisionSysRenderFrameId,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
SentAt: utils.UnixtimeMilli(),
|
||||
CountdownNanos: (pR.BattleDurationNanos - int64(collisionSysRenderFrameId)*int64(pR.RollbackEstimatedDt*1000000000)),
|
||||
}
|
||||
pR.RenderFrameBuffer.Put(&newRenderFrame)
|
||||
pR.CurDynamicsRenderFrameId++
|
||||
}
|
||||
}
|
||||
|
||||
func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool {
|
||||
return 0 == (inputFrameId % 10)
|
||||
}
|
||||
|
||||
func (pR *Room) refreshColliders() {
|
||||
gravity := box2d.MakeB2Vec2(0.0, 0.0)
|
||||
world := box2d.MakeB2World(gravity)
|
||||
world.SetContactFilter(&box2d.B2ContactFilter{})
|
||||
pR.CollidableWorld = &world
|
||||
|
||||
Logger.Info("Begins players collider processing:", zap.Any("roomId", pR.Id))
|
||||
for _, player := range pR.Players {
|
||||
var bdDef box2d.B2BodyDef
|
||||
colliderOffset := box2d.MakeB2Vec2(0, 0) // Matching that of client-side setting.
|
||||
bdDef = box2d.MakeB2BodyDef()
|
||||
bdDef.Type = box2d.B2BodyType.B2_dynamicBody
|
||||
bdDef.Position.Set(player.X+colliderOffset.X, player.Y+colliderOffset.Y)
|
||||
|
||||
b2Body := pR.CollidableWorld.CreateBody(&bdDef)
|
||||
|
||||
b2CircleShape := box2d.MakeB2CircleShape()
|
||||
b2CircleShape.M_radius = 32 // Matching that of client-side setting.
|
||||
|
||||
fd := box2d.MakeB2FixtureDef()
|
||||
fd.Shape = &b2CircleShape
|
||||
fd.Filter.CategoryBits = COLLISION_CATEGORY_CONTROLLED_PLAYER
|
||||
fd.Filter.MaskBits = COLLISION_MASK_FOR_CONTROLLED_PLAYER
|
||||
fd.Density = 0.0
|
||||
b2Body.CreateFixtureFromDef(&fd)
|
||||
|
||||
player.CollidableBody = b2Body
|
||||
b2Body.SetUserData(player)
|
||||
}
|
||||
Logger.Info("Ends players collider processing:", zap.Any("roomId", pR.Id))
|
||||
|
||||
Logger.Info("Begins barriers collider processing:", zap.Any("roomId", pR.Id))
|
||||
for _, barrier := range pR.Barriers {
|
||||
var bdDef box2d.B2BodyDef
|
||||
bdDef.Type = box2d.B2BodyType.B2_dynamicBody
|
||||
bdDef = box2d.MakeB2BodyDef()
|
||||
bdDef.Position.Set(barrier.Boundary.Anchor.X, barrier.Boundary.Anchor.Y)
|
||||
|
||||
b2Body := pR.CollidableWorld.CreateBody(&bdDef)
|
||||
|
||||
pointsCount := len(barrier.Boundary.Points)
|
||||
|
||||
b2Vertices := make([]box2d.B2Vec2, pointsCount)
|
||||
for vIndex, v2 := range barrier.Boundary.Points {
|
||||
b2Vertices[vIndex] = v2.ToB2Vec2()
|
||||
}
|
||||
|
||||
b2PolygonShape := box2d.MakeB2PolygonShape()
|
||||
b2PolygonShape.Set(b2Vertices, pointsCount)
|
||||
|
||||
fd := box2d.MakeB2FixtureDef()
|
||||
fd.Shape = &b2PolygonShape
|
||||
fd.Filter.CategoryBits = COLLISION_CATEGORY_BARRIER
|
||||
fd.Filter.MaskBits = COLLISION_MASK_FOR_BARRIER
|
||||
fd.Density = 0.0
|
||||
b2Body.CreateFixtureFromDef(&fd)
|
||||
|
||||
barrier.CollidableBody = b2Body
|
||||
b2Body.SetUserData(barrier)
|
||||
}
|
||||
Logger.Info("Ends barriers collider processing:", zap.Any("roomId", pR.Id))
|
||||
|
||||
listener := RoomBattleContactListener{
|
||||
name: "DelayNoMore",
|
||||
room: pR,
|
||||
}
|
||||
/*
|
||||
* Setting a "ContactListener" for "pR.CollidableWorld"
|
||||
* will only trigger corresponding callbacks in the
|
||||
* SAME GOROUTINE of "pR.CollidableWorld.Step(...)" according
|
||||
* to "https://github.com/ByteArena/box2d/blob/master/DynamicsB2World.go" and
|
||||
* "https://github.com/ByteArena/box2d/blob/master/DynamicsB2Contact.go".
|
||||
*
|
||||
* The invocation-chain involves "Step -> SolveTOI -> B2ContactUpdate -> [BeginContact, EndContact, PreSolve]".
|
||||
*/
|
||||
pR.CollidableWorld.SetContactListener(listener)
|
||||
}
|
||||
|
||||
type RoomBattleContactListener struct {
|
||||
name string
|
||||
room *Room
|
||||
}
|
||||
|
||||
// Implementing the GolangBox2d contact listeners [begins].
|
||||
/**
|
||||
* Note that the execution of these listeners is within the SAME GOROUTINE as that of "`battleMainLoop` in the same room".
|
||||
* See the comments in `Room.refreshColliders()` for details.
|
||||
*/
|
||||
func (l RoomBattleContactListener) BeginContact(contact box2d.B2ContactInterface) {
|
||||
var pBarrier *Barrier
|
||||
var pPlayer *Player
|
||||
|
||||
switch v := contact.GetNodeA().Other.GetUserData().(type) {
|
||||
case *Barrier:
|
||||
pBarrier = v
|
||||
case *Player:
|
||||
pPlayer = v
|
||||
default:
|
||||
//
|
||||
}
|
||||
|
||||
switch v := contact.GetNodeB().Other.GetUserData().(type) {
|
||||
case *Barrier:
|
||||
pBarrier = v
|
||||
case *Player:
|
||||
pPlayer = v
|
||||
default:
|
||||
}
|
||||
|
||||
if pBarrier != nil && pPlayer != nil {
|
||||
Logger.Info("player begins collision with barrier:", zap.Any("barrier", pBarrier), zap.Any("player", pPlayer))
|
||||
// TODO: Push back player
|
||||
joinIndex := player.JoinIndex
|
||||
pR.PlayersArr[joinIndex-1] = player
|
||||
}
|
||||
}
|
||||
|
||||
func (l RoomBattleContactListener) EndContact(contact box2d.B2ContactInterface) {
|
||||
var pBarrier *Barrier
|
||||
var pPlayer *Player
|
||||
|
||||
switch v := contact.GetNodeA().Other.GetUserData().(type) {
|
||||
case *Barrier:
|
||||
pBarrier = v
|
||||
case *Player:
|
||||
pPlayer = v
|
||||
default:
|
||||
}
|
||||
|
||||
switch v := contact.GetNodeB().Other.GetUserData().(type) {
|
||||
case *Barrier:
|
||||
pBarrier = v
|
||||
case *Player:
|
||||
pPlayer = v
|
||||
default:
|
||||
}
|
||||
|
||||
if pBarrier != nil && pPlayer != nil {
|
||||
Logger.Info("player ends collision with barrier:", zap.Any("barrier", pBarrier), zap.Any("player", pPlayer))
|
||||
}
|
||||
}
|
||||
|
||||
func (l RoomBattleContactListener) PreSolve(contact box2d.B2ContactInterface, oldManifold box2d.B2Manifold) {
|
||||
//fmt.Printf("I am PreSolve %s\n", l.name);
|
||||
}
|
||||
|
||||
func (l RoomBattleContactListener) PostSolve(contact box2d.B2ContactInterface, impulse *box2d.B2ContactImpulse) {
|
||||
//fmt.Printf("PostSolve %s\n", l.name);
|
||||
}
|
||||
|
||||
// Implementing the GolangBox2d contact listeners [ends].
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"container/heap"
|
||||
"fmt"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/solarlune/resolv"
|
||||
"go.uber.org/zap"
|
||||
. "server/common"
|
||||
"sync"
|
||||
@ -100,27 +101,31 @@ func InitRoomHeapManager() {
|
||||
pq[i] = &Room{
|
||||
Id: int32(i + 1),
|
||||
Players: make(map[int32]*Player),
|
||||
PlayersArr: make([]*Player, roomCapacity),
|
||||
CollisionSysMap: make(map[int32]*resolv.Object),
|
||||
PlayerDownsyncSessionDict: make(map[int32]*websocket.Conn),
|
||||
PlayerSignalToCloseDict: make(map[int32]SignalToCloseConnCbType),
|
||||
Capacity: roomCapacity,
|
||||
Score: calRoomScore(0, roomCapacity, currentRoomBattleState),
|
||||
State: currentRoomBattleState,
|
||||
Index: i,
|
||||
Tick: 0,
|
||||
RenderFrameId: 0,
|
||||
CurDynamicsRenderFrameId: 0,
|
||||
EffectivePlayerCount: 0,
|
||||
//BattleDurationNanos: int64(5 * 1000 * 1000 * 1000),
|
||||
BattleDurationNanos: int64(30 * 1000 * 1000 * 1000),
|
||||
ServerFPS: 60,
|
||||
Barriers: make(map[int32]*Barrier),
|
||||
AccumulatedLocalIdForBullets: 0,
|
||||
AllPlayerInputsBuffer: NewRingBuffer(1024),
|
||||
RenderFrameBuffer: NewRingBuffer(1024),
|
||||
RenderFrameBuffer: NewRingBuffer(1024),
|
||||
LastAllConfirmedInputFrameId: -1,
|
||||
LastAllConfirmedInputFrameIdWithChange: -1,
|
||||
LastAllConfirmedInputList: make([]uint64, roomCapacity),
|
||||
InputDelayFrames: 4,
|
||||
NstDelayFrames: 2,
|
||||
InputScaleFrames: 2,
|
||||
JoinIndexBooleanArr: joinIndexBooleanArr,
|
||||
RollbackEstimatedDt: float64(1.0) / 60,
|
||||
}
|
||||
roomMap[pq[i].Id] = pq[i]
|
||||
pq[i].ChooseStage()
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
const (
|
||||
LOW_SCORE_TREASURE_TYPE = 1
|
||||
HIGH_SCORE_TREASURE_TYPE = 2
|
||||
SPEED_SHOES_TYPE = 3
|
||||
SPEED_SHOES_TYPE = 3
|
||||
|
||||
LOW_SCORE_TREASURE_SCORE = 100
|
||||
HIGH_SCORE_TREASURE_SCORE = 200
|
||||
|
@ -407,7 +407,7 @@ type Player struct {
|
||||
X float64 `protobuf:"fixed64,2,opt,name=x,proto3" json:"x,omitempty"`
|
||||
Y float64 `protobuf:"fixed64,3,opt,name=y,proto3" json:"y,omitempty"`
|
||||
Dir *Direction `protobuf:"bytes,4,opt,name=dir,proto3" json:"dir,omitempty"`
|
||||
Speed int32 `protobuf:"varint,5,opt,name=speed,proto3" json:"speed,omitempty"`
|
||||
Speed float64 `protobuf:"fixed64,5,opt,name=speed,proto3" json:"speed,omitempty"`
|
||||
BattleState int32 `protobuf:"varint,6,opt,name=battleState,proto3" json:"battleState,omitempty"`
|
||||
LastMoveGmtMillis int32 `protobuf:"varint,7,opt,name=lastMoveGmtMillis,proto3" json:"lastMoveGmtMillis,omitempty"`
|
||||
Score int32 `protobuf:"varint,10,opt,name=score,proto3" json:"score,omitempty"`
|
||||
@ -475,7 +475,7 @@ func (x *Player) GetDir() *Direction {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Player) GetSpeed() int32 {
|
||||
func (x *Player) GetSpeed() float64 {
|
||||
if x != nil {
|
||||
return x.Speed
|
||||
}
|
||||
@ -596,6 +596,171 @@ func (x *PlayerMeta) GetJoinIndex() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type InputFrameUpsync struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
InputFrameId int32 `protobuf:"varint,1,opt,name=inputFrameId,proto3" json:"inputFrameId,omitempty"`
|
||||
EncodedDir int32 `protobuf:"varint,6,opt,name=encodedDir,proto3" json:"encodedDir,omitempty"`
|
||||
}
|
||||
|
||||
func (x *InputFrameUpsync) Reset() {
|
||||
*x = InputFrameUpsync{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *InputFrameUpsync) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*InputFrameUpsync) ProtoMessage() {}
|
||||
|
||||
func (x *InputFrameUpsync) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use InputFrameUpsync.ProtoReflect.Descriptor instead.
|
||||
func (*InputFrameUpsync) Descriptor() ([]byte, []int) {
|
||||
return file_room_downsync_frame_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *InputFrameUpsync) GetInputFrameId() int32 {
|
||||
if x != nil {
|
||||
return x.InputFrameId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *InputFrameUpsync) GetEncodedDir() int32 {
|
||||
if x != nil {
|
||||
return x.EncodedDir
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type InputFrameDownsync struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
InputFrameId int32 `protobuf:"varint,1,opt,name=inputFrameId,proto3" json:"inputFrameId,omitempty"`
|
||||
InputList []uint64 `protobuf:"varint,2,rep,packed,name=inputList,proto3" json:"inputList,omitempty"` // Indexed by "joinIndex", we try to compress the "single player input" into 1 word (64-bit for 64-bit Golang runtime) because atomic compare-and-swap only works on 1 word. Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible.
|
||||
ConfirmedList uint64 `protobuf:"varint,3,opt,name=confirmedList,proto3" json:"confirmedList,omitempty"` // Indexed by "joinIndex", same compression concern as above
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) Reset() {
|
||||
*x = InputFrameDownsync{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*InputFrameDownsync) ProtoMessage() {}
|
||||
|
||||
func (x *InputFrameDownsync) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use InputFrameDownsync.ProtoReflect.Descriptor instead.
|
||||
func (*InputFrameDownsync) Descriptor() ([]byte, []int) {
|
||||
return file_room_downsync_frame_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) GetInputFrameId() int32 {
|
||||
if x != nil {
|
||||
return x.InputFrameId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) GetInputList() []uint64 {
|
||||
if x != nil {
|
||||
return x.InputList
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) GetConfirmedList() uint64 {
|
||||
if x != nil {
|
||||
return x.ConfirmedList
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type HeartbeatUpsync struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ClientTimestamp int64 `protobuf:"varint,1,opt,name=clientTimestamp,proto3" json:"clientTimestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HeartbeatUpsync) Reset() {
|
||||
*x = HeartbeatUpsync{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HeartbeatUpsync) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HeartbeatUpsync) ProtoMessage() {}
|
||||
|
||||
func (x *HeartbeatUpsync) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[10]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HeartbeatUpsync.ProtoReflect.Descriptor instead.
|
||||
func (*HeartbeatUpsync) Descriptor() ([]byte, []int) {
|
||||
return file_room_downsync_frame_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *HeartbeatUpsync) GetClientTimestamp() int64 {
|
||||
if x != nil {
|
||||
return x.ClientTimestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type RoomDownsyncFrame struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -612,7 +777,7 @@ type RoomDownsyncFrame struct {
|
||||
func (x *RoomDownsyncFrame) Reset() {
|
||||
*x = RoomDownsyncFrame{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[8]
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -625,7 +790,7 @@ func (x *RoomDownsyncFrame) String() string {
|
||||
func (*RoomDownsyncFrame) ProtoMessage() {}
|
||||
|
||||
func (x *RoomDownsyncFrame) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[8]
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[11]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -638,7 +803,7 @@ func (x *RoomDownsyncFrame) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoomDownsyncFrame.ProtoReflect.Descriptor instead.
|
||||
func (*RoomDownsyncFrame) Descriptor() ([]byte, []int) {
|
||||
return file_room_downsync_frame_proto_rawDescGZIP(), []int{8}
|
||||
return file_room_downsync_frame_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *RoomDownsyncFrame) GetId() int32 {
|
||||
@ -683,171 +848,6 @@ func (x *RoomDownsyncFrame) GetPlayerMetas() map[int32]*PlayerMeta {
|
||||
return nil
|
||||
}
|
||||
|
||||
type InputFrameUpsync struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
InputFrameId int32 `protobuf:"varint,1,opt,name=inputFrameId,proto3" json:"inputFrameId,omitempty"`
|
||||
EncodedDir int32 `protobuf:"varint,6,opt,name=encodedDir,proto3" json:"encodedDir,omitempty"`
|
||||
}
|
||||
|
||||
func (x *InputFrameUpsync) Reset() {
|
||||
*x = InputFrameUpsync{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *InputFrameUpsync) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*InputFrameUpsync) ProtoMessage() {}
|
||||
|
||||
func (x *InputFrameUpsync) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use InputFrameUpsync.ProtoReflect.Descriptor instead.
|
||||
func (*InputFrameUpsync) Descriptor() ([]byte, []int) {
|
||||
return file_room_downsync_frame_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *InputFrameUpsync) GetInputFrameId() int32 {
|
||||
if x != nil {
|
||||
return x.InputFrameId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *InputFrameUpsync) GetEncodedDir() int32 {
|
||||
if x != nil {
|
||||
return x.EncodedDir
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type InputFrameDownsync struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
InputFrameId int32 `protobuf:"varint,1,opt,name=inputFrameId,proto3" json:"inputFrameId,omitempty"`
|
||||
InputList []uint64 `protobuf:"varint,2,rep,packed,name=inputList,proto3" json:"inputList,omitempty"` // Indexed by "joinIndex", we try to compress the "single player input" into 1 word (64-bit for 64-bit Golang runtime) because atomic compare-and-swap only works on 1 word. Although CAS on custom struct is possible in Golang 1.19 https://pkg.go.dev/sync/atomic@go1.19.1#Value.CompareAndSwap, using a single word is still faster whenever possible.
|
||||
ConfirmedList uint64 `protobuf:"varint,3,opt,name=confirmedList,proto3" json:"confirmedList,omitempty"` // Indexed by "joinIndex", same compression concern as above
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) Reset() {
|
||||
*x = InputFrameDownsync{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*InputFrameDownsync) ProtoMessage() {}
|
||||
|
||||
func (x *InputFrameDownsync) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[10]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use InputFrameDownsync.ProtoReflect.Descriptor instead.
|
||||
func (*InputFrameDownsync) Descriptor() ([]byte, []int) {
|
||||
return file_room_downsync_frame_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) GetInputFrameId() int32 {
|
||||
if x != nil {
|
||||
return x.InputFrameId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) GetInputList() []uint64 {
|
||||
if x != nil {
|
||||
return x.InputList
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *InputFrameDownsync) GetConfirmedList() uint64 {
|
||||
if x != nil {
|
||||
return x.ConfirmedList
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type HeartbeatUpsync struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ClientTimestamp int64 `protobuf:"varint,1,opt,name=clientTimestamp,proto3" json:"clientTimestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HeartbeatUpsync) Reset() {
|
||||
*x = HeartbeatUpsync{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HeartbeatUpsync) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HeartbeatUpsync) ProtoMessage() {}
|
||||
|
||||
func (x *HeartbeatUpsync) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_room_downsync_frame_proto_msgTypes[11]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HeartbeatUpsync.ProtoReflect.Descriptor instead.
|
||||
func (*HeartbeatUpsync) Descriptor() ([]byte, []int) {
|
||||
return file_room_downsync_frame_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *HeartbeatUpsync) GetClientTimestamp() int64 {
|
||||
if x != nil {
|
||||
return x.ClientTimestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type WsReq struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -1119,7 +1119,7 @@ var file_room_downsync_frame_proto_rawDesc = []byte{
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72,
|
||||
0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x12, 0x20, 0x0a,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x12, 0x20, 0x0a,
|
||||
0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
|
||||
0x2c, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x76, 0x65, 0x47, 0x6d, 0x74, 0x4d, 0x69,
|
||||
@ -1138,54 +1138,54 @@ var file_room_downsync_frame_proto_rawDesc = []byte{
|
||||
0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6a, 0x6f, 0x69, 0x6e,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6a, 0x6f, 0x69,
|
||||
0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xd7, 0x03, 0x0a, 0x11, 0x52, 0x6f, 0x6f, 0x6d, 0x44,
|
||||
0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a,
|
||||
0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
|
||||
0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x49, 0x0a, 0x07,
|
||||
0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e,
|
||||
0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e,
|
||||
0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d,
|
||||
0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07,
|
||||
0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41,
|
||||
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41, 0x74, 0x12,
|
||||
0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f,
|
||||
0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65,
|
||||
0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74,
|
||||
0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52,
|
||||
0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65,
|
||||
0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53,
|
||||
0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||
0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74,
|
||||
0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73,
|
||||
0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65,
|
||||
0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
|
||||
0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70,
|
||||
0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x56, 0x0a, 0x10, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46,
|
||||
0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e,
|
||||
0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
|
||||
0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e,
|
||||
0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x22, 0x7c,
|
||||
0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e,
|
||||
0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61,
|
||||
0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e,
|
||||
0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22,
|
||||
0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65,
|
||||
0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18,
|
||||
0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d,
|
||||
0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62,
|
||||
0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x22, 0xca, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a,
|
||||
0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6e, 0x70,
|
||||
0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72,
|
||||
0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f,
|
||||
0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12,
|
||||
0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xd7, 0x03, 0x0a, 0x11, 0x52, 0x6f,
|
||||
0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12,
|
||||
0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12,
|
||||
0x49, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46,
|
||||
0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65,
|
||||
0x6e, 0x74, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x74,
|
||||
0x41, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e,
|
||||
0x61, 0x6e, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c,
|
||||
0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72,
|
||||
0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45,
|
||||
0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61,
|
||||
0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
|
||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72,
|
||||
0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c,
|
||||
0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||
0x02, 0x38, 0x01, 0x22, 0xca, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73,
|
||||
0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12,
|
||||
@ -1250,10 +1250,10 @@ var file_room_downsync_frame_proto_goTypes = []interface{}{
|
||||
(*BattleColliderInfo)(nil), // 5: treasurehunterx.BattleColliderInfo
|
||||
(*Player)(nil), // 6: treasurehunterx.Player
|
||||
(*PlayerMeta)(nil), // 7: treasurehunterx.PlayerMeta
|
||||
(*RoomDownsyncFrame)(nil), // 8: treasurehunterx.RoomDownsyncFrame
|
||||
(*InputFrameUpsync)(nil), // 9: treasurehunterx.InputFrameUpsync
|
||||
(*InputFrameDownsync)(nil), // 10: treasurehunterx.InputFrameDownsync
|
||||
(*HeartbeatUpsync)(nil), // 11: treasurehunterx.HeartbeatUpsync
|
||||
(*InputFrameUpsync)(nil), // 8: treasurehunterx.InputFrameUpsync
|
||||
(*InputFrameDownsync)(nil), // 9: treasurehunterx.InputFrameDownsync
|
||||
(*HeartbeatUpsync)(nil), // 10: treasurehunterx.HeartbeatUpsync
|
||||
(*RoomDownsyncFrame)(nil), // 11: treasurehunterx.RoomDownsyncFrame
|
||||
(*WsReq)(nil), // 12: treasurehunterx.WsReq
|
||||
(*WsResp)(nil), // 13: treasurehunterx.WsResp
|
||||
nil, // 14: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry
|
||||
@ -1271,10 +1271,10 @@ var file_room_downsync_frame_proto_depIdxs = []int32{
|
||||
0, // 6: treasurehunterx.Player.dir:type_name -> treasurehunterx.Direction
|
||||
16, // 7: treasurehunterx.RoomDownsyncFrame.players:type_name -> treasurehunterx.RoomDownsyncFrame.PlayersEntry
|
||||
17, // 8: treasurehunterx.RoomDownsyncFrame.playerMetas:type_name -> treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry
|
||||
9, // 9: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync
|
||||
11, // 10: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync
|
||||
8, // 11: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame
|
||||
10, // 12: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync
|
||||
8, // 9: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync
|
||||
10, // 10: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync
|
||||
11, // 11: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame
|
||||
9, // 12: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync
|
||||
5, // 13: treasurehunterx.WsResp.bciFrame:type_name -> treasurehunterx.BattleColliderInfo
|
||||
3, // 14: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> treasurehunterx.Vec2DList
|
||||
4, // 15: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> treasurehunterx.Polygon2DList
|
||||
@ -1390,18 +1390,6 @@ func file_room_downsync_frame_proto_init() {
|
||||
}
|
||||
}
|
||||
file_room_downsync_frame_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RoomDownsyncFrame); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_room_downsync_frame_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*InputFrameUpsync); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@ -1413,7 +1401,7 @@ func file_room_downsync_frame_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_room_downsync_frame_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||
file_room_downsync_frame_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*InputFrameDownsync); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@ -1425,7 +1413,7 @@ func file_room_downsync_frame_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_room_downsync_frame_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||
file_room_downsync_frame_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HeartbeatUpsync); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@ -1437,6 +1425,18 @@ func file_room_downsync_frame_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_room_downsync_frame_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RoomDownsyncFrame); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_room_downsync_frame_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*WsReq); i {
|
||||
case 0:
|
||||
|
@ -45,7 +45,7 @@ message Player {
|
||||
double x = 2;
|
||||
double y = 3;
|
||||
Direction dir = 4;
|
||||
int32 speed = 5;
|
||||
double speed = 5;
|
||||
int32 battleState = 6;
|
||||
int32 lastMoveGmtMillis = 7;
|
||||
int32 score = 10;
|
||||
@ -61,15 +61,6 @@ message PlayerMeta {
|
||||
int32 joinIndex = 5;
|
||||
}
|
||||
|
||||
message RoomDownsyncFrame {
|
||||
int32 id = 1;
|
||||
int32 refFrameId = 2;
|
||||
map<int32, Player> players = 3;
|
||||
int64 sentAt = 4;
|
||||
int64 countdownNanos = 5;
|
||||
map<int32, PlayerMeta> playerMetas = 6;
|
||||
}
|
||||
|
||||
message InputFrameUpsync {
|
||||
int32 inputFrameId = 1;
|
||||
int32 encodedDir = 6;
|
||||
@ -85,6 +76,15 @@ message HeartbeatUpsync {
|
||||
int64 clientTimestamp = 1;
|
||||
}
|
||||
|
||||
message RoomDownsyncFrame {
|
||||
int32 id = 1;
|
||||
int32 refFrameId = 2;
|
||||
map<int32, Player> players = 3;
|
||||
int64 sentAt = 4;
|
||||
int64 countdownNanos = 5;
|
||||
map<int32, PlayerMeta> playerMetas = 6;
|
||||
}
|
||||
|
||||
message WsReq {
|
||||
int32 msgId = 1;
|
||||
int32 playerId = 2;
|
||||
|
@ -182,11 +182,11 @@ cc.Class({
|
||||
return ((renderFrameId - inputDelayFrames) >> this.inputScaleFrames);
|
||||
},
|
||||
|
||||
_convertToRenderFrameId(inputFrameId, inputDelayFrames) {
|
||||
_convertToFirstUsedRenderFrameId(inputFrameId, inputDelayFrames) {
|
||||
return ((inputFrameId << this.inputScaleFrames) + inputDelayFrames);
|
||||
},
|
||||
|
||||
_shouldGenerateInputFrameUpsync(renderFrameId) {
|
||||
shouldGenerateInputFrameUpsync(renderFrameId) {
|
||||
return ((renderFrameId & ((1 << this.inputScaleFrames)-1)) == 0);
|
||||
},
|
||||
|
||||
@ -608,7 +608,7 @@ cc.Class({
|
||||
|
||||
if (null != firstPredictedYetIncorrectInputFrameId) {
|
||||
const inputFrameId1 = firstPredictedYetIncorrectInputFrameId;
|
||||
const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId"
|
||||
const renderFrameId1 = self._convertToFirstUsedRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId"
|
||||
if (renderFrameId1 < self.renderFrameId) {
|
||||
/*
|
||||
A typical case is as follows.
|
||||
@ -787,7 +787,7 @@ cc.Class({
|
||||
try {
|
||||
let prevSelfInput = null, currSelfInput = null;
|
||||
const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here
|
||||
if (self._shouldGenerateInputFrameUpsync(self.renderFrameId)) {
|
||||
if (self.shouldGenerateInputFrameUpsync(self.renderFrameId)) {
|
||||
const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId);
|
||||
prevSelfInput = prevAndCurrInputs[0];
|
||||
currSelfInput = prevAndCurrInputs[1];
|
||||
@ -937,7 +937,7 @@ cc.Class({
|
||||
_createRoomDownsyncFrameLocally(renderFrameId, collisionSys, collisionSysMap) {
|
||||
const self = this;
|
||||
const prevRenderFrameId = renderFrameId-1;
|
||||
const inputFrameForPrevRenderFrame = (
|
||||
const inputFrameAppliedOnPrevRenderFrame = (
|
||||
0 > prevRenderFrameId
|
||||
?
|
||||
null
|
||||
@ -948,11 +948,11 @@ cc.Class({
|
||||
// TODO: Find a better way to assign speeds instead of using "speedRefRenderFrameId".
|
||||
const speedRefRenderFrameId = prevRenderFrameId;
|
||||
const speedRefRenderFrame = (
|
||||
0 > prevRenderFrameId
|
||||
0 > speedRefRenderFrameId
|
||||
?
|
||||
null
|
||||
:
|
||||
self.recentRenderCache.getByFrameId(prevRenderFrameId)
|
||||
self.recentRenderCache.getByFrameId(speedRefRenderFrameId)
|
||||
);
|
||||
|
||||
const rdf = {
|
||||
@ -968,13 +968,13 @@ cc.Class({
|
||||
id: playerRichInfo.id,
|
||||
x: playerCollider.x,
|
||||
y: playerCollider.y,
|
||||
dir: self.ctrl.decodeDirection(null == inputFrameForPrevRenderFrame ? 0 : inputFrameForPrevRenderFrame.inputList[joinIndex-1]),
|
||||
dir: self.ctrl.decodeDirection(null == inputFrameAppliedOnPrevRenderFrame ? 0 : inputFrameAppliedOnPrevRenderFrame.inputList[joinIndex-1]),
|
||||
speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed),
|
||||
joinIndex: joinIndex
|
||||
};
|
||||
});
|
||||
if (
|
||||
null != inputFrameForPrevRenderFrame && self._allConfirmed(inputFrameForPrevRenderFrame.confirmedList)
|
||||
null != inputFrameAppliedOnPrevRenderFrame && self._allConfirmed(inputFrameAppliedOnPrevRenderFrame.confirmedList)
|
||||
&&
|
||||
self.lastAllConfirmedRenderFrameId >= prevRenderFrameId
|
||||
&&
|
||||
@ -1033,6 +1033,8 @@ cc.Class({
|
||||
playerCollider.y = player.y;
|
||||
});
|
||||
|
||||
// [WARNING] Traverse in the order of joinIndices to guarantee determinism.
|
||||
|
||||
/*
|
||||
This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd".
|
||||
*/
|
||||
@ -1040,8 +1042,8 @@ cc.Class({
|
||||
const renderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"
|
||||
const j = self._convertToInputFrameId(i, self.inputDelayFrames);
|
||||
const inputList = self.getCachedInputFrameDownsyncWithPrediction(j).inputList;
|
||||
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
|
||||
const joinIndex = playerRichInfo.joinIndex;
|
||||
for (let j in self.playerRichInfoArr) {
|
||||
const joinIndex = parseInt(j) + 1;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const player = renderFrame.players[playerId];
|
||||
@ -1055,12 +1057,11 @@ cc.Class({
|
||||
console.log("playerId=", playerId, "@renderFrameId=", i, ", delayedInputFrameId=", j, ", baseChange=", baseChange, ": x=", playerCollider.x, ", y=", playerCollider.y);
|
||||
}
|
||||
*/
|
||||
});
|
||||
}
|
||||
|
||||
collisionSys.update();
|
||||
const result = collisionSys.createResult(); // Can I reuse a "self.latestCollisionSysResult" object throughout the whole battle?
|
||||
|
||||
// [WARNING] Traverse in the order of joinIndices to guarantee determinism.
|
||||
for (let i in self.playerRichInfoArr) {
|
||||
const joinIndex = parseInt(i) + 1;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
|
@ -1815,7 +1815,7 @@ $root.treasurehunterx = (function() {
|
||||
if (message.dir != null && Object.hasOwnProperty.call(message, "dir"))
|
||||
$root.treasurehunterx.Direction.encode(message.dir, writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim();
|
||||
if (message.speed != null && Object.hasOwnProperty.call(message, "speed"))
|
||||
writer.uint32(/* id 5, wireType 0 =*/40).int32(message.speed);
|
||||
writer.uint32(/* id 5, wireType 1 =*/41).double(message.speed);
|
||||
if (message.battleState != null && Object.hasOwnProperty.call(message, "battleState"))
|
||||
writer.uint32(/* id 6, wireType 0 =*/48).int32(message.battleState);
|
||||
if (message.lastMoveGmtMillis != null && Object.hasOwnProperty.call(message, "lastMoveGmtMillis"))
|
||||
@ -1877,7 +1877,7 @@ $root.treasurehunterx = (function() {
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
message.speed = reader.int32();
|
||||
message.speed = reader.double();
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
@ -1950,8 +1950,8 @@ $root.treasurehunterx = (function() {
|
||||
return "dir." + error;
|
||||
}
|
||||
if (message.speed != null && message.hasOwnProperty("speed"))
|
||||
if (!$util.isInteger(message.speed))
|
||||
return "speed: integer expected";
|
||||
if (typeof message.speed !== "number")
|
||||
return "speed: number expected";
|
||||
if (message.battleState != null && message.hasOwnProperty("battleState"))
|
||||
if (!$util.isInteger(message.battleState))
|
||||
return "battleState: integer expected";
|
||||
@ -1994,7 +1994,7 @@ $root.treasurehunterx = (function() {
|
||||
message.dir = $root.treasurehunterx.Direction.fromObject(object.dir);
|
||||
}
|
||||
if (object.speed != null)
|
||||
message.speed = object.speed | 0;
|
||||
message.speed = Number(object.speed);
|
||||
if (object.battleState != null)
|
||||
message.battleState = object.battleState | 0;
|
||||
if (object.lastMoveGmtMillis != null)
|
||||
@ -2042,7 +2042,7 @@ $root.treasurehunterx = (function() {
|
||||
if (message.dir != null && message.hasOwnProperty("dir"))
|
||||
object.dir = $root.treasurehunterx.Direction.toObject(message.dir, options);
|
||||
if (message.speed != null && message.hasOwnProperty("speed"))
|
||||
object.speed = message.speed;
|
||||
object.speed = options.json && !isFinite(message.speed) ? String(message.speed) : message.speed;
|
||||
if (message.battleState != null && message.hasOwnProperty("battleState"))
|
||||
object.battleState = message.battleState;
|
||||
if (message.lastMoveGmtMillis != null && message.hasOwnProperty("lastMoveGmtMillis"))
|
||||
@ -2381,446 +2381,6 @@ $root.treasurehunterx = (function() {
|
||||
return PlayerMeta;
|
||||
})();
|
||||
|
||||
treasurehunterx.RoomDownsyncFrame = (function() {
|
||||
|
||||
/**
|
||||
* Properties of a RoomDownsyncFrame.
|
||||
* @memberof treasurehunterx
|
||||
* @interface IRoomDownsyncFrame
|
||||
* @property {number|null} [id] RoomDownsyncFrame id
|
||||
* @property {number|null} [refFrameId] RoomDownsyncFrame refFrameId
|
||||
* @property {Object.<string,treasurehunterx.Player>|null} [players] RoomDownsyncFrame players
|
||||
* @property {number|Long|null} [sentAt] RoomDownsyncFrame sentAt
|
||||
* @property {number|Long|null} [countdownNanos] RoomDownsyncFrame countdownNanos
|
||||
* @property {Object.<string,treasurehunterx.PlayerMeta>|null} [playerMetas] RoomDownsyncFrame playerMetas
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a new RoomDownsyncFrame.
|
||||
* @memberof treasurehunterx
|
||||
* @classdesc Represents a RoomDownsyncFrame.
|
||||
* @implements IRoomDownsyncFrame
|
||||
* @constructor
|
||||
* @param {treasurehunterx.IRoomDownsyncFrame=} [properties] Properties to set
|
||||
*/
|
||||
function RoomDownsyncFrame(properties) {
|
||||
this.players = {};
|
||||
this.playerMetas = {};
|
||||
if (properties)
|
||||
for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i)
|
||||
if (properties[keys[i]] != null)
|
||||
this[keys[i]] = properties[keys[i]];
|
||||
}
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame id.
|
||||
* @member {number} id
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.id = 0;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame refFrameId.
|
||||
* @member {number} refFrameId
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.refFrameId = 0;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame players.
|
||||
* @member {Object.<string,treasurehunterx.Player>} players
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.players = $util.emptyObject;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame sentAt.
|
||||
* @member {number|Long} sentAt
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.sentAt = $util.Long ? $util.Long.fromBits(0,0,false) : 0;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame countdownNanos.
|
||||
* @member {number|Long} countdownNanos
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.countdownNanos = $util.Long ? $util.Long.fromBits(0,0,false) : 0;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame playerMetas.
|
||||
* @member {Object.<string,treasurehunterx.PlayerMeta>} playerMetas
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.playerMetas = $util.emptyObject;
|
||||
|
||||
/**
|
||||
* Creates a new RoomDownsyncFrame instance using the specified properties.
|
||||
* @function create
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {treasurehunterx.IRoomDownsyncFrame=} [properties] Properties to set
|
||||
* @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame instance
|
||||
*/
|
||||
RoomDownsyncFrame.create = function create(properties) {
|
||||
return new RoomDownsyncFrame(properties);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes the specified RoomDownsyncFrame message. Does not implicitly {@link treasurehunterx.RoomDownsyncFrame.verify|verify} messages.
|
||||
* @function encode
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame message or plain object to encode
|
||||
* @param {$protobuf.Writer} [writer] Writer to encode to
|
||||
* @returns {$protobuf.Writer} Writer
|
||||
*/
|
||||
RoomDownsyncFrame.encode = function encode(message, writer) {
|
||||
if (!writer)
|
||||
writer = $Writer.create();
|
||||
if (message.id != null && Object.hasOwnProperty.call(message, "id"))
|
||||
writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id);
|
||||
if (message.refFrameId != null && Object.hasOwnProperty.call(message, "refFrameId"))
|
||||
writer.uint32(/* id 2, wireType 0 =*/16).int32(message.refFrameId);
|
||||
if (message.players != null && Object.hasOwnProperty.call(message, "players"))
|
||||
for (var keys = Object.keys(message.players), i = 0; i < keys.length; ++i) {
|
||||
writer.uint32(/* id 3, wireType 2 =*/26).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
|
||||
$root.treasurehunterx.Player.encode(message.players[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
|
||||
}
|
||||
if (message.sentAt != null && Object.hasOwnProperty.call(message, "sentAt"))
|
||||
writer.uint32(/* id 4, wireType 0 =*/32).int64(message.sentAt);
|
||||
if (message.countdownNanos != null && Object.hasOwnProperty.call(message, "countdownNanos"))
|
||||
writer.uint32(/* id 5, wireType 0 =*/40).int64(message.countdownNanos);
|
||||
if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas"))
|
||||
for (var keys = Object.keys(message.playerMetas), i = 0; i < keys.length; ++i) {
|
||||
writer.uint32(/* id 6, wireType 2 =*/50).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
|
||||
$root.treasurehunterx.PlayerMeta.encode(message.playerMetas[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
|
||||
}
|
||||
return writer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes the specified RoomDownsyncFrame message, length delimited. Does not implicitly {@link treasurehunterx.RoomDownsyncFrame.verify|verify} messages.
|
||||
* @function encodeDelimited
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame message or plain object to encode
|
||||
* @param {$protobuf.Writer} [writer] Writer to encode to
|
||||
* @returns {$protobuf.Writer} Writer
|
||||
*/
|
||||
RoomDownsyncFrame.encodeDelimited = function encodeDelimited(message, writer) {
|
||||
return this.encode(message, writer).ldelim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a RoomDownsyncFrame message from the specified reader or buffer.
|
||||
* @function decode
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
|
||||
* @param {number} [length] Message length if known beforehand
|
||||
* @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
RoomDownsyncFrame.decode = function decode(reader, length) {
|
||||
if (!(reader instanceof $Reader))
|
||||
reader = $Reader.create(reader);
|
||||
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.RoomDownsyncFrame(), key, value;
|
||||
while (reader.pos < end) {
|
||||
var tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
message.id = reader.int32();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
message.refFrameId = reader.int32();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (message.players === $util.emptyObject)
|
||||
message.players = {};
|
||||
var end2 = reader.uint32() + reader.pos;
|
||||
key = 0;
|
||||
value = null;
|
||||
while (reader.pos < end2) {
|
||||
var tag2 = reader.uint32();
|
||||
switch (tag2 >>> 3) {
|
||||
case 1:
|
||||
key = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
value = $root.treasurehunterx.Player.decode(reader, reader.uint32());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag2 & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
message.players[key] = value;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
message.sentAt = reader.int64();
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
message.countdownNanos = reader.int64();
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (message.playerMetas === $util.emptyObject)
|
||||
message.playerMetas = {};
|
||||
var end2 = reader.uint32() + reader.pos;
|
||||
key = 0;
|
||||
value = null;
|
||||
while (reader.pos < end2) {
|
||||
var tag2 = reader.uint32();
|
||||
switch (tag2 >>> 3) {
|
||||
case 1:
|
||||
key = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
value = $root.treasurehunterx.PlayerMeta.decode(reader, reader.uint32());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag2 & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
message.playerMetas[key] = value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a RoomDownsyncFrame message from the specified reader or buffer, length delimited.
|
||||
* @function decodeDelimited
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
|
||||
* @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
RoomDownsyncFrame.decodeDelimited = function decodeDelimited(reader) {
|
||||
if (!(reader instanceof $Reader))
|
||||
reader = new $Reader(reader);
|
||||
return this.decode(reader, reader.uint32());
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies a RoomDownsyncFrame message.
|
||||
* @function verify
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {Object.<string,*>} message Plain object to verify
|
||||
* @returns {string|null} `null` if valid, otherwise the reason why it is not
|
||||
*/
|
||||
RoomDownsyncFrame.verify = function verify(message) {
|
||||
if (typeof message !== "object" || message === null)
|
||||
return "object expected";
|
||||
if (message.id != null && message.hasOwnProperty("id"))
|
||||
if (!$util.isInteger(message.id))
|
||||
return "id: integer expected";
|
||||
if (message.refFrameId != null && message.hasOwnProperty("refFrameId"))
|
||||
if (!$util.isInteger(message.refFrameId))
|
||||
return "refFrameId: integer expected";
|
||||
if (message.players != null && message.hasOwnProperty("players")) {
|
||||
if (!$util.isObject(message.players))
|
||||
return "players: object expected";
|
||||
var key = Object.keys(message.players);
|
||||
for (var i = 0; i < key.length; ++i) {
|
||||
if (!$util.key32Re.test(key[i]))
|
||||
return "players: integer key{k:int32} expected";
|
||||
{
|
||||
var error = $root.treasurehunterx.Player.verify(message.players[key[i]]);
|
||||
if (error)
|
||||
return "players." + error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.sentAt != null && message.hasOwnProperty("sentAt"))
|
||||
if (!$util.isInteger(message.sentAt) && !(message.sentAt && $util.isInteger(message.sentAt.low) && $util.isInteger(message.sentAt.high)))
|
||||
return "sentAt: integer|Long expected";
|
||||
if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos"))
|
||||
if (!$util.isInteger(message.countdownNanos) && !(message.countdownNanos && $util.isInteger(message.countdownNanos.low) && $util.isInteger(message.countdownNanos.high)))
|
||||
return "countdownNanos: integer|Long expected";
|
||||
if (message.playerMetas != null && message.hasOwnProperty("playerMetas")) {
|
||||
if (!$util.isObject(message.playerMetas))
|
||||
return "playerMetas: object expected";
|
||||
var key = Object.keys(message.playerMetas);
|
||||
for (var i = 0; i < key.length; ++i) {
|
||||
if (!$util.key32Re.test(key[i]))
|
||||
return "playerMetas: integer key{k:int32} expected";
|
||||
{
|
||||
var error = $root.treasurehunterx.PlayerMeta.verify(message.playerMetas[key[i]]);
|
||||
if (error)
|
||||
return "playerMetas." + error;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a RoomDownsyncFrame message from a plain object. Also converts values to their respective internal types.
|
||||
* @function fromObject
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {Object.<string,*>} object Plain object
|
||||
* @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame
|
||||
*/
|
||||
RoomDownsyncFrame.fromObject = function fromObject(object) {
|
||||
if (object instanceof $root.treasurehunterx.RoomDownsyncFrame)
|
||||
return object;
|
||||
var message = new $root.treasurehunterx.RoomDownsyncFrame();
|
||||
if (object.id != null)
|
||||
message.id = object.id | 0;
|
||||
if (object.refFrameId != null)
|
||||
message.refFrameId = object.refFrameId | 0;
|
||||
if (object.players) {
|
||||
if (typeof object.players !== "object")
|
||||
throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected");
|
||||
message.players = {};
|
||||
for (var keys = Object.keys(object.players), i = 0; i < keys.length; ++i) {
|
||||
if (typeof object.players[keys[i]] !== "object")
|
||||
throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected");
|
||||
message.players[keys[i]] = $root.treasurehunterx.Player.fromObject(object.players[keys[i]]);
|
||||
}
|
||||
}
|
||||
if (object.sentAt != null)
|
||||
if ($util.Long)
|
||||
(message.sentAt = $util.Long.fromValue(object.sentAt)).unsigned = false;
|
||||
else if (typeof object.sentAt === "string")
|
||||
message.sentAt = parseInt(object.sentAt, 10);
|
||||
else if (typeof object.sentAt === "number")
|
||||
message.sentAt = object.sentAt;
|
||||
else if (typeof object.sentAt === "object")
|
||||
message.sentAt = new $util.LongBits(object.sentAt.low >>> 0, object.sentAt.high >>> 0).toNumber();
|
||||
if (object.countdownNanos != null)
|
||||
if ($util.Long)
|
||||
(message.countdownNanos = $util.Long.fromValue(object.countdownNanos)).unsigned = false;
|
||||
else if (typeof object.countdownNanos === "string")
|
||||
message.countdownNanos = parseInt(object.countdownNanos, 10);
|
||||
else if (typeof object.countdownNanos === "number")
|
||||
message.countdownNanos = object.countdownNanos;
|
||||
else if (typeof object.countdownNanos === "object")
|
||||
message.countdownNanos = new $util.LongBits(object.countdownNanos.low >>> 0, object.countdownNanos.high >>> 0).toNumber();
|
||||
if (object.playerMetas) {
|
||||
if (typeof object.playerMetas !== "object")
|
||||
throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected");
|
||||
message.playerMetas = {};
|
||||
for (var keys = Object.keys(object.playerMetas), i = 0; i < keys.length; ++i) {
|
||||
if (typeof object.playerMetas[keys[i]] !== "object")
|
||||
throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected");
|
||||
message.playerMetas[keys[i]] = $root.treasurehunterx.PlayerMeta.fromObject(object.playerMetas[keys[i]]);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a plain object from a RoomDownsyncFrame message. Also converts values to other types if specified.
|
||||
* @function toObject
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame
|
||||
* @param {$protobuf.IConversionOptions} [options] Conversion options
|
||||
* @returns {Object.<string,*>} Plain object
|
||||
*/
|
||||
RoomDownsyncFrame.toObject = function toObject(message, options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
var object = {};
|
||||
if (options.objects || options.defaults) {
|
||||
object.players = {};
|
||||
object.playerMetas = {};
|
||||
}
|
||||
if (options.defaults) {
|
||||
object.id = 0;
|
||||
object.refFrameId = 0;
|
||||
if ($util.Long) {
|
||||
var long = new $util.Long(0, 0, false);
|
||||
object.sentAt = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long;
|
||||
} else
|
||||
object.sentAt = options.longs === String ? "0" : 0;
|
||||
if ($util.Long) {
|
||||
var long = new $util.Long(0, 0, false);
|
||||
object.countdownNanos = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long;
|
||||
} else
|
||||
object.countdownNanos = options.longs === String ? "0" : 0;
|
||||
}
|
||||
if (message.id != null && message.hasOwnProperty("id"))
|
||||
object.id = message.id;
|
||||
if (message.refFrameId != null && message.hasOwnProperty("refFrameId"))
|
||||
object.refFrameId = message.refFrameId;
|
||||
var keys2;
|
||||
if (message.players && (keys2 = Object.keys(message.players)).length) {
|
||||
object.players = {};
|
||||
for (var j = 0; j < keys2.length; ++j)
|
||||
object.players[keys2[j]] = $root.treasurehunterx.Player.toObject(message.players[keys2[j]], options);
|
||||
}
|
||||
if (message.sentAt != null && message.hasOwnProperty("sentAt"))
|
||||
if (typeof message.sentAt === "number")
|
||||
object.sentAt = options.longs === String ? String(message.sentAt) : message.sentAt;
|
||||
else
|
||||
object.sentAt = options.longs === String ? $util.Long.prototype.toString.call(message.sentAt) : options.longs === Number ? new $util.LongBits(message.sentAt.low >>> 0, message.sentAt.high >>> 0).toNumber() : message.sentAt;
|
||||
if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos"))
|
||||
if (typeof message.countdownNanos === "number")
|
||||
object.countdownNanos = options.longs === String ? String(message.countdownNanos) : message.countdownNanos;
|
||||
else
|
||||
object.countdownNanos = options.longs === String ? $util.Long.prototype.toString.call(message.countdownNanos) : options.longs === Number ? new $util.LongBits(message.countdownNanos.low >>> 0, message.countdownNanos.high >>> 0).toNumber() : message.countdownNanos;
|
||||
if (message.playerMetas && (keys2 = Object.keys(message.playerMetas)).length) {
|
||||
object.playerMetas = {};
|
||||
for (var j = 0; j < keys2.length; ++j)
|
||||
object.playerMetas[keys2[j]] = $root.treasurehunterx.PlayerMeta.toObject(message.playerMetas[keys2[j]], options);
|
||||
}
|
||||
return object;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts this RoomDownsyncFrame to JSON.
|
||||
* @function toJSON
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
* @returns {Object.<string,*>} JSON object
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.toJSON = function toJSON() {
|
||||
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the default type url for RoomDownsyncFrame
|
||||
* @function getTypeUrl
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
|
||||
* @returns {string} The default type url
|
||||
*/
|
||||
RoomDownsyncFrame.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
|
||||
if (typeUrlPrefix === undefined) {
|
||||
typeUrlPrefix = "type.googleapis.com";
|
||||
}
|
||||
return typeUrlPrefix + "/treasurehunterx.RoomDownsyncFrame";
|
||||
};
|
||||
|
||||
return RoomDownsyncFrame;
|
||||
})();
|
||||
|
||||
treasurehunterx.InputFrameUpsync = (function() {
|
||||
|
||||
/**
|
||||
@ -3564,6 +3124,446 @@ $root.treasurehunterx = (function() {
|
||||
return HeartbeatUpsync;
|
||||
})();
|
||||
|
||||
treasurehunterx.RoomDownsyncFrame = (function() {
|
||||
|
||||
/**
|
||||
* Properties of a RoomDownsyncFrame.
|
||||
* @memberof treasurehunterx
|
||||
* @interface IRoomDownsyncFrame
|
||||
* @property {number|null} [id] RoomDownsyncFrame id
|
||||
* @property {number|null} [refFrameId] RoomDownsyncFrame refFrameId
|
||||
* @property {Object.<string,treasurehunterx.Player>|null} [players] RoomDownsyncFrame players
|
||||
* @property {number|Long|null} [sentAt] RoomDownsyncFrame sentAt
|
||||
* @property {number|Long|null} [countdownNanos] RoomDownsyncFrame countdownNanos
|
||||
* @property {Object.<string,treasurehunterx.PlayerMeta>|null} [playerMetas] RoomDownsyncFrame playerMetas
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a new RoomDownsyncFrame.
|
||||
* @memberof treasurehunterx
|
||||
* @classdesc Represents a RoomDownsyncFrame.
|
||||
* @implements IRoomDownsyncFrame
|
||||
* @constructor
|
||||
* @param {treasurehunterx.IRoomDownsyncFrame=} [properties] Properties to set
|
||||
*/
|
||||
function RoomDownsyncFrame(properties) {
|
||||
this.players = {};
|
||||
this.playerMetas = {};
|
||||
if (properties)
|
||||
for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i)
|
||||
if (properties[keys[i]] != null)
|
||||
this[keys[i]] = properties[keys[i]];
|
||||
}
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame id.
|
||||
* @member {number} id
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.id = 0;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame refFrameId.
|
||||
* @member {number} refFrameId
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.refFrameId = 0;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame players.
|
||||
* @member {Object.<string,treasurehunterx.Player>} players
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.players = $util.emptyObject;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame sentAt.
|
||||
* @member {number|Long} sentAt
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.sentAt = $util.Long ? $util.Long.fromBits(0,0,false) : 0;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame countdownNanos.
|
||||
* @member {number|Long} countdownNanos
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.countdownNanos = $util.Long ? $util.Long.fromBits(0,0,false) : 0;
|
||||
|
||||
/**
|
||||
* RoomDownsyncFrame playerMetas.
|
||||
* @member {Object.<string,treasurehunterx.PlayerMeta>} playerMetas
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.playerMetas = $util.emptyObject;
|
||||
|
||||
/**
|
||||
* Creates a new RoomDownsyncFrame instance using the specified properties.
|
||||
* @function create
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {treasurehunterx.IRoomDownsyncFrame=} [properties] Properties to set
|
||||
* @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame instance
|
||||
*/
|
||||
RoomDownsyncFrame.create = function create(properties) {
|
||||
return new RoomDownsyncFrame(properties);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes the specified RoomDownsyncFrame message. Does not implicitly {@link treasurehunterx.RoomDownsyncFrame.verify|verify} messages.
|
||||
* @function encode
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame message or plain object to encode
|
||||
* @param {$protobuf.Writer} [writer] Writer to encode to
|
||||
* @returns {$protobuf.Writer} Writer
|
||||
*/
|
||||
RoomDownsyncFrame.encode = function encode(message, writer) {
|
||||
if (!writer)
|
||||
writer = $Writer.create();
|
||||
if (message.id != null && Object.hasOwnProperty.call(message, "id"))
|
||||
writer.uint32(/* id 1, wireType 0 =*/8).int32(message.id);
|
||||
if (message.refFrameId != null && Object.hasOwnProperty.call(message, "refFrameId"))
|
||||
writer.uint32(/* id 2, wireType 0 =*/16).int32(message.refFrameId);
|
||||
if (message.players != null && Object.hasOwnProperty.call(message, "players"))
|
||||
for (var keys = Object.keys(message.players), i = 0; i < keys.length; ++i) {
|
||||
writer.uint32(/* id 3, wireType 2 =*/26).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
|
||||
$root.treasurehunterx.Player.encode(message.players[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
|
||||
}
|
||||
if (message.sentAt != null && Object.hasOwnProperty.call(message, "sentAt"))
|
||||
writer.uint32(/* id 4, wireType 0 =*/32).int64(message.sentAt);
|
||||
if (message.countdownNanos != null && Object.hasOwnProperty.call(message, "countdownNanos"))
|
||||
writer.uint32(/* id 5, wireType 0 =*/40).int64(message.countdownNanos);
|
||||
if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas"))
|
||||
for (var keys = Object.keys(message.playerMetas), i = 0; i < keys.length; ++i) {
|
||||
writer.uint32(/* id 6, wireType 2 =*/50).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
|
||||
$root.treasurehunterx.PlayerMeta.encode(message.playerMetas[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
|
||||
}
|
||||
return writer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes the specified RoomDownsyncFrame message, length delimited. Does not implicitly {@link treasurehunterx.RoomDownsyncFrame.verify|verify} messages.
|
||||
* @function encodeDelimited
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame message or plain object to encode
|
||||
* @param {$protobuf.Writer} [writer] Writer to encode to
|
||||
* @returns {$protobuf.Writer} Writer
|
||||
*/
|
||||
RoomDownsyncFrame.encodeDelimited = function encodeDelimited(message, writer) {
|
||||
return this.encode(message, writer).ldelim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a RoomDownsyncFrame message from the specified reader or buffer.
|
||||
* @function decode
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
|
||||
* @param {number} [length] Message length if known beforehand
|
||||
* @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
RoomDownsyncFrame.decode = function decode(reader, length) {
|
||||
if (!(reader instanceof $Reader))
|
||||
reader = $Reader.create(reader);
|
||||
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.treasurehunterx.RoomDownsyncFrame(), key, value;
|
||||
while (reader.pos < end) {
|
||||
var tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
message.id = reader.int32();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
message.refFrameId = reader.int32();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (message.players === $util.emptyObject)
|
||||
message.players = {};
|
||||
var end2 = reader.uint32() + reader.pos;
|
||||
key = 0;
|
||||
value = null;
|
||||
while (reader.pos < end2) {
|
||||
var tag2 = reader.uint32();
|
||||
switch (tag2 >>> 3) {
|
||||
case 1:
|
||||
key = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
value = $root.treasurehunterx.Player.decode(reader, reader.uint32());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag2 & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
message.players[key] = value;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
message.sentAt = reader.int64();
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
message.countdownNanos = reader.int64();
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (message.playerMetas === $util.emptyObject)
|
||||
message.playerMetas = {};
|
||||
var end2 = reader.uint32() + reader.pos;
|
||||
key = 0;
|
||||
value = null;
|
||||
while (reader.pos < end2) {
|
||||
var tag2 = reader.uint32();
|
||||
switch (tag2 >>> 3) {
|
||||
case 1:
|
||||
key = reader.int32();
|
||||
break;
|
||||
case 2:
|
||||
value = $root.treasurehunterx.PlayerMeta.decode(reader, reader.uint32());
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag2 & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
message.playerMetas[key] = value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a RoomDownsyncFrame message from the specified reader or buffer, length delimited.
|
||||
* @function decodeDelimited
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
|
||||
* @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
RoomDownsyncFrame.decodeDelimited = function decodeDelimited(reader) {
|
||||
if (!(reader instanceof $Reader))
|
||||
reader = new $Reader(reader);
|
||||
return this.decode(reader, reader.uint32());
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies a RoomDownsyncFrame message.
|
||||
* @function verify
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {Object.<string,*>} message Plain object to verify
|
||||
* @returns {string|null} `null` if valid, otherwise the reason why it is not
|
||||
*/
|
||||
RoomDownsyncFrame.verify = function verify(message) {
|
||||
if (typeof message !== "object" || message === null)
|
||||
return "object expected";
|
||||
if (message.id != null && message.hasOwnProperty("id"))
|
||||
if (!$util.isInteger(message.id))
|
||||
return "id: integer expected";
|
||||
if (message.refFrameId != null && message.hasOwnProperty("refFrameId"))
|
||||
if (!$util.isInteger(message.refFrameId))
|
||||
return "refFrameId: integer expected";
|
||||
if (message.players != null && message.hasOwnProperty("players")) {
|
||||
if (!$util.isObject(message.players))
|
||||
return "players: object expected";
|
||||
var key = Object.keys(message.players);
|
||||
for (var i = 0; i < key.length; ++i) {
|
||||
if (!$util.key32Re.test(key[i]))
|
||||
return "players: integer key{k:int32} expected";
|
||||
{
|
||||
var error = $root.treasurehunterx.Player.verify(message.players[key[i]]);
|
||||
if (error)
|
||||
return "players." + error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.sentAt != null && message.hasOwnProperty("sentAt"))
|
||||
if (!$util.isInteger(message.sentAt) && !(message.sentAt && $util.isInteger(message.sentAt.low) && $util.isInteger(message.sentAt.high)))
|
||||
return "sentAt: integer|Long expected";
|
||||
if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos"))
|
||||
if (!$util.isInteger(message.countdownNanos) && !(message.countdownNanos && $util.isInteger(message.countdownNanos.low) && $util.isInteger(message.countdownNanos.high)))
|
||||
return "countdownNanos: integer|Long expected";
|
||||
if (message.playerMetas != null && message.hasOwnProperty("playerMetas")) {
|
||||
if (!$util.isObject(message.playerMetas))
|
||||
return "playerMetas: object expected";
|
||||
var key = Object.keys(message.playerMetas);
|
||||
for (var i = 0; i < key.length; ++i) {
|
||||
if (!$util.key32Re.test(key[i]))
|
||||
return "playerMetas: integer key{k:int32} expected";
|
||||
{
|
||||
var error = $root.treasurehunterx.PlayerMeta.verify(message.playerMetas[key[i]]);
|
||||
if (error)
|
||||
return "playerMetas." + error;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a RoomDownsyncFrame message from a plain object. Also converts values to their respective internal types.
|
||||
* @function fromObject
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {Object.<string,*>} object Plain object
|
||||
* @returns {treasurehunterx.RoomDownsyncFrame} RoomDownsyncFrame
|
||||
*/
|
||||
RoomDownsyncFrame.fromObject = function fromObject(object) {
|
||||
if (object instanceof $root.treasurehunterx.RoomDownsyncFrame)
|
||||
return object;
|
||||
var message = new $root.treasurehunterx.RoomDownsyncFrame();
|
||||
if (object.id != null)
|
||||
message.id = object.id | 0;
|
||||
if (object.refFrameId != null)
|
||||
message.refFrameId = object.refFrameId | 0;
|
||||
if (object.players) {
|
||||
if (typeof object.players !== "object")
|
||||
throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected");
|
||||
message.players = {};
|
||||
for (var keys = Object.keys(object.players), i = 0; i < keys.length; ++i) {
|
||||
if (typeof object.players[keys[i]] !== "object")
|
||||
throw TypeError(".treasurehunterx.RoomDownsyncFrame.players: object expected");
|
||||
message.players[keys[i]] = $root.treasurehunterx.Player.fromObject(object.players[keys[i]]);
|
||||
}
|
||||
}
|
||||
if (object.sentAt != null)
|
||||
if ($util.Long)
|
||||
(message.sentAt = $util.Long.fromValue(object.sentAt)).unsigned = false;
|
||||
else if (typeof object.sentAt === "string")
|
||||
message.sentAt = parseInt(object.sentAt, 10);
|
||||
else if (typeof object.sentAt === "number")
|
||||
message.sentAt = object.sentAt;
|
||||
else if (typeof object.sentAt === "object")
|
||||
message.sentAt = new $util.LongBits(object.sentAt.low >>> 0, object.sentAt.high >>> 0).toNumber();
|
||||
if (object.countdownNanos != null)
|
||||
if ($util.Long)
|
||||
(message.countdownNanos = $util.Long.fromValue(object.countdownNanos)).unsigned = false;
|
||||
else if (typeof object.countdownNanos === "string")
|
||||
message.countdownNanos = parseInt(object.countdownNanos, 10);
|
||||
else if (typeof object.countdownNanos === "number")
|
||||
message.countdownNanos = object.countdownNanos;
|
||||
else if (typeof object.countdownNanos === "object")
|
||||
message.countdownNanos = new $util.LongBits(object.countdownNanos.low >>> 0, object.countdownNanos.high >>> 0).toNumber();
|
||||
if (object.playerMetas) {
|
||||
if (typeof object.playerMetas !== "object")
|
||||
throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected");
|
||||
message.playerMetas = {};
|
||||
for (var keys = Object.keys(object.playerMetas), i = 0; i < keys.length; ++i) {
|
||||
if (typeof object.playerMetas[keys[i]] !== "object")
|
||||
throw TypeError(".treasurehunterx.RoomDownsyncFrame.playerMetas: object expected");
|
||||
message.playerMetas[keys[i]] = $root.treasurehunterx.PlayerMeta.fromObject(object.playerMetas[keys[i]]);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a plain object from a RoomDownsyncFrame message. Also converts values to other types if specified.
|
||||
* @function toObject
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {treasurehunterx.RoomDownsyncFrame} message RoomDownsyncFrame
|
||||
* @param {$protobuf.IConversionOptions} [options] Conversion options
|
||||
* @returns {Object.<string,*>} Plain object
|
||||
*/
|
||||
RoomDownsyncFrame.toObject = function toObject(message, options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
var object = {};
|
||||
if (options.objects || options.defaults) {
|
||||
object.players = {};
|
||||
object.playerMetas = {};
|
||||
}
|
||||
if (options.defaults) {
|
||||
object.id = 0;
|
||||
object.refFrameId = 0;
|
||||
if ($util.Long) {
|
||||
var long = new $util.Long(0, 0, false);
|
||||
object.sentAt = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long;
|
||||
} else
|
||||
object.sentAt = options.longs === String ? "0" : 0;
|
||||
if ($util.Long) {
|
||||
var long = new $util.Long(0, 0, false);
|
||||
object.countdownNanos = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long;
|
||||
} else
|
||||
object.countdownNanos = options.longs === String ? "0" : 0;
|
||||
}
|
||||
if (message.id != null && message.hasOwnProperty("id"))
|
||||
object.id = message.id;
|
||||
if (message.refFrameId != null && message.hasOwnProperty("refFrameId"))
|
||||
object.refFrameId = message.refFrameId;
|
||||
var keys2;
|
||||
if (message.players && (keys2 = Object.keys(message.players)).length) {
|
||||
object.players = {};
|
||||
for (var j = 0; j < keys2.length; ++j)
|
||||
object.players[keys2[j]] = $root.treasurehunterx.Player.toObject(message.players[keys2[j]], options);
|
||||
}
|
||||
if (message.sentAt != null && message.hasOwnProperty("sentAt"))
|
||||
if (typeof message.sentAt === "number")
|
||||
object.sentAt = options.longs === String ? String(message.sentAt) : message.sentAt;
|
||||
else
|
||||
object.sentAt = options.longs === String ? $util.Long.prototype.toString.call(message.sentAt) : options.longs === Number ? new $util.LongBits(message.sentAt.low >>> 0, message.sentAt.high >>> 0).toNumber() : message.sentAt;
|
||||
if (message.countdownNanos != null && message.hasOwnProperty("countdownNanos"))
|
||||
if (typeof message.countdownNanos === "number")
|
||||
object.countdownNanos = options.longs === String ? String(message.countdownNanos) : message.countdownNanos;
|
||||
else
|
||||
object.countdownNanos = options.longs === String ? $util.Long.prototype.toString.call(message.countdownNanos) : options.longs === Number ? new $util.LongBits(message.countdownNanos.low >>> 0, message.countdownNanos.high >>> 0).toNumber() : message.countdownNanos;
|
||||
if (message.playerMetas && (keys2 = Object.keys(message.playerMetas)).length) {
|
||||
object.playerMetas = {};
|
||||
for (var j = 0; j < keys2.length; ++j)
|
||||
object.playerMetas[keys2[j]] = $root.treasurehunterx.PlayerMeta.toObject(message.playerMetas[keys2[j]], options);
|
||||
}
|
||||
return object;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts this RoomDownsyncFrame to JSON.
|
||||
* @function toJSON
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @instance
|
||||
* @returns {Object.<string,*>} JSON object
|
||||
*/
|
||||
RoomDownsyncFrame.prototype.toJSON = function toJSON() {
|
||||
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the default type url for RoomDownsyncFrame
|
||||
* @function getTypeUrl
|
||||
* @memberof treasurehunterx.RoomDownsyncFrame
|
||||
* @static
|
||||
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
|
||||
* @returns {string} The default type url
|
||||
*/
|
||||
RoomDownsyncFrame.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
|
||||
if (typeUrlPrefix === undefined) {
|
||||
typeUrlPrefix = "type.googleapis.com";
|
||||
}
|
||||
return typeUrlPrefix + "/treasurehunterx.RoomDownsyncFrame";
|
||||
};
|
||||
|
||||
return RoomDownsyncFrame;
|
||||
})();
|
||||
|
||||
treasurehunterx.WsReq = (function() {
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user