mirror of
https://github.com/genxium/DelayNoMore
synced 2025-10-09 00:26:39 +00:00
Refactoring backend for periodical force confirmation.
This commit is contained in:
@@ -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:
|
||||
|
Reference in New Issue
Block a user