Fixed some trivial runtime errors.

This commit is contained in:
genxium 2022-10-01 17:26:37 +08:00
parent a2a8be9068
commit cd83539197
5 changed files with 31 additions and 54 deletions

View File

@ -348,7 +348,7 @@ func (pR *Room) EncodeUpsyncCmd(upsyncCmd *pb.InputFrameUpsync) uint64 {
func (pR *Room) AllPlayerInputsBufferString() string { func (pR *Room) AllPlayerInputsBufferString() string {
s := make([]string, 0) s := make([]string, 0)
s = append(s, fmt.Sprintf("{lastAllConfirmedInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v}", pR.LastAllConfirmedInputFrameId, pR.LastAllConfirmedInputFrameIdWithChange)) s = append(s, fmt.Sprintf("\n{stInputFrameId: %v, edInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v, lastAllConfirmedInputFrameId: %v}", pR.AllPlayerInputsBuffer.StFrameId, pR.AllPlayerInputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId))
for playerId, player := range pR.Players { for playerId, player := range pR.Players {
s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId)) s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId))
} }
@ -456,7 +456,7 @@ func (pR *Room) StartBattle() {
toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt) toSendInputFrames := make([]*pb.InputFrameDownsync, 0, pR.AllPlayerInputsBuffer.Cnt)
candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1 candidateToSendInputFrameId := atomic.LoadInt32(&(pR.Players[playerId].LastSentInputFrameId)) + 1
if candidateToSendInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { if candidateToSendInputFrameId < pR.AllPlayerInputsBuffer.StFrameId {
Logger.Warn("LastSentInputFrameId already popped:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("lastSentInputFrameId", candidateToSendInputFrameId-1), zap.Any("playerAckingInputFrameId", player.AckingInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) Logger.Warn(fmt.Sprintf("LastSentInputFrameId already popped: roomId=%v, playerId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, candidateToSendInputFrameId-1, player.AckingInputFrameId, pR.AllPlayerInputsBufferString()))
candidateToSendInputFrameId = pR.AllPlayerInputsBuffer.StFrameId candidateToSendInputFrameId = pR.AllPlayerInputsBuffer.StFrameId
} }
@ -534,7 +534,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) {
ackingInputFrameId := pReq.AckingInputFrameId ackingInputFrameId := pReq.AckingInputFrameId
if _, existent := pR.Players[playerId]; !existent { if _, existent := pR.Players[playerId]; !existent {
Logger.Warn("upcmd player doesn't exist:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId)) Logger.Warn(fmt.Sprintf("upcmd player doesn't exist: roomId=%v, playerId=%v", pR.Id, playerId))
return return
} }
@ -549,7 +549,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) {
for _, inputFrameUpsync := range inputFrameUpsyncBatch { for _, inputFrameUpsync := range inputFrameUpsyncBatch {
clientInputFrameId := inputFrameUpsync.InputFrameId clientInputFrameId := inputFrameUpsync.InputFrameId
if clientInputFrameId < pR.AllPlayerInputsBuffer.StFrameId { if clientInputFrameId < pR.AllPlayerInputsBuffer.StFrameId {
Logger.Warn("Obsolete inputFrameUpsync:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) Logger.Warn(fmt.Sprintf("Obsolete inputFrameUpsync: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString()))
return return
} }
@ -557,36 +557,36 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) {
encodedInput := pR.EncodeUpsyncCmd(inputFrameUpsync) encodedInput := pR.EncodeUpsyncCmd(inputFrameUpsync)
if clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId { if clientInputFrameId >= pR.AllPlayerInputsBuffer.EdFrameId {
Logger.Warn("inputFrame too advanced! is the player cheating?", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) Logger.Warn(fmt.Sprintf("inputFrame too advanced! is the player cheating?: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString()))
return return
} }
tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId) tmp2 := pR.AllPlayerInputsBuffer.GetByFrameId(clientInputFrameId)
if nil == tmp2 { if nil == tmp2 {
// This shouldn't happen due to the previous 2 checks // This shouldn't happen due to the previous 2 checks
Logger.Warn("Mysterious error getting an input frame:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) Logger.Warn(fmt.Sprintf("Mysterious error getting an input frame: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString()))
return return
} }
inputFrameDownsync := tmp2.(*pb.InputFrameDownsync) inputFrameDownsync := tmp2.(*pb.InputFrameDownsync)
oldConfirmedList := atomic.LoadUint64(&(inputFrameDownsync.ConfirmedList)) oldConfirmedList := atomic.LoadUint64(&(inputFrameDownsync.ConfirmedList))
if (oldConfirmedList & joinMask) > 0 { if (oldConfirmedList & joinMask) > 0 {
Logger.Warn("Cmd already confirmed but getting set attempt, omitting this upsync cmd:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) Logger.Warn(fmt.Sprintf("Cmd already confirmed but getting set attempt, omitting this upsync cmd: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.AllPlayerInputsBufferString()))
return 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. // 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 swapped := atomic.CompareAndSwapUint64(&inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr], uint64(0), encodedInput); !swapped {
Logger.Warn("Failed input CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId)) Logger.Warn(fmt.Sprintf("Failed input CAS: roomId=%v, playerId=%v, clientInputFrameId=%v", pR.Id, playerId, clientInputFrameId))
return return
} }
newConfirmedList := (oldConfirmedList | joinMask) newConfirmedList := (oldConfirmedList | joinMask)
if swapped := atomic.CompareAndSwapUint64(&(inputFrameDownsync.ConfirmedList), oldConfirmedList, newConfirmedList); !swapped { 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". // [WARNING] Upon this error, the actual input has already been updated, which is an expected result if it caused by the force confirmation from "battleMainLoop".
Logger.Warn("Failed confirm CAS:", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("clientInputFrameId", clientInputFrameId)) Logger.Warn(fmt.Sprintf("Failed confirm CAS: roomId=%v, playerId=%v, clientInputFrameId=%v", pR.Id, playerId, clientInputFrameId))
return return
} }
totPlayerCnt := uint32(len(pR.Players)) totPlayerCnt := uint32(pR.Capacity)
allConfirmedMask := uint64((1 << totPlayerCnt) - 1) allConfirmedMask := uint64((1 << totPlayerCnt) - 1)
if allConfirmedMask == newConfirmedList { if allConfirmedMask == newConfirmedList {
pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, playerId) pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, playerId)
@ -598,7 +598,7 @@ func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFra
clientInputFrameId := inputFrameDownsync.InputFrameId clientInputFrameId := inputFrameDownsync.InputFrameId
if false == pR.equalInputLists(inputFrameDownsync.InputList, pR.LastAllConfirmedInputList) { 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! atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), clientInputFrameId) // [WARNING] Different from the CAS in "battleMainLoop", it's safe to just update "pR.LastAllConfirmedInputFrameIdWithChange" here, because only monotonic increment is possible here!
Logger.Info("Key inputFrame change", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", clientInputFrameId), zap.Any("lastInputFrameId", pR.LastAllConfirmedInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString()), zap.Any("newInputList", inputFrameDownsync.InputList), zap.Any("lastInputList", pR.LastAllConfirmedInputList)) Logger.Info(fmt.Sprintf("Key inputFrame change: roomId=%v, playerId=%v, clientInputFrameId=%v, lastInputFrameId=%v, newInputList=%v, lastInputList=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.LastAllConfirmedInputFrameId, inputFrameDownsync.InputList, pR.LastAllConfirmedInputList, pR.AllPlayerInputsBufferString()))
} }
atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! 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 { for i, v := range inputFrameDownsync.InputList {
@ -606,7 +606,7 @@ func (pR *Room) onInputFrameDownsyncAllConfirmed(inputFrameDownsync *pb.InputFra
pR.LastAllConfirmedInputList[i] = v pR.LastAllConfirmedInputList[i] = v
} }
if pR.inputFrameIdDebuggable(clientInputFrameId) { if pR.inputFrameIdDebuggable(clientInputFrameId) {
Logger.Info("inputFrame lifecycle#2[allconfirmed]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("AllPlayerInputsBuffer", pR.AllPlayerInputsBufferString())) Logger.Info(fmt.Sprintf("inputFrame lifecycle#2[allconfirmed]: roomId=%v, playerId=%v, clientInputFrameId=%v, AllPlayerInputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.LastAllConfirmedInputFrameId, pR.AllPlayerInputsBufferString()))
} }
} }
@ -1074,7 +1074,7 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
if nil == tmp { 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())) panic(fmt.Sprintf("delayedInputFrameId=%v doesn't exist for roomId=%v, this is abnormal because it's to be used for applying dynamics to [fromRenderFrameId:%v, toRenderFrameId:%v) @ collisionSysRenderFrameId=%v! AllPlayerInputsBuffer=%v", delayedInputFrameId, pR.Id, fromRenderFrameId, toRenderFrameId, collisionSysRenderFrameId, pR.AllPlayerInputsBufferString()))
} }
delayedInputFrame := tmp.(pb.InputFrameDownsync) delayedInputFrame := tmp.(*pb.InputFrameDownsync)
if swapped := atomic.CompareAndSwapUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask, allConfirmedMask); !swapped { 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())) 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()))
} }

