Fixes for simultaneous reconnection w.r.t. same room, and updates for documentation.

This commit is contained in:
genxium 2022-11-27 00:00:39 +08:00
parent 9bce561441
commit e0fb21f3fb
6 changed files with 134 additions and 331 deletions

View File

@ -96,7 +96,7 @@ func getPlayer(cond sq.Eq) (*Player, error) {
p.CreatedAt = int64(val.(int64)) p.CreatedAt = int64(val.(int64))
} }
} }
Logger.Info("Queried player from db", zap.Any("cond", cond), zap.Any("p", p), zap.Any("pd", pd), zap.Any("cols", cols), zap.Any("rowValues", vals)) Logger.Debug("Queried player from db", zap.Any("cond", cond), zap.Any("p", p), zap.Any("pd", pd), zap.Any("cols", cols), zap.Any("rowValues", vals))
} }
p.PlayerDownsync = pd p.PlayerDownsync = pd
return &p, nil return &p, nil

View File

@ -476,7 +476,7 @@ func (pR *Room) StartBattle() {
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 not yet active, because it could jam the channel and cause significant delay upon "battle recovery for reconnected player".
continue continue
} }
if 0 == pR.RenderFrameId { if 0 == pR.RenderFrameId {
@ -995,6 +995,12 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool {
This function is triggered by an upsync message via WebSocket, thus downsync sending is also available by now. This function is triggered by an upsync message via WebSocket, thus downsync sending is also available by now.
*/ */
currPlayerBattleState := atomic.LoadInt32(&(eachPlayer.BattleState))
if PlayerBattleStateIns.DISCONNECTED == currPlayerBattleState || PlayerBattleStateIns.LOST == currPlayerBattleState {
// [WARNING] DON'T try to send any message to an inactive player!
continue
}
switch targetPlayer.BattleState { switch targetPlayer.BattleState {
case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK: case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK:
playerAckedFrame := &RoomDownsyncFrame{ playerAckedFrame := &RoomDownsyncFrame{
@ -1338,7 +1344,7 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
thatPlayerInNextFrame := nextRenderFramePlayers[playerId] thatPlayerInNextFrame := nextRenderFramePlayers[playerId]
if 0 < thatPlayerInNextFrame.FramesToRecover { if 0 < thatPlayerInNextFrame.FramesToRecover {
// No need to process inputs for this player, but there might be bullet pushbacks on this player // No need to process inputs for this player, but there might be bullet pushbacks on this player
// Also note that in this case we keep "CharacterState" of this player from last render frame // Also note that in this case we keep "CharacterState" of this player from last render frame
playerCollider.X += bulletPushbacks[joinIndex-1].X playerCollider.X += bulletPushbacks[joinIndex-1].X
playerCollider.Y += bulletPushbacks[joinIndex-1].Y playerCollider.Y += bulletPushbacks[joinIndex-1].Y
// Update in the collision system // Update in the collision system
@ -1374,7 +1380,7 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
Logger.Debug(fmt.Sprintf("roomId=%v, playerId=%v triggered a falling-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId)) Logger.Debug(fmt.Sprintf("roomId=%v, playerId=%v triggered a falling-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId))
} else { } else {
// No bullet trigger, process movement inputs // No bullet trigger, process movement inputs
// Note that by now "0 == thatPlayerInNextFrame.FramesToRecover", we should change "CharacterState" to "WALKING" or "IDLE" depending on player inputs // Note that by now "0 == thatPlayerInNextFrame.FramesToRecover", we should change "CharacterState" to "WALKING" or "IDLE" depending on player inputs
if 0 != decodedInput.Dx || 0 != decodedInput.Dy { if 0 != decodedInput.Dx || 0 != decodedInput.Dy {
thatPlayerInNextFrame.DirX = decodedInput.Dx thatPlayerInNextFrame.DirX = decodedInput.Dx
thatPlayerInNextFrame.DirY = decodedInput.Dy thatPlayerInNextFrame.DirY = decodedInput.Dy

View File

@ -5,6 +5,7 @@ import (
. "dnmshared" . "dnmshared"
"fmt" "fmt"
"go.uber.org/zap" "go.uber.org/zap"
"strings"
"sync" "sync"
) )
@ -21,11 +22,13 @@ var (
func (pPq *RoomHeap) PrintInOrder() { func (pPq *RoomHeap) PrintInOrder() {
pq := *pPq pq := *pPq
fmt.Printf("The RoomHeap instance now contains:\n") s := make([]string, 0)
s = append(s, fmt.Sprintf("The RoomHeap instance now contains:"))
for i := 0; i < len(pq); i++ { for i := 0; i < len(pq); i++ {
fmt.Printf("{index: %d, roomID: %d, score: %.2f} ", i, pq[i].Id, pq[i].Score) s = append(s, fmt.Sprintf("{index: %d, roomID: %d, score: %.2f} ", i, pq[i].Id, pq[i].Score))
} }
fmt.Printf("\n")
Logger.Debug(strings.Join(s, "\n"))
} }
func (pq RoomHeap) Len() int { return len(pq) } func (pq RoomHeap) Len() int { return len(pq) }

View File

@ -16,7 +16,6 @@ import (
"time" "time"
. "dnmshared" . "dnmshared"
"runtime/debug"
) )
const ( const (
@ -47,9 +46,8 @@ func Serve(c *gin.Context) {
c.AbortWithStatus(http.StatusBadRequest) c.AbortWithStatus(http.StatusBadRequest)
return return
} }
Logger.Info("Finding PlayerLogin record for ws authentication:", zap.Any("intAuthToken", token))
boundRoomId := 0 boundRoomId := 0
expectRoomId := 0 expectedRoomId := 0
var err error var err error
if boundRoomIdStr, hasBoundRoomId := c.GetQuery("boundRoomId"); hasBoundRoomId { if boundRoomIdStr, hasBoundRoomId := c.GetQuery("boundRoomId"); hasBoundRoomId {
boundRoomId, err = strconv.Atoi(boundRoomIdStr) boundRoomId, err = strconv.Atoi(boundRoomIdStr)
@ -58,27 +56,28 @@ func Serve(c *gin.Context) {
c.AbortWithStatus(http.StatusBadRequest) c.AbortWithStatus(http.StatusBadRequest)
return return
} }
Logger.Info("Finding PlayerLogin record for ws authentication:", zap.Any("intAuthToken", token), zap.Any("boundRoomId", boundRoomId)) Logger.Debug("Finding PlayerLogin record for ws authentication:", zap.Any("intAuthToken", token), zap.Any("boundRoomId", boundRoomId))
} } else if expectedRoomIdStr, hasExpectRoomId := c.GetQuery("expectedRoomId"); hasExpectRoomId {
if expectRoomIdStr, hasExpectRoomId := c.GetQuery("expectedRoomId"); hasExpectRoomId { expectedRoomId, err = strconv.Atoi(expectedRoomIdStr)
expectRoomId, err = strconv.Atoi(expectRoomIdStr)
if err != nil { if err != nil {
c.AbortWithStatus(http.StatusBadRequest) c.AbortWithStatus(http.StatusBadRequest)
return return
} }
Logger.Info("Finding PlayerLogin record for ws authentication:", zap.Any("intAuthToken", token), zap.Any("expectedRoomId", expectRoomId)) Logger.Debug("Finding PlayerLogin record for ws authentication:", zap.Any("intAuthToken", token), zap.Any("expectedRoomId", expectedRoomId))
} else {
Logger.Debug("Finding PlayerLogin record for ws authentication:", zap.Any("intAuthToken", token))
} }
// TODO: Wrap the following 2 stmts by sql transaction! // TODO: Wrap the following 2 stmts by sql transaction!
playerId, err := models.GetPlayerIdByToken(token) playerId, err := models.GetPlayerIdByToken(token)
if err != nil || playerId == 0 { if err != nil || playerId == 0 {
// TODO: Abort with specific message. // TODO: Abort with specific message.
Logger.Info("PlayerLogin record not found for ws authentication:", zap.Any("intAuthToken", token)) Logger.Warn("PlayerLogin record not found for ws authentication:", zap.Any("intAuthToken", token))
c.AbortWithStatus(http.StatusBadRequest) c.AbortWithStatus(http.StatusBadRequest)
return return
} }
Logger.Info("PlayerLogin record has been found for ws authentication:", zap.Any("playerId", playerId)) Logger.Info("PlayerLogin record has been found for ws authentication:", zap.Any("playerId", playerId), zap.Any("intAuthToken", token), zap.Any("boundRoomId", boundRoomId), zap.Any("expectedRoomId", expectedRoomId))
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil { if err != nil {
@ -160,14 +159,14 @@ func Serve(c *gin.Context) {
signalToCloseConnOfThisPlayer(Constants.RetCode.PlayerNotFound, "") signalToCloseConnOfThisPlayer(Constants.RetCode.PlayerNotFound, "")
} }
Logger.Info("Player has logged in and its profile is found from persistent storage:", zap.Any("playerId", playerId), zap.Any("play", pPlayer)) Logger.Debug("Player has logged in and its profile is found from persistent storage:", zap.Any("playerId", playerId), zap.Any("player", pPlayer))
// Find a room to join. // Find a room to join.
Logger.Info("About to acquire RoomHeapMux for player:", zap.Any("playerId", playerId)) Logger.Debug("About to acquire RoomHeapMux for player:", zap.Any("playerId", playerId))
(*(models.RoomHeapMux)).Lock() (*(models.RoomHeapMux)).Lock()
defer func() { defer func() {
(*(models.RoomHeapMux)).Unlock() (*(models.RoomHeapMux)).Unlock()
Logger.Info("Released RoomHeapMux for player:", zap.Any("playerId", playerId)) Logger.Debug("Released RoomHeapMux for player:", zap.Any("playerId", playerId))
}() }()
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -175,13 +174,12 @@ func Serve(c *gin.Context) {
signalToCloseConnOfThisPlayer(Constants.RetCode.UnknownError, "") signalToCloseConnOfThisPlayer(Constants.RetCode.UnknownError, "")
} }
}() }()
Logger.Info("Acquired RoomHeapMux for player:", zap.Any("playerId", playerId)) Logger.Debug("Acquired RoomHeapMux for player:", zap.Any("playerId", playerId))
// Logger.Info("The RoomHeapManagerIns has:", zap.Any("addr", fmt.Sprintf("%p", models.RoomHeapManagerIns)), zap.Any("size", len(*(models.RoomHeapManagerIns)))) // Logger.Info("The RoomHeapManagerIns has:", zap.Any("addr", fmt.Sprintf("%p", models.RoomHeapManagerIns)), zap.Any("size", len(*(models.RoomHeapManagerIns))))
playerSuccessfullyAddedToRoom := false playerSuccessfullyAddedToRoom := false
if 0 < boundRoomId { if 0 < boundRoomId {
if tmpPRoom, existent := (*models.RoomMapManagerIns)[int32(boundRoomId)]; existent { if tmpPRoom, existent := (*models.RoomMapManagerIns)[int32(boundRoomId)]; existent {
pRoom = tmpPRoom pRoom = tmpPRoom
Logger.Info("Successfully got:\n", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("forBoundRoomId", boundRoomId))
res := pRoom.ReAddPlayerIfPossible(pPlayer, conn, signalToCloseConnOfThisPlayer) res := pRoom.ReAddPlayerIfPossible(pPlayer, conn, signalToCloseConnOfThisPlayer)
if !res { if !res {
Logger.Warn("Failed to get:\n", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("forBoundRoomId", boundRoomId)) Logger.Warn("Failed to get:\n", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("forBoundRoomId", boundRoomId))
@ -189,19 +187,16 @@ func Serve(c *gin.Context) {
playerSuccessfullyAddedToRoom = true playerSuccessfullyAddedToRoom = true
} }
} }
} } else if 0 < expectedRoomId {
if tmpRoom, existent := (*models.RoomMapManagerIns)[int32(expectedRoomId)]; existent {
if 0 < expectRoomId {
if tmpRoom, existent := (*models.RoomMapManagerIns)[int32(expectRoomId)]; existent {
pRoom = tmpRoom pRoom = tmpRoom
Logger.Info("Successfully got:\n", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("forExpectedRoomId", expectRoomId))
if pRoom.ReAddPlayerIfPossible(pPlayer, conn, signalToCloseConnOfThisPlayer) { if pRoom.ReAddPlayerIfPossible(pPlayer, conn, signalToCloseConnOfThisPlayer) {
playerSuccessfullyAddedToRoom = true playerSuccessfullyAddedToRoom = true
} else if pRoom.AddPlayerIfPossible(pPlayer, conn, signalToCloseConnOfThisPlayer) { } else if pRoom.AddPlayerIfPossible(pPlayer, conn, signalToCloseConnOfThisPlayer) {
playerSuccessfullyAddedToRoom = true playerSuccessfullyAddedToRoom = true
} else { } else {
Logger.Warn("Failed to get:\n", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("forExpectedRoomId", expectRoomId)) Logger.Warn("Failed to get:\n", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId), zap.Any("forExpectedRoomId", expectedRoomId))
playerSuccessfullyAddedToRoom = false playerSuccessfullyAddedToRoom = false
} }
@ -221,7 +216,7 @@ func Serve(c *gin.Context) {
signalToCloseConnOfThisPlayer(Constants.RetCode.LocallyNoAvailableRoom, fmt.Sprintf("Cannot pop a (*Room) for playerId == %v!", playerId)) signalToCloseConnOfThisPlayer(Constants.RetCode.LocallyNoAvailableRoom, fmt.Sprintf("Cannot pop a (*Room) for playerId == %v!", playerId))
} else { } else {
pRoom = tmpRoom pRoom = tmpRoom
Logger.Info("Successfully popped:\n", zap.Any("roomId", pRoom.Id), zap.Any("playerId", playerId)) Logger.Info("Successfully popped:\n", zap.Any("roomId", pRoom.Id), zap.Any("forPlayerId", playerId))
res := pRoom.AddPlayerIfPossible(pPlayer, conn, signalToCloseConnOfThisPlayer) res := pRoom.AddPlayerIfPossible(pPlayer, conn, signalToCloseConnOfThisPlayer)
if !res { if !res {
signalToCloseConnOfThisPlayer(Constants.RetCode.PlayerNotAddableToRoom, fmt.Sprintf("AddPlayerIfPossible returns false for roomId == %v, playerId == %v!", pRoom.Id, playerId)) signalToCloseConnOfThisPlayer(Constants.RetCode.PlayerNotAddableToRoom, fmt.Sprintf("AddPlayerIfPossible returns false for roomId == %v, playerId == %v!", pRoom.Id, playerId))
@ -361,7 +356,7 @@ func Serve(c *gin.Context) {
receivingLoopAgainstPlayer := func() error { receivingLoopAgainstPlayer := func() error {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
Logger.Error("Goroutine `receivingLoopAgainstPlayer`, recovery spot#1, recovered from: ", zap.Any("panic", r), zap.Any("callstack", debug.Stack())) Logger.Error("Goroutine `receivingLoopAgainstPlayer`, recovery spot#1, recovered from: ", zap.Any("panic", r))
} }
Logger.Info("Goroutine `receivingLoopAgainstPlayer` is stopped for:", zap.Any("playerId", playerId), zap.Any("roomId", pRoom.Id)) Logger.Info("Goroutine `receivingLoopAgainstPlayer` is stopped for:", zap.Any("playerId", playerId), zap.Any("roomId", pRoom.Id))
}() }()

View File

@ -8,7 +8,8 @@
"__id__": 1 "__id__": 1
}, },
"optimizationPolicy": 0, "optimizationPolicy": 0,
"asyncLoadAssets": false "asyncLoadAssets": false,
"readonly": false
}, },
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
@ -20,24 +21,20 @@
"__id__": 2 "__id__": 2
}, },
{ {
"__id__": 9 "__id__": 10
},
{
"__id__": 17
} }
], ],
"_active": true, "_active": true,
"_level": 1,
"_components": [ "_components": [
{ {
"__id__": 20 "__id__": 13
}, },
{ {
"__id__": 21 "__id__": 14
} }
], ],
"_prefab": { "_prefab": {
"__id__": 22 "__id__": 15
}, },
"_opacity": 255, "_opacity": 255,
"_color": { "_color": {
@ -57,17 +54,6 @@
"x": 0.5, "x": 0.5,
"y": 0.5 "y": 0.5
}, },
"_quat": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_skewX": 0,
"_skewY": 0,
"groupIndex": 0,
"_id": "",
"_trs": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -83,231 +69,20 @@
1, 1,
1 1
] ]
}
},
{
"__type__": "cc.Node",
"_name": "ruleNode",
"_objFlags": 0,
"_parent": {
"__id__": 1
}, },
"_children": [ "_eulerAngles": {
{ "__type__": "cc.Vec3",
"__id__": 3
}
],
"_active": true,
"_level": 2,
"_components": [
{
"__id__": 7
}
],
"_prefab": {
"__id__": 8
},
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 644,
"height": 793
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_quat": {
"__type__": "cc.Quat",
"x": 0, "x": 0,
"y": 0, "y": 0,
"z": 0, "z": 0
"w": 1
}, },
"_skewX": 0, "_skewX": 0,
"_skewY": 0, "_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0, "groupIndex": 0,
"_id": "",
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
0,
257,
0,
0,
0,
0,
1,
1,
1,
1
]
}
},
{
"__type__": "cc.Node",
"_name": "rule",
"_objFlags": 0,
"_parent": {
"__id__": 2
},
"_children": [],
"_active": true,
"_level": 3,
"_components": [
{
"__id__": 4
},
{
"__id__": 5
}
],
"_prefab": {
"__id__": 6
},
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 0,
"g": 0,
"b": 0,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 550,
"height": 560
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_quat": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_skewX": 0,
"_skewY": 0,
"groupIndex": 0,
"_id": "",
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
48,
12,
0,
0,
0,
0,
1,
1,
1,
1
]
}
},
{
"__type__": "cc.Label",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 3
},
"_enabled": true,
"_useOriginalSize": false,
"_string": "gameRule.tip",
"_N$string": "gameRule.tip",
"_fontSize": 38,
"_lineHeight": 70,
"_enableWrapText": true,
"_N$file": null,
"_isSystemFontUsed": true,
"_spacingX": 0,
"_batchAsBitmap": false,
"_N$horizontalAlign": 1,
"_N$verticalAlign": 1,
"_N$fontFamily": "Arial",
"_N$overflow": 1,
"_N$cacheMode": 0,
"_id": "" "_id": ""
}, },
{
"__type__": "744dcs4DCdNprNhG0xwq6FK",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 3
},
"_enabled": true,
"_dataID": "gameRule.tip",
"_id": ""
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__uuid__": "32b8e752-8362-4783-a4a6-1160af8b7109"
},
"fileId": "11EBmT5DNNXbQDiC9n1CEy",
"sync": false
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 2
},
"_enabled": true,
"_spriteFrame": {
"__uuid__": "0fe43223-61fc-4cb8-95bd-bd9e8f01ce8f"
},
"_type": 0,
"_sizeMode": 1,
"_fillType": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_state": 0,
"_atlas": {
"__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4"
},
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_id": ""
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__uuid__": "32b8e752-8362-4783-a4a6-1160af8b7109"
},
"fileId": "9exF2/yWJPwK/biWKavf16",
"sync": false
},
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
"_name": "modeButton", "_name": "modeButton",
@ -317,21 +92,20 @@
}, },
"_children": [ "_children": [
{ {
"__id__": 10 "__id__": 3
} }
], ],
"_active": true, "_active": true,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 14 "__id__": 7
}, },
{ {
"__id__": 15 "__id__": 8
} }
], ],
"_prefab": { "_prefab": {
"__id__": 16 "__id__": 9
}, },
"_opacity": 255, "_opacity": 255,
"_color": { "_color": {
@ -351,17 +125,6 @@
"x": 0.5, "x": 0.5,
"y": 0.5 "y": 0.5
}, },
"_quat": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_skewX": 0,
"_skewY": 0,
"groupIndex": 0,
"_id": "",
"_trs": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -377,28 +140,39 @@
1, 1,
1 1
] ]
} },
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": ""
}, },
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
"_name": "Label", "_name": "Label",
"_objFlags": 0, "_objFlags": 0,
"_parent": { "_parent": {
"__id__": 9 "__id__": 2
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_level": 0,
"_components": [ "_components": [
{ {
"__id__": 11 "__id__": 4
}, },
{ {
"__id__": 12 "__id__": 5
} }
], ],
"_prefab": { "_prefab": {
"__id__": 13 "__id__": 6
}, },
"_opacity": 255, "_opacity": 255,
"_color": { "_color": {
@ -418,17 +192,6 @@
"x": 0.5, "x": 0.5,
"y": 0.5 "y": 0.5
}, },
"_quat": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_skewX": 0,
"_skewY": 0,
"groupIndex": 0,
"_id": "",
"_trs": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -444,16 +207,33 @@
1, 1,
1 1
] ]
} },
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": ""
}, },
{ {
"__type__": "cc.Label", "__type__": "cc.Label",
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 10 "__id__": 3
}, },
"_enabled": true, "_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_useOriginalSize": false, "_useOriginalSize": false,
"_string": "gameRule.mode", "_string": "gameRule.mode",
"_N$string": "gameRule.mode", "_N$string": "gameRule.mode",
@ -476,7 +256,7 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 10 "__id__": 3
}, },
"_enabled": true, "_enabled": true,
"_dataID": "gameRule.mode", "_dataID": "gameRule.mode",
@ -498,9 +278,16 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 9 "__id__": 2
}, },
"_enabled": true, "_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": { "_spriteFrame": {
"__uuid__": "081ad337-20ca-4313-ae3e-bb6dee3547b7" "__uuid__": "081ad337-20ca-4313-ae3e-bb6dee3547b7"
}, },
@ -515,12 +302,9 @@
"_fillStart": 0, "_fillStart": 0,
"_fillRange": 0, "_fillRange": 0,
"_isTrimmedMode": true, "_isTrimmedMode": true,
"_state": 0,
"_atlas": { "_atlas": {
"__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4"
}, },
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_id": "" "_id": ""
}, },
{ {
@ -528,9 +312,11 @@
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 9 "__id__": 2
}, },
"_enabled": true, "_enabled": true,
"_normalMaterial": null,
"_grayMaterial": null,
"duration": 0.1, "duration": 0.1,
"zoomScale": 1.2, "zoomScale": 1.2,
"clickEvents": [], "clickEvents": [],
@ -589,7 +375,7 @@
"hoverSprite": null, "hoverSprite": null,
"_N$disabledSprite": null, "_N$disabledSprite": null,
"_N$target": { "_N$target": {
"__id__": 9 "__id__": 2
}, },
"_id": "" "_id": ""
}, },
@ -613,14 +399,13 @@
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 18 "__id__": 11
} }
], ],
"_prefab": { "_prefab": {
"__id__": 19 "__id__": 12
}, },
"_opacity": 255, "_opacity": 255,
"_color": { "_color": {
@ -640,17 +425,6 @@
"x": 0.5, "x": 0.5,
"y": 0.5 "y": 0.5
}, },
"_quat": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_skewX": 0,
"_skewY": 0,
"groupIndex": 0,
"_id": "",
"_trs": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -666,16 +440,35 @@
1, 1,
1 1
] ]
} },
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": ""
}, },
{ {
"__type__": "cc.Sprite", "__type__": "cc.Sprite",
"_name": "", "_name": "",
"_objFlags": 0, "_objFlags": 0,
"node": { "node": {
"__id__": 17 "__id__": 10
}, },
"_enabled": true, "_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": { "_spriteFrame": {
"__uuid__": "153d890a-fc37-4d59-8779-93a8fb19fa85" "__uuid__": "153d890a-fc37-4d59-8779-93a8fb19fa85"
}, },
@ -690,12 +483,9 @@
"_fillStart": 0, "_fillStart": 0,
"_fillRange": 0, "_fillRange": 0,
"_isTrimmedMode": true, "_isTrimmedMode": true,
"_state": 0,
"_atlas": { "_atlas": {
"__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4"
}, },
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_id": "" "_id": ""
}, },
{ {
@ -718,7 +508,7 @@
}, },
"_enabled": true, "_enabled": true,
"modeButton": { "modeButton": {
"__id__": 15 "__id__": 8
}, },
"mapNode": null, "mapNode": null,
"_id": "" "_id": ""
@ -731,6 +521,13 @@
"__id__": 1 "__id__": 1
}, },
"_enabled": true, "_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": { "_spriteFrame": {
"__uuid__": "7838f276-ab48-445a-b858-937dd27d9520" "__uuid__": "7838f276-ab48-445a-b858-937dd27d9520"
}, },
@ -745,10 +542,7 @@
"_fillStart": 0, "_fillStart": 0,
"_fillRange": 0, "_fillRange": 0,
"_isTrimmedMode": true, "_isTrimmedMode": true,
"_state": 0,
"_atlas": null, "_atlas": null,
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_id": "" "_id": ""
}, },
{ {

View File

@ -12,6 +12,11 @@ for (let k in window.ATK_CHARACTER_STATE) {
window.ATK_CHARACTER_STATE_ARR.push(window.ATK_CHARACTER_STATE[k]); window.ATK_CHARACTER_STATE_ARR.push(window.ATK_CHARACTER_STATE[k]);
} }
/*
Kindly note that the use of dragonBones anim is an informed choice for the feasibility of "gotoAndPlayByFrame", which is a required feature by "Map.rollbackAndChase". You might find that "cc.Animation" -- the traditional frame animation -- can also suffice this requirement, yet if we want to develop 3D frontend in the future, working with skeletal animation will make a smoother transition.
I've also spent sometime in extending "ccc wrapped dragoneBones.ArmatureDisplay" for enabling "gotoAndPlayByFrame" in CACHE mode (in REALTIME mode it's just the same as what's done here), but the debugging is an unexpected brainteaser -- not worth the time.
*/
cc.Class({ cc.Class({
extends: BaseCharacter, extends: BaseCharacter,
properties: { properties: {