mirror of
https://github.com/genxium/DelayNoMore
synced 2025-10-18 13:06:29 +00:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4e0f7b52d4 | ||
|
486c46f608 | ||
|
6d075877ec | ||
|
fe826b393b | ||
|
c69aa25353 | ||
|
0f4d067c06 | ||
|
cff31d295c | ||
|
150e30db2a | ||
|
bc8989a0e6 | ||
|
1959a7fd9a | ||
|
3baaf1d52c | ||
|
62f10e0877 |
@@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/solarlune/resolv"
|
"github.com/solarlune/resolv"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"math"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
. "server/common"
|
. "server/common"
|
||||||
"server/common/utils"
|
"server/common/utils"
|
||||||
@@ -189,6 +188,7 @@ type Room struct {
|
|||||||
StageTileH int32
|
StageTileH int32
|
||||||
RawBattleStrToVec2DListMap StrToVec2DListMap
|
RawBattleStrToVec2DListMap StrToVec2DListMap
|
||||||
RawBattleStrToPolygon2DListMap StrToPolygon2DListMap
|
RawBattleStrToPolygon2DListMap StrToPolygon2DListMap
|
||||||
|
BackendDynamicsEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -329,7 +329,7 @@ func (pR *Room) ChooseStage() error {
|
|||||||
|
|
||||||
var barrierLocalIdInBattle int32 = 0
|
var barrierLocalIdInBattle int32 = 0
|
||||||
for _, polygon2DUnaligned := range barrierPolygon2DList {
|
for _, polygon2DUnaligned := range barrierPolygon2DList {
|
||||||
polygon2D := AlignPolygon2DToBoundingBox(polygon2DUnaligned)
|
polygon2D := AlignPolygon2DToBoundingBox(polygon2DUnaligned)
|
||||||
/*
|
/*
|
||||||
// For debug-printing only.
|
// For debug-printing only.
|
||||||
Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points))
|
Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points))
|
||||||
@@ -460,17 +460,12 @@ func (pR *Room) StartBattle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Force setting all-confirmed of buffered inputFrames periodically
|
// Force setting all-confirmed of buffered inputFrames periodically
|
||||||
unconfirmedMask := pR.forceConfirmationIfApplicable()
|
unconfirmedMask := uint64(0)
|
||||||
|
if pR.BackendDynamicsEnabled {
|
||||||
dynamicsDuration := int64(0)
|
unconfirmedMask = pR.forceConfirmationIfApplicable()
|
||||||
if 0 <= pR.LastAllConfirmedInputFrameId {
|
} else {
|
||||||
dynamicsStartedAt := utils.UnixtimeNano()
|
pR.markConfirmationIfApplicable()
|
||||||
// Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId"
|
}
|
||||||
nextDynamicsRenderFrameId := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames)
|
|
||||||
Logger.Debug(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, LastAllConfirmedInputFrameId=%v, InputDelayFrames=%v, nextDynamicsRenderFrameId=%v", pR.Id, pR.RenderFrameId, pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames, nextDynamicsRenderFrameId))
|
|
||||||
pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId)
|
|
||||||
dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
upperToSendInputFrameId := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameId))
|
upperToSendInputFrameId := atomic.LoadInt32(&(pR.LastAllConfirmedInputFrameId))
|
||||||
/*
|
/*
|
||||||
@@ -482,13 +477,26 @@ func (pR *Room) StartBattle() {
|
|||||||
Hence even upon resync, it's still possible that "refRenderFrameId < frontend.chaserRenderFrameId".
|
Hence even upon resync, it's still possible that "refRenderFrameId < frontend.chaserRenderFrameId".
|
||||||
*/
|
*/
|
||||||
refRenderFrameId := pR.ConvertToGeneratingRenderFrameId(upperToSendInputFrameId) + (1 << pR.InputScaleFrames) - 1
|
refRenderFrameId := pR.ConvertToGeneratingRenderFrameId(upperToSendInputFrameId) + (1 << pR.InputScaleFrames) - 1
|
||||||
// [WARNING] The following inequalities are seldom true, but just to avoid that in good network condition the frontend resyncs itself to a "too advanced frontend.renderFrameId", and then starts upsyncing "too advanced inputFrameId".
|
|
||||||
if refRenderFrameId > pR.RenderFrameId {
|
if refRenderFrameId > pR.RenderFrameId {
|
||||||
refRenderFrameId = pR.RenderFrameId
|
refRenderFrameId = pR.RenderFrameId
|
||||||
}
|
}
|
||||||
if refRenderFrameId > pR.CurDynamicsRenderFrameId {
|
|
||||||
refRenderFrameId = pR.CurDynamicsRenderFrameId
|
dynamicsDuration := int64(0)
|
||||||
}
|
if pR.BackendDynamicsEnabled {
|
||||||
|
if 0 <= pR.LastAllConfirmedInputFrameId {
|
||||||
|
dynamicsStartedAt := utils.UnixtimeNano()
|
||||||
|
// Apply "all-confirmed inputFrames" to move forward "pR.CurDynamicsRenderFrameId"
|
||||||
|
nextDynamicsRenderFrameId := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames)
|
||||||
|
Logger.Debug(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, LastAllConfirmedInputFrameId=%v, InputDelayFrames=%v, nextDynamicsRenderFrameId=%v", pR.Id, pR.RenderFrameId, pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames, nextDynamicsRenderFrameId))
|
||||||
|
pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId)
|
||||||
|
dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
// [WARNING] The following inequality are seldom true, but just to avoid that in good network condition the frontend resyncs itself to a "too advanced frontend.renderFrameId", and then starts upsyncing "too advanced inputFrameId".
|
||||||
|
if refRenderFrameId > pR.CurDynamicsRenderFrameId {
|
||||||
|
refRenderFrameId = pR.CurDynamicsRenderFrameId
|
||||||
|
}
|
||||||
|
}
|
||||||
for playerId, player := range pR.Players {
|
for playerId, player := range pR.Players {
|
||||||
if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped {
|
if swapped := atomic.CompareAndSwapInt32(&player.BattleState, PlayerBattleStateIns.ACTIVE, PlayerBattleStateIns.ACTIVE); !swapped {
|
||||||
// [WARNING] DON'T send anything if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player".
|
// [WARNING] DON'T send anything if the player is disconnected, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player".
|
||||||
@@ -538,7 +546,7 @@ func (pR *Room) StartBattle() {
|
|||||||
|
|
||||||
indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1)
|
indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1)
|
||||||
var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr)
|
var joinMask uint64 = (1 << indiceInJoinIndexBooleanArr)
|
||||||
if MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId || 0 < (unconfirmedMask&joinMask) {
|
if pR.BackendDynamicsEnabled && (MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId || 0 < (unconfirmedMask&joinMask)) {
|
||||||
// [WARNING] Even upon "MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED", it could be true that "0 == (unconfirmedMask & joinMask)"!
|
// [WARNING] Even upon "MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED", it could be true that "0 == (unconfirmedMask & joinMask)"!
|
||||||
tmp := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId)
|
tmp := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId)
|
||||||
if nil == tmp {
|
if nil == tmp {
|
||||||
@@ -802,6 +810,8 @@ func (pR *Room) OnDismissed() {
|
|||||||
pR.InputFrameUpsyncDelayTolerance = 2
|
pR.InputFrameUpsyncDelayTolerance = 2
|
||||||
pR.MaxChasingRenderFramesPerUpdate = 10
|
pR.MaxChasingRenderFramesPerUpdate = 10
|
||||||
|
|
||||||
|
pR.BackendDynamicsEnabled = true // [WARNING] When "false", recovery upon reconnection wouldn't work!
|
||||||
|
|
||||||
pR.ChooseStage()
|
pR.ChooseStage()
|
||||||
pR.EffectivePlayerCount = 0
|
pR.EffectivePlayerCount = 0
|
||||||
|
|
||||||
@@ -1069,6 +1079,43 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *pb.InputFrameDowns
|
|||||||
return currInputFrameDownsync
|
return currInputFrameDownsync
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pR *Room) markConfirmationIfApplicable() {
|
||||||
|
inputFrameId1 := pR.LastAllConfirmedInputFrameId+1
|
||||||
|
gap := int32(4) // This value is hardcoded and doesn't need be much bigger, because the backend side is supposed to never lag when "false == BackendDynamicsEnabled".
|
||||||
|
inputFrameId2 := inputFrameId1+gap
|
||||||
|
if inputFrameId2 > pR.InputsBuffer.EdFrameId {
|
||||||
|
inputFrameId2 = pR.InputsBuffer.EdFrameId
|
||||||
|
}
|
||||||
|
|
||||||
|
totPlayerCnt := uint32(pR.Capacity)
|
||||||
|
allConfirmedMask := uint64((1 << totPlayerCnt) - 1)
|
||||||
|
for inputFrameId := inputFrameId1; inputFrameId < inputFrameId2; inputFrameId++ {
|
||||||
|
tmp := pR.InputsBuffer.GetByFrameId(inputFrameId)
|
||||||
|
if nil == tmp {
|
||||||
|
panic(fmt.Sprintf("inputFrameId=%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! InputsBuffer=%v", inputFrameId, pR.Id, pR.InputsBufferString(false)))
|
||||||
|
}
|
||||||
|
inputFrameDownsync := tmp.(*pb.InputFrameDownsync)
|
||||||
|
for _, player := range pR.Players {
|
||||||
|
bufIndex := pR.toDiscreteInputsBufferIndex(inputFrameId, player.JoinIndex)
|
||||||
|
tmp, loaded := pR.DiscreteInputsBuffer.LoadAndDelete(bufIndex) // It's safe to "LoadAndDelete" here because the "inputFrameUpsync" of this player is already remembered by the corresponding "inputFrameDown".
|
||||||
|
if !loaded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
inputFrameUpsync := tmp.(*pb.InputFrameUpsync)
|
||||||
|
indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1)
|
||||||
|
inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr] = pR.EncodeUpsyncCmd(inputFrameUpsync)
|
||||||
|
inputFrameDownsync.ConfirmedList |= (1 << indiceInJoinIndexBooleanArr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force confirmation of "inputFrame2"
|
||||||
|
if allConfirmedMask == inputFrameDownsync.ConfirmedList {
|
||||||
|
pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, -1)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (pR *Room) forceConfirmationIfApplicable() uint64 {
|
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
|
// 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
|
renderFrameId1 := (pR.RenderFrameId - pR.NstDelayFrames) // the renderFrameId which should've been rendered on frontend
|
||||||
@@ -1151,19 +1198,21 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor
|
baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor
|
||||||
dx := baseChange * float64(decodedInput[0])
|
oldDx, oldDy := baseChange*float64(decodedInput[0]), baseChange*float64(decodedInput[1])
|
||||||
dy := baseChange * float64(decodedInput[1])
|
dx, dy := oldDx, oldDy
|
||||||
|
|
||||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||||
playerCollider := pR.CollisionSysMap[collisionPlayerIndex]
|
playerCollider := pR.CollisionSysMap[collisionPlayerIndex]
|
||||||
if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil {
|
if collision := playerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil {
|
||||||
changeWithCollision := collision.ContactWithObject(collision.Objects[0])
|
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
Logger.Info(fmt.Sprintf("Collided: roomId=%v, playerId=%v, orig dx=%v, orig dy=%v, proposed new dx =%v, proposed new dy=%v", pR.Id, player.Id, dx, dy, changeWithCollision.X(), changeWithCollision.Y()))
|
barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon)
|
||||||
// FIXME: Use a mechanism equivalent to that of the frontend!
|
if overlapped, pushbackX, pushbackY := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
|
||||||
// dx = changeWithCollision.X()
|
Logger.Debug(fmt.Sprintf("Collided & overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
||||||
// dy = changeWithCollision.Y()
|
dx -= pushbackX
|
||||||
dx = 0
|
dy -= pushbackY
|
||||||
dy = 0
|
} else {
|
||||||
|
Logger.Debug(fmt.Sprintf("Collider BUT not overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
playerCollider.X += dx
|
playerCollider.X += dx
|
||||||
playerCollider.Y += dy
|
playerCollider.Y += dy
|
||||||
@@ -1200,12 +1249,10 @@ func (pR *Room) refreshColliders() {
|
|||||||
spaceOffsetX := float64(spaceW) * 0.5
|
spaceOffsetX := float64(spaceW) * 0.5
|
||||||
spaceOffsetY := float64(spaceH) * 0.5
|
spaceOffsetY := float64(spaceH) * 0.5
|
||||||
|
|
||||||
minStep := int(3) // the approx minimum distance a player can move per frame
|
minStep := int(3) // the approx minimum distance a player can move per frame
|
||||||
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled
|
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled
|
||||||
for _, player := range pR.Players {
|
for _, player := range pR.Players {
|
||||||
playerCollider := resolv.NewObject(player.X+spaceOffsetX, player.Y+spaceOffsetY, playerColliderRadius*2, playerColliderRadius*2)
|
playerCollider := GenerateRectCollider(player.X, player.Y, playerColliderRadius*2, playerColliderRadius*2, spaceOffsetX, spaceOffsetY, "Player")
|
||||||
playerColliderShape := resolv.NewCircle(0, 0, playerColliderRadius*2)
|
|
||||||
playerCollider.SetShape(playerColliderShape)
|
|
||||||
space.Add(playerCollider)
|
space.Add(playerCollider)
|
||||||
// Keep track of the collider in "pR.CollisionSysMap"
|
// Keep track of the collider in "pR.CollisionSysMap"
|
||||||
joinIndex := player.JoinIndex
|
joinIndex := player.JoinIndex
|
||||||
@@ -1215,31 +1262,8 @@ func (pR *Room) refreshColliders() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, barrier := range pR.Barriers {
|
for _, barrier := range pR.Barriers {
|
||||||
|
boundaryUnaligned := barrier.Boundary
|
||||||
var w float64 = 0
|
barrierCollider := GenerateConvexPolygonCollider(boundaryUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
|
||||||
var h float64 = 0
|
|
||||||
|
|
||||||
for i, pi := range barrier.Boundary.Points {
|
|
||||||
for j, pj := range barrier.Boundary.Points {
|
|
||||||
if i == j {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if math.Abs(pj.X-pi.X) > w {
|
|
||||||
w = math.Abs(pj.X - pi.X)
|
|
||||||
}
|
|
||||||
if math.Abs(pj.Y-pi.Y) > h {
|
|
||||||
h = math.Abs(pj.Y - pi.Y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
barrierColliderShape := resolv.NewConvexPolygon()
|
|
||||||
for _, p := range barrier.Boundary.Points {
|
|
||||||
barrierColliderShape.AddPoints(p.X, p.Y)
|
|
||||||
}
|
|
||||||
|
|
||||||
barrierCollider := resolv.NewObject(barrier.Boundary.Anchor.X+spaceOffsetX, barrier.Boundary.Anchor.Y+spaceOffsetY, w, h, "Barrier")
|
|
||||||
barrierCollider.SetShape(barrierColliderShape)
|
|
||||||
space.Add(barrierCollider)
|
space.Add(barrierCollider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -85,8 +85,8 @@ type Game struct {
|
|||||||
|
|
||||||
func NewGame() *Game {
|
func NewGame() *Game {
|
||||||
|
|
||||||
// stageName := "simple" // Use this for calibration
|
stageName := "simple" // Use this for calibration
|
||||||
stageName := "richsoil"
|
// stageName := "richsoil"
|
||||||
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap, err := parseStage(stageName)
|
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap, err := parseStage(stageName)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -160,7 +160,6 @@ func (g *Game) DebugDraw(screen *ebiten.Image, space *resolv.Space) {
|
|||||||
ebitenutil.DrawLine(screen, cx, cy+ch, cx, cy, drawColor)
|
ebitenutil.DrawLine(screen, cx, cy+ch, cx, cy, drawColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Layout(w, h int) (int, int) {
|
func (g *Game) Layout(w, h int) (int, int) {
|
||||||
|
@@ -4,12 +4,9 @@ import (
|
|||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
||||||
"github.com/solarlune/resolv"
|
"github.com/solarlune/resolv"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WorldColliderDisplay struct {
|
type WorldColliderDisplay struct {
|
||||||
@@ -35,52 +32,48 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
|||||||
spaceOffsetX := float64(spaceW) * 0.5
|
spaceOffsetX := float64(spaceW) * 0.5
|
||||||
spaceOffsetY := float64(spaceH) * 0.5
|
spaceOffsetY := float64(spaceH) * 0.5
|
||||||
|
|
||||||
// TODO: Move collider y-axis transformation to a "dnmshared"
|
playerColliderRadius := float64(32)
|
||||||
playerColliderRadius := float64(12) // hardcoded
|
playerColliders := make([]*resolv.Object, len(playerList))
|
||||||
space := resolv.NewSpace(int(spaceW), int(spaceH), 16, 16)
|
space := resolv.NewSpace(int(spaceW), int(spaceH), 16, 16)
|
||||||
for _, player := range playerList {
|
for i, player := range playerList {
|
||||||
playerCollider := resolv.NewObject(player.X+spaceOffsetX, player.Y+spaceOffsetY, playerColliderRadius*2, playerColliderRadius*2, "Player")
|
playerCollider := GenerateRectCollider(player.X, player.Y, playerColliderRadius*2, playerColliderRadius*2, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
|
||||||
playerColliderShape := resolv.NewCircle(0, 0, playerColliderRadius*2)
|
Logger.Info(fmt.Sprintf("Player Collider#%d: player.X=%v, player.Y=%v, radius=%v, spaceOffsetX=%v, spaceOffsetY=%v, shape=%v; calibrationCheckX=player.X-radius+spaceOffsetX=%v", i, player.X, player.Y, playerColliderRadius, spaceOffsetX, spaceOffsetY, playerCollider.Shape, player.X-playerColliderRadius+spaceOffsetX))
|
||||||
playerCollider.SetShape(playerColliderShape)
|
playerColliders[i] = playerCollider
|
||||||
space.Add(playerCollider)
|
space.Add(playerCollider)
|
||||||
}
|
}
|
||||||
|
|
||||||
barrierLocalId := 0
|
barrierLocalId := 0
|
||||||
for _, barrierUnaligned := range barrierList {
|
for _, barrierUnaligned := range barrierList {
|
||||||
barrier := AlignPolygon2DToBoundingBox(barrierUnaligned)
|
barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
|
||||||
|
Logger.Info(fmt.Sprintf("Added barrier: shape=%v", barrierCollider.Shape))
|
||||||
var w float64 = 0
|
|
||||||
var h float64 = 0
|
|
||||||
|
|
||||||
for i, pi := range barrier.Points {
|
|
||||||
for j, pj := range barrier.Points {
|
|
||||||
if i == j {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if math.Abs(pj.X-pi.X) > w {
|
|
||||||
w = math.Abs(pj.X - pi.X)
|
|
||||||
}
|
|
||||||
if math.Abs(pj.Y-pi.Y) > h {
|
|
||||||
h = math.Abs(pj.Y - pi.Y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
barrierColliderShape := resolv.NewConvexPolygon()
|
|
||||||
for i := 0; i < len(barrier.Points); i++ {
|
|
||||||
p := barrier.Points[i]
|
|
||||||
barrierColliderShape.AddPoints(p.X, p.Y)
|
|
||||||
}
|
|
||||||
|
|
||||||
barrierCollider := resolv.NewObject(barrier.Anchor.X+spaceOffsetX, barrier.Anchor.Y+spaceOffsetY, w, h, "Barrier")
|
|
||||||
barrierCollider.SetShape(barrierColliderShape)
|
|
||||||
|
|
||||||
space.Add(barrierCollider)
|
space.Add(barrierCollider)
|
||||||
|
|
||||||
barrierLocalId++
|
barrierLocalId++
|
||||||
}
|
}
|
||||||
|
|
||||||
world.Space = space
|
world.Space = space
|
||||||
|
|
||||||
|
moveToCollide := true
|
||||||
|
if moveToCollide {
|
||||||
|
toTestPlayerCollider := playerColliders[0]
|
||||||
|
oldDx, oldDy := -2.98, -50.0
|
||||||
|
dx, dy := oldDx, oldDy
|
||||||
|
if collision := toTestPlayerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil {
|
||||||
|
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
|
barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon)
|
||||||
|
if overlapped, pushbackX, pushbackY := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
|
||||||
|
Logger.Info(fmt.Sprintf("Collided & overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", toTestPlayerCollider.X, toTestPlayerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
||||||
|
dx -= pushbackX
|
||||||
|
dy -= pushbackY
|
||||||
|
} else {
|
||||||
|
Logger.Info(fmt.Sprintf("Collider BUT not overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v", toTestPlayerCollider.X, toTestPlayerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toTestPlayerCollider.X += dx
|
||||||
|
toTestPlayerCollider.Y += dy
|
||||||
|
toTestPlayerCollider.Update()
|
||||||
|
}
|
||||||
|
|
||||||
return world
|
return world
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,9 +85,8 @@ func (world *WorldColliderDisplay) Draw(screen *ebiten.Image) {
|
|||||||
|
|
||||||
for _, o := range world.Space.Objects() {
|
for _, o := range world.Space.Objects() {
|
||||||
if o.HasTags("Player") {
|
if o.HasTags("Player") {
|
||||||
circle := o.Shape.(*resolv.Circle)
|
|
||||||
drawColor := color.RGBA{0, 255, 0, 255}
|
drawColor := color.RGBA{0, 255, 0, 255}
|
||||||
ebitenutil.DrawCircle(screen, circle.X, circle.Y, circle.Radius, drawColor)
|
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
||||||
} else {
|
} else {
|
||||||
drawColor := color.RGBA{60, 60, 60, 255}
|
drawColor := color.RGBA{60, 60, 60, 255}
|
||||||
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
||||||
|
@@ -15,6 +15,10 @@ type Vec2D struct {
|
|||||||
Y float64 `json:"y,omitempty"`
|
Y float64 `json:"y,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NormVec2D(dx, dy float64) Vec2D {
|
||||||
|
return Vec2D{dy, -dx}
|
||||||
|
}
|
||||||
|
|
||||||
type Polygon2D struct {
|
type Polygon2D struct {
|
||||||
Anchor *Vec2D `json:"-"` // This "Polygon2D.Anchor" is used to be assigned to "B2BodyDef.Position", which in turn is used as the position of the FIRST POINT of the polygon.
|
Anchor *Vec2D `json:"-"` // This "Polygon2D.Anchor" is used to be assigned to "B2BodyDef.Position", which in turn is used as the position of the FIRST POINT of the polygon.
|
||||||
Points []*Vec2D `json:"-"`
|
Points []*Vec2D `json:"-"`
|
||||||
|
220
dnmshared/resolv_helper.go
Normal file
220
dnmshared/resolv_helper.go
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
package dnmshared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kvartborg/vector"
|
||||||
|
"github.com/solarlune/resolv"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
|
||||||
|
var s []string = make([]string, len(body.Points))
|
||||||
|
for i, p := range body.Points {
|
||||||
|
s[i] = fmt.Sprintf("[%v, %v]", p[0]+body.X, p[1]+body.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[%s]", strings.Join(s, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRectCollider(origX, origY, w, h, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
|
||||||
|
collider := resolv.NewObject(origX-w*0.5+spaceOffsetX, origY-h*0.5+spaceOffsetY, w, h, tag)
|
||||||
|
shape := resolv.NewRectangle(0, 0, w, h)
|
||||||
|
collider.SetShape(shape)
|
||||||
|
return collider
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
|
||||||
|
aligned := AlignPolygon2DToBoundingBox(unalignedSrc)
|
||||||
|
var w, h float64 = 0, 0
|
||||||
|
|
||||||
|
shape := resolv.NewConvexPolygon()
|
||||||
|
for i, pi := range aligned.Points {
|
||||||
|
for j, pj := range aligned.Points {
|
||||||
|
if i == j {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if math.Abs(pj.X-pi.X) > w {
|
||||||
|
w = math.Abs(pj.X - pi.X)
|
||||||
|
}
|
||||||
|
if math.Abs(pj.Y-pi.Y) > h {
|
||||||
|
h = math.Abs(pj.Y - pi.Y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(aligned.Points); i++ {
|
||||||
|
p := aligned.Points[i]
|
||||||
|
shape.AddPoints(p.X, p.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
collider := resolv.NewObject(aligned.Anchor.X+spaceOffsetX, aligned.Anchor.Y+spaceOffsetY, w, h, tag)
|
||||||
|
collider.SetShape(shape)
|
||||||
|
|
||||||
|
return collider
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.ConvexPolygon) (bool, float64, float64) {
|
||||||
|
origX, origY := playerShape.Position()
|
||||||
|
defer func() {
|
||||||
|
playerShape.SetPosition(origX, origY)
|
||||||
|
}()
|
||||||
|
playerShape.SetPosition(origX+oldDx, origY+oldDy)
|
||||||
|
overlapResult := &SatResult{
|
||||||
|
Overlap: 0,
|
||||||
|
OverlapX: 0,
|
||||||
|
OverlapY: 0,
|
||||||
|
AContainedInB: true,
|
||||||
|
BContainedInA: true,
|
||||||
|
Axis: vector.Vector{0, 0},
|
||||||
|
}
|
||||||
|
if overlapped := IsPolygonPairOverlapped(playerShape, barrierShape, overlapResult); overlapped {
|
||||||
|
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
|
||||||
|
return true, pushbackX, pushbackY
|
||||||
|
} else {
|
||||||
|
return false, 0, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SatResult struct {
|
||||||
|
Overlap float64
|
||||||
|
OverlapX float64
|
||||||
|
OverlapY float64
|
||||||
|
AContainedInB bool
|
||||||
|
BContainedInA bool
|
||||||
|
Axis vector.Vector
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool {
|
||||||
|
aCnt, bCnt := len(a.Points), len(b.Points)
|
||||||
|
// Single point case
|
||||||
|
if 1 == aCnt && 1 == bCnt {
|
||||||
|
if nil != result {
|
||||||
|
result.Overlap = 0
|
||||||
|
}
|
||||||
|
return a.Points[0].X() == b.Points[0].X() && a.Points[0].Y() == b.Points[0].Y()
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 < aCnt {
|
||||||
|
for _, axis := range a.SATAxes() {
|
||||||
|
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 < bCnt {
|
||||||
|
for _, axis := range b.SATAxes() {
|
||||||
|
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, result *SatResult) bool {
|
||||||
|
/*
|
||||||
|
[WARNING] This function is deliberately made private, it shouldn't be used alone (i.e. not along the norms of a polygon), otherwise the pushbacks calculated would be meaningless.
|
||||||
|
|
||||||
|
Consider the following example
|
||||||
|
a: {
|
||||||
|
anchor: [1337.19 1696.74]
|
||||||
|
points: [[0 0] [24 0] [24 24] [0 24]]
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
anchor: [1277.72 1570.56]
|
||||||
|
points: [[642.57 319.16] [0 319.16] [5.73 0] [643.75 0.90]]
|
||||||
|
}
|
||||||
|
|
||||||
|
e = (-2.98, 1.49).Unit()
|
||||||
|
*/
|
||||||
|
|
||||||
|
var aStart, aEnd, bStart, bEnd float64 = math.MaxFloat64, -math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64
|
||||||
|
for _, p := range a.Points {
|
||||||
|
dot := (p.X()+a.X)*e.X() + (p.Y()+a.Y)*e.Y()
|
||||||
|
|
||||||
|
if aStart > dot {
|
||||||
|
aStart = dot
|
||||||
|
}
|
||||||
|
|
||||||
|
if aEnd < dot {
|
||||||
|
aEnd = dot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range b.Points {
|
||||||
|
dot := (p.X()+b.X)*e.X() + (p.Y()+b.Y)*e.Y()
|
||||||
|
|
||||||
|
if bStart > dot {
|
||||||
|
bStart = dot
|
||||||
|
}
|
||||||
|
|
||||||
|
if bEnd < dot {
|
||||||
|
bEnd = dot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if aStart > bEnd || aEnd < bStart {
|
||||||
|
// Separated by unit vector "e"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil != result {
|
||||||
|
result.Axis = e
|
||||||
|
overlap := float64(0)
|
||||||
|
|
||||||
|
if aStart < bStart {
|
||||||
|
result.AContainedInB = false
|
||||||
|
|
||||||
|
if aEnd < bEnd {
|
||||||
|
overlap = aEnd - bStart
|
||||||
|
result.BContainedInA = false
|
||||||
|
} else {
|
||||||
|
option1 := aEnd - bStart
|
||||||
|
option2 := bEnd - aStart
|
||||||
|
if option1 < option2 {
|
||||||
|
overlap = option1
|
||||||
|
} else {
|
||||||
|
overlap = -option2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.BContainedInA = false
|
||||||
|
|
||||||
|
if aEnd > bEnd {
|
||||||
|
overlap = aStart - bEnd
|
||||||
|
result.AContainedInB = false
|
||||||
|
} else {
|
||||||
|
option1 := aEnd - bStart
|
||||||
|
option2 := bEnd - aStart
|
||||||
|
if option1 < option2 {
|
||||||
|
overlap = option1
|
||||||
|
} else {
|
||||||
|
overlap = -option2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentOverlap := result.Overlap
|
||||||
|
absoluteOverlap := overlap
|
||||||
|
if overlap < 0 {
|
||||||
|
absoluteOverlap = -overlap
|
||||||
|
}
|
||||||
|
|
||||||
|
if 0 == currentOverlap || currentOverlap > absoluteOverlap {
|
||||||
|
var sign float64 = 1
|
||||||
|
if overlap < 0 {
|
||||||
|
sign = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Overlap = absoluteOverlap
|
||||||
|
result.OverlapX = e.X() * sign
|
||||||
|
result.OverlapY = e.Y() * sign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the specified unit vector "e" doesn't separate "a" and "b", overlap result is generated
|
||||||
|
return false
|
||||||
|
}
|
@@ -444,38 +444,38 @@ func (pTmxMapIns *TmxMap) continuousObjLayerOffsetToContinuousMapNodePos(continu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
|
func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
|
||||||
// Transform again to put "anchor" at the top-left point of the bounding box for "resolv"
|
// Transform again to put "anchor" at the top-left point of the bounding box for "resolv"
|
||||||
float64Max := float64(99999999999999.9)
|
float64Max := float64(99999999999999.9)
|
||||||
boundingBoxTL := &Vec2D{
|
boundingBoxTL := &Vec2D{
|
||||||
X: float64Max,
|
X: float64Max,
|
||||||
Y: float64Max,
|
Y: float64Max,
|
||||||
}
|
}
|
||||||
for _, p := range input.Points {
|
for _, p := range input.Points {
|
||||||
if p.X < boundingBoxTL.X {
|
if p.X < boundingBoxTL.X {
|
||||||
boundingBoxTL.X = p.X
|
boundingBoxTL.X = p.X
|
||||||
}
|
}
|
||||||
if p.Y < boundingBoxTL.Y {
|
if p.Y < boundingBoxTL.Y {
|
||||||
boundingBoxTL.Y = p.Y
|
boundingBoxTL.Y = p.Y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now "input.Anchor" should move to "input.Anchor+boundingBoxTL", thus "boundingBoxTL" is also the value of the negative diff for all "input.Points"
|
// Now "input.Anchor" should move to "input.Anchor+boundingBoxTL", thus "boundingBoxTL" is also the value of the negative diff for all "input.Points"
|
||||||
output := &Polygon2D{
|
output := &Polygon2D{
|
||||||
Anchor: &Vec2D{
|
Anchor: &Vec2D{
|
||||||
X: input.Anchor.X+boundingBoxTL.X,
|
X: input.Anchor.X + boundingBoxTL.X,
|
||||||
Y: input.Anchor.Y+boundingBoxTL.Y,
|
Y: input.Anchor.Y + boundingBoxTL.Y,
|
||||||
},
|
},
|
||||||
Points: make([]*Vec2D, len(input.Points)),
|
Points: make([]*Vec2D, len(input.Points)),
|
||||||
TileWidth: input.TileWidth,
|
TileWidth: input.TileWidth,
|
||||||
TileHeight: input.TileHeight,
|
TileHeight: input.TileHeight,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, p := range input.Points {
|
for i, p := range input.Points {
|
||||||
output.Points[i] = &Vec2D{
|
output.Points[i] = &Vec2D{
|
||||||
X: p.X-boundingBoxTL.X,
|
X: p.X - boundingBoxTL.X,
|
||||||
Y: p.Y-boundingBoxTL.Y,
|
Y: p.Y - boundingBoxTL.Y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
@@ -539,7 +539,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
210.4441731196186,
|
342.9460598986377,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@@ -440,7 +440,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
209.73151519075364,
|
344.75930058781137,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@@ -67,19 +67,6 @@ cc.Class({
|
|||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
|
||||||
//kobako: 腾讯统计代码
|
|
||||||
//WARN: 打包到微信小游戏的时候会导致出错
|
|
||||||
/*
|
|
||||||
(function() {
|
|
||||||
var mta = document.createElement("script");
|
|
||||||
mta.src = "//pingjs.qq.com/h5/stats.js?v2.0.4";
|
|
||||||
mta.setAttribute("name", "MTAH5");
|
|
||||||
mta.setAttribute("sid", "500674632");
|
|
||||||
var s = document.getElementsByTagName("script")[0];
|
|
||||||
s.parentNode.insertBefore(mta, s);
|
|
||||||
})();
|
|
||||||
*/
|
|
||||||
|
|
||||||
window.atFirstLocationHref = window.location.href.split('#')[0];
|
window.atFirstLocationHref = window.location.href.split('#')[0];
|
||||||
const self = this;
|
const self = this;
|
||||||
self.getRetCodeList();
|
self.getRetCodeList();
|
||||||
@@ -97,10 +84,8 @@ cc.Class({
|
|||||||
self.smsLoginCaptchaLabel.active = true;
|
self.smsLoginCaptchaLabel.active = true;
|
||||||
|
|
||||||
self.loginButton.active = true;
|
self.loginButton.active = true;
|
||||||
self.checkPhoneNumber = self.checkPhoneNumber.bind(self);
|
self.onLoginButtonClicked = self.onLoginButtonClicked.bind(self);
|
||||||
self.checkIntAuthTokenExpire = self.checkIntAuthTokenExpire.bind(self);
|
self.onSMSCaptchaGetButtonClicked = self.onSMSCaptchaGetButtonClicked.bind(self);
|
||||||
self.checkCaptcha = self.checkCaptcha.bind(self);
|
|
||||||
self.onSMSCaptchaGetButtonClicked = self.onSMSCaptchaGetButtonClicked.bind(self);
|
|
||||||
self.smsLoginCaptchaButton.on('click', self.onSMSCaptchaGetButtonClicked);
|
self.smsLoginCaptchaButton.on('click', self.onSMSCaptchaGetButtonClicked);
|
||||||
|
|
||||||
self.loadingNode = cc.instantiate(this.loadingPrefab);
|
self.loadingNode = cc.instantiate(this.loadingPrefab);
|
||||||
@@ -125,11 +110,12 @@ cc.Class({
|
|||||||
window.WsReq = protoRoot.lookupType("treasurehunterx.WsReq");
|
window.WsReq = protoRoot.lookupType("treasurehunterx.WsReq");
|
||||||
window.WsResp = protoRoot.lookupType("treasurehunterx.WsResp");
|
window.WsResp = protoRoot.lookupType("treasurehunterx.WsResp");
|
||||||
self.checkIntAuthTokenExpire().then(
|
self.checkIntAuthTokenExpire().then(
|
||||||
() => {
|
(intAuthToken) => {
|
||||||
const intAuthToken = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')).intAuthToken;
|
console.log("Successfully found `intAuthToken` in local cache");
|
||||||
self.useTokenLogin(intAuthToken);
|
self.useTokenLogin(intAuthToken);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
|
console.warn("Failed to find `intAuthToken` in local cache");
|
||||||
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
|
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -221,11 +207,28 @@ cc.Class({
|
|||||||
checkIntAuthTokenExpire() {
|
checkIntAuthTokenExpire() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!cc.sys.localStorage.getItem('selfPlayer')) {
|
if (!cc.sys.localStorage.getItem('selfPlayer')) {
|
||||||
|
console.warn("Couldn't find selfPlayer key in local cache");
|
||||||
reject();
|
reject();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const selfPlayer = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
|
const selfPlayer = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
|
||||||
(selfPlayer.intAuthToken && new Date().getTime() < selfPlayer.expiresAt) ? resolve() : reject();
|
if (null == selfPlayer) {
|
||||||
|
console.warn("Couldn't find selfPlayer object in local cache");
|
||||||
|
reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == selfPlayer.intAuthToken) {
|
||||||
|
console.warn("Couldn't find selfPlayer object with key `intAuthToken` in local cache");
|
||||||
|
reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (new Date().getTime() > selfPlayer.expiresAt) {
|
||||||
|
console.warn("Couldn't find unexpired selfPlayer `intAuthToken` in local cache");
|
||||||
|
reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(selfPlayer.intAuthToken);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -278,13 +281,15 @@ cc.Class({
|
|||||||
intAuthToken: _intAuthToken
|
intAuthToken: _intAuthToken
|
||||||
},
|
},
|
||||||
success: function(resp) {
|
success: function(resp) {
|
||||||
|
console.log("Login attempt `useTokenLogin` succeeded.");
|
||||||
self.onLoggedIn(resp);
|
self.onLoggedIn(resp);
|
||||||
},
|
},
|
||||||
error: function(xhr, status, errMsg) {
|
error: function(xhr, status, errMsg) {
|
||||||
console.log("Login attempt `useTokenLogin` failed, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
|
console.warn("Login attempt `useTokenLogin` failed, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
|
||||||
window.clearBoundRoomIdInBothVolatileAndPersistentStorage()
|
window.clearBoundRoomIdInBothVolatileAndPersistentStorage()
|
||||||
},
|
},
|
||||||
timeout: function() {
|
timeout: function() {
|
||||||
|
console.warn("Login attempt `useTokenLogin` timed out, about to enable interactive controls.");
|
||||||
self.enableInteractiveControls(true);
|
self.enableInteractiveControls(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -335,7 +340,7 @@ cc.Class({
|
|||||||
|
|
||||||
onLoggedIn(res) {
|
onLoggedIn(res) {
|
||||||
const self = this;
|
const self = this;
|
||||||
cc.log(`OnLoggedIn ${JSON.stringify(res)}.`)
|
console.log("OnLoggedIn ", JSON.stringify(res))
|
||||||
if (res.ret === self.retCodeDict.OK) {
|
if (res.ret === self.retCodeDict.OK) {
|
||||||
self.enableInteractiveControls(false);
|
self.enableInteractiveControls(false);
|
||||||
const date = Number(res.expiresAt);
|
const date = Number(res.expiresAt);
|
||||||
@@ -360,6 +365,7 @@ cc.Class({
|
|||||||
);
|
);
|
||||||
cc.director.loadScene('default_map');
|
cc.director.loadScene('default_map');
|
||||||
} else {
|
} else {
|
||||||
|
console.log("OnLoggedIn failed, about to remove `selfPlayer` in local cache.")
|
||||||
cc.sys.localStorage.removeItem("selfPlayer");
|
cc.sys.localStorage.removeItem("selfPlayer");
|
||||||
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
|
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
|
||||||
self.enableInteractiveControls(true);
|
self.enableInteractiveControls(true);
|
||||||
|
@@ -253,8 +253,8 @@ cc.Class({
|
|||||||
if (null != window.handleBattleColliderInfo) {
|
if (null != window.handleBattleColliderInfo) {
|
||||||
window.handleBattleColliderInfo = null;
|
window.handleBattleColliderInfo = null;
|
||||||
}
|
}
|
||||||
if (null != window.handleClientSessionCloseOrError) {
|
if (null != window.handleClientSessionError) {
|
||||||
window.handleClientSessionCloseOrError = null;
|
window.handleClientSessionError = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -349,8 +349,8 @@ cc.Class({
|
|||||||
window.forceBigEndianFloatingNumDecoding = self.forceBigEndianFloatingNumDecoding;
|
window.forceBigEndianFloatingNumDecoding = self.forceBigEndianFloatingNumDecoding;
|
||||||
|
|
||||||
console.warn("+++++++ Map onLoad()");
|
console.warn("+++++++ Map onLoad()");
|
||||||
window.handleClientSessionCloseOrError = function() {
|
window.handleClientSessionError = function() {
|
||||||
console.warn('+++++++ Common handleClientSessionCloseOrError()');
|
console.warn('+++++++ Common handleClientSessionError()');
|
||||||
|
|
||||||
if (ALL_BATTLE_STATES.IN_SETTLEMENT == self.battleState) {
|
if (ALL_BATTLE_STATES.IN_SETTLEMENT == self.battleState) {
|
||||||
console.log("Battled ended by settlement");
|
console.log("Battled ended by settlement");
|
||||||
@@ -472,7 +472,7 @@ cc.Class({
|
|||||||
pts.push([boundaryObj[i].x - x0, boundaryObj[i].y - y0]);
|
pts.push([boundaryObj[i].x - x0, boundaryObj[i].y - y0]);
|
||||||
}
|
}
|
||||||
const newBarrierLatest = self.latestCollisionSys.createPolygon(x0, y0, pts);
|
const newBarrierLatest = self.latestCollisionSys.createPolygon(x0, y0, pts);
|
||||||
console.log("Created barrier: ", newBarrierLatest);
|
// console.log("Created barrier: ", newBarrierLatest);
|
||||||
const newBarrierChaser = self.chaserCollisionSys.createPolygon(x0, y0, pts);
|
const newBarrierChaser = self.chaserCollisionSys.createPolygon(x0, y0, pts);
|
||||||
++barrierIdCounter;
|
++barrierIdCounter;
|
||||||
const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter);
|
const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter);
|
||||||
@@ -740,9 +740,13 @@ cc.Class({
|
|||||||
newPlayerNode.setPosition(cc.v2(x, y));
|
newPlayerNode.setPosition(cc.v2(x, y));
|
||||||
newPlayerNode.getComponent("SelfPlayer").mapNode = self.node;
|
newPlayerNode.getComponent("SelfPlayer").mapNode = self.node;
|
||||||
const currentSelfColliderCircle = newPlayerNode.getComponent(cc.CircleCollider);
|
const currentSelfColliderCircle = newPlayerNode.getComponent(cc.CircleCollider);
|
||||||
|
const r = currentSelfColliderCircle.radius, d = 2*r;
|
||||||
|
// The collision box of an individual player is a polygon instead of a circle, because the backend collision engine doesn't handle circle alignment well.
|
||||||
|
const x0 = x-r, y0 = y-r;
|
||||||
|
let pts = [[0, 0], [d, 0], [d, d], [0, d]];
|
||||||
|
|
||||||
const newPlayerColliderLatest = self.latestCollisionSys.createCircle(x, y, currentSelfColliderCircle.radius);
|
const newPlayerColliderLatest = self.latestCollisionSys.createPolygon(x0, y0, pts);
|
||||||
const newPlayerColliderChaser = self.chaserCollisionSys.createCircle(x, y, currentSelfColliderCircle.radius);
|
const newPlayerColliderChaser = self.chaserCollisionSys.createPolygon(x0, y0, pts);
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||||
self.latestCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderLatest);
|
self.latestCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderLatest);
|
||||||
self.chaserCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderChaser);
|
self.chaserCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderChaser);
|
||||||
@@ -952,10 +956,12 @@ cc.Class({
|
|||||||
const joinIndex = playerRichInfo.joinIndex;
|
const joinIndex = playerRichInfo.joinIndex;
|
||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||||
|
const currentSelfColliderCircle = playerRichInfo.node.getComponent(cc.CircleCollider);
|
||||||
|
const r = currentSelfColliderCircle.radius;
|
||||||
rdf.players[playerRichInfo.id] = {
|
rdf.players[playerRichInfo.id] = {
|
||||||
id: playerRichInfo.id,
|
id: playerRichInfo.id,
|
||||||
x: playerCollider.x,
|
x: playerCollider.x + r, // [WARNING] the (x, y) of "playerCollider" is offset to the anchor (i.e. first point of all points) of the polygon shape
|
||||||
y: playerCollider.y,
|
y: playerCollider.y + r,
|
||||||
dir: self.ctrl.decodeDirection(null == inputFrameAppliedOnPrevRenderFrame ? 0 : inputFrameAppliedOnPrevRenderFrame.inputList[joinIndex - 1]),
|
dir: self.ctrl.decodeDirection(null == inputFrameAppliedOnPrevRenderFrame ? 0 : inputFrameAppliedOnPrevRenderFrame.inputList[joinIndex - 1]),
|
||||||
speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed),
|
speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed),
|
||||||
joinIndex: joinIndex
|
joinIndex: joinIndex
|
||||||
@@ -1025,8 +1031,11 @@ cc.Class({
|
|||||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||||
const player = latestRdf.players[playerId];
|
const player = latestRdf.players[playerId];
|
||||||
playerCollider.x = player.x;
|
|
||||||
playerCollider.y = player.y;
|
const currentSelfColliderCircle = playerRichInfo.node.getComponent(cc.CircleCollider);
|
||||||
|
const r = currentSelfColliderCircle.radius;
|
||||||
|
playerCollider.x = player.x - r;
|
||||||
|
playerCollider.y = player.y - r;
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -104,18 +104,6 @@ window.getExpectedRoomIdSync = function() {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
window.unsetClientSessionCloseOrErrorFlag = function() {
|
|
||||||
cc.sys.localStorage.removeItem("ClientSessionCloseOrErrorFlag");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.setClientSessionCloseOrErrorFlag = function() {
|
|
||||||
const oldVal = cc.sys.localStorage.getItem("ClientSessionCloseOrErrorFlag");
|
|
||||||
if (true == oldVal) return false;
|
|
||||||
cc.sys.localStorage.setItem("ClientSessionCloseOrErrorFlag", true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
||||||
if (window.clientSession && window.clientSession.readyState == WebSocket.OPEN) {
|
if (window.clientSession && window.clientSession.readyState == WebSocket.OPEN) {
|
||||||
if (null != onopenCb) {
|
if (null != onopenCb) {
|
||||||
@@ -123,8 +111,10 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const intAuthToken = cc.sys.localStorage.getItem("selfPlayer") ? JSON.parse(cc.sys.localStorage.getItem('selfPlayer')).intAuthToken : "";
|
const selfPlayerStr = cc.sys.localStorage.getItem("selfPlayer");
|
||||||
|
const selfPlayer = null == selfPlayerStr ? null : JSON.parse(selfPlayerStr);
|
||||||
|
const intAuthToken = null == selfPlayer ? "" : selfPlayer.intAuthToken;
|
||||||
|
|
||||||
let urlToConnect = backendAddress.PROTOCOL.replace('http', 'ws') + '://' + backendAddress.HOST + ":" + backendAddress.PORT + backendAddress.WS_PATH_PREFIX + "?intAuthToken=" + intAuthToken;
|
let urlToConnect = backendAddress.PROTOCOL.replace('http', 'ws') + '://' + backendAddress.HOST + ":" + backendAddress.PORT + backendAddress.WS_PATH_PREFIX + "?intAuthToken=" + intAuthToken;
|
||||||
|
|
||||||
@@ -144,19 +134,19 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
|||||||
const clientSession = new WebSocket(urlToConnect);
|
const clientSession = new WebSocket(urlToConnect);
|
||||||
clientSession.binaryType = 'arraybuffer'; // Make 'event.data' of 'onmessage' an "ArrayBuffer" instead of a "Blob"
|
clientSession.binaryType = 'arraybuffer'; // Make 'event.data' of 'onmessage' an "ArrayBuffer" instead of a "Blob"
|
||||||
|
|
||||||
clientSession.onopen = function(event) {
|
clientSession.onopen = function(evt) {
|
||||||
console.log("The WS clientSession is opened.");
|
console.log("The WS clientSession is opened. clientSession.id=", clientSession.id);
|
||||||
window.clientSession = clientSession;
|
window.clientSession = clientSession;
|
||||||
if (null == onopenCb) return;
|
if (null == onopenCb) return;
|
||||||
onopenCb();
|
onopenCb();
|
||||||
};
|
};
|
||||||
|
|
||||||
clientSession.onmessage = function(event) {
|
clientSession.onmessage = function(evt) {
|
||||||
if (null == event || null == event.data) {
|
if (null == evt || null == evt.data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const resp = window.WsResp.decode(new Uint8Array(event.data));
|
const resp = window.WsResp.decode(new Uint8Array(evt.data));
|
||||||
switch (resp.act) {
|
switch (resp.act) {
|
||||||
case window.DOWNSYNC_MSG_ACT_HB_REQ:
|
case window.DOWNSYNC_MSG_ACT_HB_REQ:
|
||||||
window.handleHbRequirements(resp); // 获取boundRoomId并存储到localStorage
|
window.handleHbRequirements(resp); // 获取boundRoomId并存储到localStorage
|
||||||
@@ -189,10 +179,11 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
|||||||
const inputFrameIdConsecutive = (resp.inputFrameDownsyncBatch[0].inputFrameId == mapIns.lastAllConfirmedInputFrameId + 1);
|
const inputFrameIdConsecutive = (resp.inputFrameDownsyncBatch[0].inputFrameId == mapIns.lastAllConfirmedInputFrameId + 1);
|
||||||
const renderFrameIdConsecutive = (resp.rdf.id <= mapIns.renderFrameId + mapIns.renderFrameIdLagTolerance);
|
const renderFrameIdConsecutive = (resp.rdf.id <= mapIns.renderFrameId + mapIns.renderFrameIdLagTolerance);
|
||||||
if (inputFrameIdConsecutive && renderFrameIdConsecutive) {
|
if (inputFrameIdConsecutive && renderFrameIdConsecutive) {
|
||||||
console.log("Got consecutive resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp));
|
// console.log("Got consecutive resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp));
|
||||||
mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch);
|
mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch);
|
||||||
} else {
|
} else {
|
||||||
console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp, null, 2));
|
// console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp, null, 2));
|
||||||
|
console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", inputFrameIdConsecutive=", inputFrameIdConsecutive, ", renderFrameIdConsecutive=", renderFrameIdConsecutive);
|
||||||
// The following order of execution is important
|
// The following order of execution is important
|
||||||
mapIns.onRoomDownsyncFrame(resp.rdf);
|
mapIns.onRoomDownsyncFrame(resp.rdf);
|
||||||
mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch);
|
mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch);
|
||||||
@@ -202,52 +193,46 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Unexpected error when parsing data of:", event.data, e);
|
console.error("Unexpected error when parsing data of:", evt.data, e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
clientSession.onerror = function(event) {
|
clientSession.onerror = function(evt) {
|
||||||
if (!window.setClientSessionCloseOrErrorFlag()) {
|
console.error("Error caught on the WS clientSession: ", evt);
|
||||||
return;
|
if (window.handleClientSessionError) {
|
||||||
|
window.handleClientSessionError();
|
||||||
}
|
}
|
||||||
console.error("Error caught on the WS clientSession: ", event);
|
|
||||||
if (window.clientSessionPingInterval) {
|
|
||||||
clearInterval(window.clientSessionPingInterval);
|
|
||||||
}
|
|
||||||
if (window.handleClientSessionCloseOrError) {
|
|
||||||
window.handleClientSessionCloseOrError();
|
|
||||||
}
|
|
||||||
window.unsetClientSessionCloseOrErrorFlag();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
clientSession.onclose = function(event) {
|
clientSession.onclose = function(evt) {
|
||||||
if (!window.setClientSessionCloseOrErrorFlag()) {
|
// [WARNING] The callback "onclose" might be called AFTER the webpage is refreshed with "1001 == evt.code".
|
||||||
return;
|
console.warn("The WS clientSession is closed: ", evt, clientSession);
|
||||||
}
|
if (false == evt.wasClean) {
|
||||||
console.warn("The WS clientSession is closed: ", event);
|
/*
|
||||||
if (window.clientSessionPingInterval) {
|
Chrome doesn't allow the use of "CustomCloseCode"s (yet) and will callback with a "WebsocketStdCloseCode 1006" and "false == evt.wasClean" here. See https://tools.ietf.org/html/rfc6455#section-7.4 for more information.
|
||||||
clearInterval(window.clientSessionPingInterval);
|
*/
|
||||||
}
|
if (window.handleClientSessionError) {
|
||||||
if (false == event.wasClean) {
|
window.handleClientSessionError();
|
||||||
// Chrome doesn't allow the use of "CustomCloseCode"s (yet) and will callback with a "WebsocketStdCloseCode 1006" and "false == event.wasClean" here. See https://tools.ietf.org/html/rfc6455#section-7.4 for more information.
|
|
||||||
if (window.handleClientSessionCloseOrError) {
|
|
||||||
window.handleClientSessionCloseOrError();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (event.code) {
|
switch (evt.code) {
|
||||||
|
case constants.RET_CODE.PLAYER_NOT_ADDABLE_TO_ROOM:
|
||||||
|
case constants.RET_CODE.PLAYER_NOT_READDABLE_TO_ROOM:
|
||||||
|
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
|
||||||
|
break;
|
||||||
|
case constants.RET_CODE.UNKNOWN_ERROR:
|
||||||
|
case constants.RET_CODE.MYSQL_ERROR:
|
||||||
case constants.RET_CODE.PLAYER_NOT_FOUND:
|
case constants.RET_CODE.PLAYER_NOT_FOUND:
|
||||||
case constants.RET_CODE.PLAYER_CHEATING:
|
case constants.RET_CODE.PLAYER_CHEATING:
|
||||||
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
|
case 1006: // Peer(i.e. the backend) gone unexpectedly
|
||||||
|
if (window.handleClientSessionError) {
|
||||||
|
window.handleClientSessionError();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.handleClientSessionCloseOrError) {
|
|
||||||
window.handleClientSessionCloseOrError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
window.unsetClientSessionCloseOrErrorFlag();
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -258,17 +243,17 @@ window.clearLocalStorageAndBackToLoginScene = function(shouldRetainBoundRoomIdIn
|
|||||||
window.mapIns.musicEffectManagerScriptIns.stopAllMusic();
|
window.mapIns.musicEffectManagerScriptIns.stopAllMusic();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Here I deliberately removed the callback in the "common `handleClientSessionCloseOrError` callback"
|
* Here I deliberately removed the callback in the "common `handleClientSessionError` callback"
|
||||||
* within which another invocation to `clearLocalStorageAndBackToLoginScene` will be made.
|
* within which another invocation to `clearLocalStorageAndBackToLoginScene` will be made.
|
||||||
*
|
*
|
||||||
* It'll be re-assigned to the common one upon reentrance of `Map.onLoad`.
|
* It'll be re-assigned to the common one upon reentrance of `Map.onLoad`.
|
||||||
*
|
*
|
||||||
* -- YFLu 2019-04-06
|
* -- YFLu 2019-04-06
|
||||||
*/
|
*/
|
||||||
window.handleClientSessionCloseOrError = () => {
|
window.handleClientSessionError = () => {
|
||||||
console.warn("+++++++ Special handleClientSessionCloseOrError() assigned within `clearLocalStorageAndBackToLoginScene`");
|
console.warn("+++++++ Special handleClientSessionError() assigned within `clearLocalStorageAndBackToLoginScene`");
|
||||||
// TBD.
|
// TBD.
|
||||||
window.handleClientSessionCloseOrError = null; // To ensure that it's called at most once.
|
window.handleClientSessionError = null; // To ensure that it's called at most once.
|
||||||
};
|
};
|
||||||
window.closeWSConnection();
|
window.closeWSConnection();
|
||||||
window.clearSelfPlayer();
|
window.clearSelfPlayer();
|
||||||
|
39
frontend/assets/scripts/collision_test_nodejs.js
Normal file
39
frontend/assets/scripts/collision_test_nodejs.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
const collisions = require('./modules/Collisions');
|
||||||
|
|
||||||
|
const collisionSys = new collisions.Collisions();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Backend result reference
|
||||||
|
|
||||||
|
2022-10-22T12:11:25.156+0800 INFO collider_visualizer/worldColliderDisplay.go:77 Collided: player.X=1257.665, player.Y=1415.335, oldDx=-2.98, oldDy=-50, playerShape=&{[[0 0] [64 0] [64 64] [0 64]] 1254.685 1365.335 true}, toCheckBarrier=&{[[628.626 54.254500000000064] [0 56.03250000000003] [0.42449999999999477 1.1229999999999905] [625.9715000000001 0]] 1289.039 1318.0805 true}, pushbackX=-0.15848054013127655, pushbackY=-56.03205175509715, result=&{56.03227587710039 -0.0028283794946841584 -0.9999960001267175 false false [0.9988052279193613 -0.04886836073527201]}
|
||||||
|
*/
|
||||||
|
function polygonStr(body) {
|
||||||
|
let coords = [];
|
||||||
|
let cnt = body._coords.length;
|
||||||
|
for (let ix = 0, iy = 1; ix < cnt; ix += 2, iy += 2) {
|
||||||
|
coords.push([body._coords[ix], body._coords[iy]]);
|
||||||
|
}
|
||||||
|
return JSON.stringify(coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
const playerCollider = collisionSys.createPolygon(1257.665, 1415.335, [[0, 0], [64, 0], [64, 64], [0, 64]]);
|
||||||
|
const barrierCollider = collisionSys.createPolygon(1289.039, 1318.0805, [[628.626, 54.254500000000064], [0, 56.03250000000003], [0.42449999999999477, 1.1229999999999905], [625.9715000000001, 0]]);
|
||||||
|
|
||||||
|
const oldDx = -2.98;
|
||||||
|
const oldDy = -50.0;
|
||||||
|
|
||||||
|
playerCollider.x += oldDx;
|
||||||
|
playerCollider.y += oldDy;
|
||||||
|
|
||||||
|
collisionSys.update();
|
||||||
|
const result = collisionSys.createResult();
|
||||||
|
|
||||||
|
const potentials = playerCollider.potentials();
|
||||||
|
|
||||||
|
let overlapCheckId = 0;
|
||||||
|
for (const barrier of potentials) {
|
||||||
|
if (!playerCollider.collides(barrier, result)) continue;
|
||||||
|
const pushbackX = result.overlap * result.overlap_x;
|
||||||
|
const pushbackY = result.overlap * result.overlap_y;
|
||||||
|
console.log("For overlapCheckId=" + overlapCheckId + ", the overlap: a=", polygonStr(result.a), ", b=", polygonStr(result.b), ", pushbackX=", pushbackX, ", pushbackY=", pushbackY);
|
||||||
|
}
|
9
frontend/assets/scripts/collision_test_nodejs.js.meta
Normal file
9
frontend/assets/scripts/collision_test_nodejs.js.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.5",
|
||||||
|
"uuid": "fce86138-76fc-44d5-8eac-2731b3b0cefd",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user