View File

@ -6,8 +6,6 @@ import (
"encoding/base64" "encoding/base64"
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt"
"github.com/ByteArena/box2d"
"go.uber.org/zap" "go.uber.org/zap"
"io/ioutil" "io/ioutil"
"math" "math"
@ -181,17 +179,12 @@ type Polygon2DList []*Polygon2D
type StrToVec2DListMap map[string]*Vec2DList // Note that it's deliberately NOT using "map[string]Vec2DList", for the easy of passing return value to "models/room.go". type StrToVec2DListMap map[string]*Vec2DList // Note that it's deliberately NOT using "map[string]Vec2DList", for the easy of passing return value to "models/room.go".
type StrToPolygon2DListMap map[string]*Polygon2DList // Note that it's deliberately NOT using "map[string]Polygon2DList", for the easy of passing return value to "models/room.go". type StrToPolygon2DListMap map[string]*Polygon2DList // Note that it's deliberately NOT using "map[string]Polygon2DList", for the easy of passing return value to "models/room.go".
func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) { func tmxPolylineToPolygon2D(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) {
if nil == targetPolyline { if nil == targetPolyline {
return nil, nil return nil, nil
} }
singleValueArray := strings.Split(targetPolyline.Points, " ") singleValueArray := strings.Split(targetPolyline.Points, " ")
pointsCount := len(singleValueArray)
if pointsCount >= box2d.B2_maxPolygonVertices {
return nil, errors.New(fmt.Sprintf("During `TmxPolylineToPolygon2DInB2World`, you have a polygon with pointsCount == %v, exceeding or equal to box2d.B2_maxPolygonVertices == %v, of polyines [%v]", pointsCount, box2d.B2_maxPolygonVertices, singleValueArray))
}
theUntransformedAnchor := &Vec2D{ theUntransformedAnchor := &Vec2D{
X: singleObjInTmxFile.X, X: singleObjInTmxFile.X,
@ -217,7 +210,6 @@ func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *Tmx
} }
} }
// Transform to B2World space coordinate.
tmp := &Vec2D{ tmp := &Vec2D{
X: thePolygon2DFromPolyline.Points[k].X, X: thePolygon2DFromPolyline.Points[k].X,
Y: thePolygon2DFromPolyline.Points[k].Y, Y: thePolygon2DFromPolyline.Points[k].Y,
@ -230,7 +222,7 @@ func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *Tmx
return thePolygon2DFromPolyline, nil return thePolygon2DFromPolyline, nil
} }
func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInTsxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline, pTsxIns *Tsx) (*Polygon2D, error) { func tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns *TmxMap, singleObjInTsxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline, pTsxIns *Tsx) (*Polygon2D, error) {
if nil == targetPolyline { if nil == targetPolyline {
return nil, nil return nil, nil
} }
@ -241,10 +233,6 @@ func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInT
singleValueArray := strings.Split(targetPolyline.Points, " ") singleValueArray := strings.Split(targetPolyline.Points, " ")
pointsCount := len(singleValueArray) pointsCount := len(singleValueArray)
if pointsCount >= box2d.B2_maxPolygonVertices {
return nil, errors.New(fmt.Sprintf("During `TsxPolylineToOffsetsWrtTileCenterInB2World`, you have a polygon with pointsCount == %v, exceeding or equal to box2d.B2_maxPolygonVertices == %v", pointsCount, box2d.B2_maxPolygonVertices))
}
thePolygon2DFromPolyline := &Polygon2D{ thePolygon2DFromPolyline := &Polygon2D{
Anchor: nil, Anchor: nil,
Points: make([]*Vec2D, pointsCount), Points: make([]*Vec2D, pointsCount),
@ -253,7 +241,7 @@ func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInT
} }
/* /*
[WARNING] In this case, the "Treasure"s and "GuardTower"s are put into Tmx file as "ImageObject"s, of each the "ProportionalAnchor" is (0.5, 0). Therefore we calculate that "thePolygon2DFromPolyline.Points" are "offsets(in B2World) w.r.t. the BottomCenter". See https://shimo.im/docs/SmLJJhXm2C8XMzZT for details. [WARNING] In this case, the "Treasure"s and "GuardTower"s are put into Tmx file as "ImageObject"s, of each the "ProportionalAnchor" is (0.5, 0). Therefore the "thePolygon2DFromPolyline.Points" are "offsets w.r.t. the BottomCenter". See https://shimo.im/docs/SmLJJhXm2C8XMzZT for details.
*/ */
for k, value := range singleValueArray { for k, value := range singleValueArray {
@ -271,14 +259,12 @@ func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInT
thePolygon2DFromPolyline.Points[k].Y = float64(pTsxIns.TileHeight) - (coordinateValue + offsetFromTopLeftInTileLocalCoordY) thePolygon2DFromPolyline.Points[k].Y = float64(pTsxIns.TileHeight) - (coordinateValue + offsetFromTopLeftInTileLocalCoordY)
} }
} }
// No need to transform for B2World space coordinate because the marks in a Tsx file is already rectilinear.
} }
return thePolygon2DFromPolyline, nil return thePolygon2DFromPolyline, nil
} }
func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, firstGid int, gidBoundariesMapInB2World map[int]StrToPolygon2DListMap) error { func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, firstGid int, gidBoundariesMap map[int]StrToPolygon2DListMap) error {
pTsxIns := &Tsx{} pTsxIns := &Tsx{}
err := xml.Unmarshal(byteArrOfTsxFile, pTsxIns) err := xml.Unmarshal(byteArrOfTsxFile, pTsxIns)
if nil != err { if nil != err {
@ -312,7 +298,7 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
``` ```
, we currently REQUIRE that "`an object of a tile` with ONE OR MORE polylines must come with a single corresponding '<property name=`type` value=`...` />', and viceversa". , we currently REQUIRE that "`an object of a tile` with ONE OR MORE polylines must come with a single corresponding '<property name=`type` value=`...` />', and viceversa".
Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for how we theoretically fit a "Polyline in Tsx" into a "Polygon2D" and then into the corresponding "B2BodyDef & B2Body in the `world of colliding bodies`". Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for how we theoretically fit a "Polyline in Tsx" into a "Polygon2D".
*/ */
theObjGroup := tile.ObjectGroup theObjGroup := tile.ObjectGroup
@ -331,11 +317,11 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
key := singleObj.Properties.Property[0].Value key := singleObj.Properties.Property[0].Value
var theStrToPolygon2DListMap StrToPolygon2DListMap var theStrToPolygon2DListMap StrToPolygon2DListMap
if existingStrToPolygon2DListMap, ok := gidBoundariesMapInB2World[globalGid]; ok { if existingStrToPolygon2DListMap, ok := gidBoundariesMap[globalGid]; ok {
theStrToPolygon2DListMap = existingStrToPolygon2DListMap theStrToPolygon2DListMap = existingStrToPolygon2DListMap
} else { } else {
gidBoundariesMapInB2World[globalGid] = make(StrToPolygon2DListMap, 0) gidBoundariesMap[globalGid] = make(StrToPolygon2DListMap, 0)
theStrToPolygon2DListMap = gidBoundariesMapInB2World[globalGid] theStrToPolygon2DListMap = gidBoundariesMap[globalGid]
} }
var pThePolygon2DList *Polygon2DList var pThePolygon2DList *Polygon2DList
@ -347,7 +333,7 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
pThePolygon2DList = theStrToPolygon2DListMap[key] pThePolygon2DList = theStrToPolygon2DListMap[key]
} }
thePolygon2DFromPolyline, err := TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns) thePolygon2DFromPolyline, err := tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns)
if nil != err { if nil != err {
panic(err) panic(err)
} }
@ -357,16 +343,9 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
return nil return nil
} }
func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[int]StrToPolygon2DListMap) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) { func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToPolygon2DListMap) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) {
toRetStrToVec2DListMap := make(StrToVec2DListMap, 0) toRetStrToVec2DListMap := make(StrToVec2DListMap, 0)
toRetStrToPolygon2DListMap := make(StrToPolygon2DListMap, 0) toRetStrToPolygon2DListMap := make(StrToPolygon2DListMap, 0)
/*
Note that both
- "Vec2D"s of "toRetStrToVec2DListMap", and
- "Polygon2D"s of "toRetStrToPolygon2DListMap"
are already transformed into the "coordinate of B2World".
*/
for _, objGroup := range pTmxMapIns.ObjectGroups { for _, objGroup := range pTmxMapIns.ObjectGroups {
switch objGroup.Name { switch objGroup.Name {
@ -376,10 +355,8 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i
if false == ok { if false == ok {
theVec2DListToCache := make(Vec2DList, 0) theVec2DListToCache := make(Vec2DList, 0)
toRetStrToVec2DListMap[objGroup.Name] = &theVec2DListToCache toRetStrToVec2DListMap[objGroup.Name] = &theVec2DListToCache
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name] }
} else { pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
}
for _, singleObjInTmxFile := range objGroup.Objects { for _, singleObjInTmxFile := range objGroup.Objects {
theUntransformedPos := &Vec2D{ theUntransformedPos := &Vec2D{
X: singleObjInTmxFile.X, X: singleObjInTmxFile.X,
@ -389,16 +366,14 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i
*pTheVec2DListToCache = append(*pTheVec2DListToCache, &thePosInWorld) *pTheVec2DListToCache = append(*pTheVec2DListToCache, &thePosInWorld)
} }
case "Barrier": case "Barrier":
// Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]" w.r.t. B2World. // Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]".
var pThePolygon2DListToCache *Polygon2DList var pThePolygon2DListToCache *Polygon2DList
_, ok := toRetStrToPolygon2DListMap[objGroup.Name] _, ok := toRetStrToPolygon2DListMap[objGroup.Name]
if false == ok { if false == ok {
thePolygon2DListToCache := make(Polygon2DList, 0) thePolygon2DListToCache := make(Polygon2DList, 0)
toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
} else {
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
} }
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
for _, singleObjInTmxFile := range objGroup.Objects { for _, singleObjInTmxFile := range objGroup.Objects {
if nil == singleObjInTmxFile.Polyline { if nil == singleObjInTmxFile.Polyline {
@ -408,7 +383,7 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[i
continue continue
} }
thePolygon2DInWorld, err := TmxPolylineToPolygon2DInB2World(pTmxMapIns, singleObjInTmxFile, singleObjInTmxFile.Polyline) thePolygon2DInWorld, err := tmxPolylineToPolygon2D(pTmxMapIns, singleObjInTmxFile, singleObjInTmxFile.Polyline)
if nil != err { if nil != err {
panic(err) panic(err)
} }

View File

@ -440,7 +440,7 @@
"array": [ "array": [
0, 0,
0, 0,
216.05530045313827, 209.73151519075364,
0, 0,
0, 0,
0, 0,

View File

@ -1047,6 +1047,7 @@ cc.Class({
const inputList = self.getCachedInputFrameDownsyncWithPrediction(j).inputList; const inputList = self.getCachedInputFrameDownsyncWithPrediction(j).inputList;
for (let j in self.playerRichInfoArr) { for (let j in self.playerRichInfoArr) {
const joinIndex = parseInt(j) + 1; const joinIndex = parseInt(j) + 1;
const playerId = self.playerRichInfoArr[j].id;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex); const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const player = renderFrame.players[playerId]; const player = renderFrame.players[playerId];

View File

@ -166,6 +166,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
break; break;
case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC: case window.DOWNSYNC_MSG_ACT_FORCED_RESYNC:
if (window.handleInputFrameDownsyncBatch && window.handleRoomDownsyncFrame) { if (window.handleInputFrameDownsyncBatch && window.handleRoomDownsyncFrame) {
console.warn("GOT forced resync:", resp);
// The following order of execution is important, because "handleInputFrameDownsyncBatch" is only available when state is IN_BATTLE // The following order of execution is important, because "handleInputFrameDownsyncBatch" is only available when state is IN_BATTLE
window.handleRoomDownsyncFrame(resp.rdf); window.handleRoomDownsyncFrame(resp.rdf);
window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); window.handleInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch);